diff --git a/Ghidra/Features/Base/developer_scripts/ConsistencyCheck.java b/Ghidra/Features/Base/developer_scripts/ConsistencyCheck.java index 5a12690d15..014f9041b6 100644 --- a/Ghidra/Features/Base/developer_scripts/ConsistencyCheck.java +++ b/Ghidra/Features/Base/developer_scripts/ConsistencyCheck.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +14,11 @@ * limitations under the License. */ // Performs database consistency check on the current program +import db.DBHandle; import ghidra.app.script.GhidraScript; import ghidra.app.services.ProgramManager; import ghidra.framework.model.DomainFile; import ghidra.program.database.ProgramDB; -import ghidra.program.model.listing.Program; -import db.DBHandle; public class ConsistencyCheck extends GhidraScript { @@ -56,6 +54,8 @@ public class ConsistencyCheck extends GhidraScript { return; } + monitor.checkCanceled(); + if (!df.canSave() || !currentProgram.hasExclusiveAccess()) { popup("Program database is NOT consistent!\nRebuild requires exclusive checkout."); return; @@ -67,19 +67,22 @@ public class ConsistencyCheck extends GhidraScript { } end(false); + + ProgramDB program = (ProgramDB) df.getDomainObject(this, false, false, monitor); + programMgr.closeProgram(currentProgram, true); - currentProgram = (Program) df.getDomainObject(this, false, false, monitor); - dbh = ((ProgramDB) currentProgram).getDBHandle(); + monitor.clearCanceled(); // compensate for Script Manager cancelling task on program close + dbh = program.getDBHandle(); try { boolean success = false; - long txId = dbh.startTransaction(); + int txId = program.startTransaction("Rebuild DB Indexes"); try { success = dbh.rebuild(monitor); } finally { - dbh.endTransaction(txId, success); + program.endTransaction(txId, success); } if (!success) { @@ -92,11 +95,12 @@ public class ConsistencyCheck extends GhidraScript { return; } - currentProgram.save("DB Rebuild", monitor); + program.save("DB Rebuild", monitor); } finally { - currentProgram.release(this); - currentProgram = programMgr.openProgram(df); + programMgr.openProgram(program); + program.release(this); + currentProgram = program; start(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbLargeTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbLargeTableModel.java index 93bb813c1d..d825743c61 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbLargeTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbLargeTableModel.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +15,6 @@ */ package ghidra.app.plugin.debug.dbtable; -import ghidra.util.Msg; -import ghidra.util.exception.AssertException; - import java.io.IOException; import java.util.*; @@ -26,6 +22,8 @@ import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; import db.*; +import ghidra.util.Msg; +import ghidra.util.exception.AssertException; public class DbLargeTableModel implements TableModel { private ArrayList listeners = new ArrayList(); @@ -43,7 +41,7 @@ public class DbLargeTableModel implements TableModel { this.table = table; schema = table.getSchema(); try { - keyType = schema.getKeyFieldClass().newInstance(); + keyType = schema.getKeyFieldType(); } catch (Exception e) { Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); @@ -59,39 +57,39 @@ public class DbLargeTableModel implements TableModel { Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } - columns.add(getColumn(schema.getKeyFieldClass())); + columns.add(getColumn(schema.getKeyFieldType())); - Class[] classes = schema.getFieldClasses(); - int fieldCount = schema.getFieldCount(); - for (int i = 0; i < fieldCount; i++) { - columns.add(getColumn(classes[i])); + Field[] fields = schema.getFields(); + for (Field field : fields) { + columns.add(getColumn(field)); } } - private AbstractColumnAdapter getColumn(Class c) { - if (c == ByteField.class) { + private AbstractColumnAdapter getColumn(Field field) { + if (field instanceof ByteField) { return new ByteColumnAdapter(); } - else if (c == BooleanField.class) { + else if (field instanceof BooleanField) { return new BooleanColumnAdapter(); } - else if (c == ShortField.class) { + else if (field instanceof ShortField) { return new ShortColumnAdapter(); } - else if (c == IntField.class) { + else if (field instanceof IntField) { return new IntegerColumnAdapter(); } - else if (c == LongField.class) { + else if (field instanceof LongField) { return new LongColumnAdapter(); } - else if (c == StringField.class) { + else if (field instanceof StringField) { return new StringColumnAdapter(); } - else if (c == BinaryField.class) { + else if (field instanceof BinaryField) { return new BinaryColumnAdapter(); } - throw new AssertException("New, unexpected DB column class type: " + c); + throw new AssertException( + "New, unexpected DB column type: " + field.getClass().getSimpleName()); } private void findMinKey() throws IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbSmallTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbSmallTableModel.java index 1a0e43a3ae..476f931725 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbSmallTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/dbtable/DbSmallTableModel.java @@ -36,12 +36,11 @@ public class DbSmallTableModel extends AbstractSortedTableModel { records = new ArrayList<>(table.getRecordCount()); - columns.add(getColumn(schema.getKeyFieldClass())); + columns.add(getColumn(schema.getKeyFieldType())); - Class[] classes = schema.getFieldClasses(); - int fieldCount = schema.getFieldCount(); - for (int i = 0; i < fieldCount; i++) { - columns.add(getColumn(classes[i])); + Field[] fields = schema.getFields(); + for (Field field : fields) { + columns.add(getColumn(field)); } try { @@ -55,29 +54,30 @@ public class DbSmallTableModel extends AbstractSortedTableModel { } } - private AbstractColumnAdapter getColumn(Class c) { - if (c == ByteField.class) { + private AbstractColumnAdapter getColumn(Field field) { + if (field instanceof ByteField) { return new ByteColumnAdapter(); } - else if (c == BooleanField.class) { + else if (field instanceof BooleanField) { return new BooleanColumnAdapter(); } - else if (c == ShortField.class) { + else if (field instanceof ShortField) { return new ShortColumnAdapter(); } - else if (c == IntField.class) { + else if (field instanceof IntField) { return new IntegerColumnAdapter(); } - else if (c == LongField.class) { + else if (field instanceof LongField) { return new LongColumnAdapter(); } - else if (c == StringField.class) { + else if (field instanceof StringField) { return new StringColumnAdapter(); } - else if (c == BinaryField.class) { + else if (field instanceof BinaryField) { return new BinaryColumnAdapter(); } - throw new AssertException("New, unexpected DB column class type: " + c); + throw new AssertException( + "New, unexpected DB column type: " + field.getClass().getSimpleName()); } @Override diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java index ef75614c84..da057e774f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java @@ -66,7 +66,7 @@ public class AddressIndexPrimaryKeyIteratorTest extends AbstractGhidraHeadedInte // Create table with indexed address column Schema schema = - new Schema(0, "id", new Class[] { LongField.class }, new String[] { "addr" }); + new Schema(0, "id", new Field[] { LongField.INSTANCE }, new String[] { "addr" }); DBHandle handle = program.getDBHandle(); myTable = handle.createTable("MyTable", schema, new int[] { 0 }); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java index 288a60c2c1..a3f89473a6 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java @@ -32,7 +32,7 @@ import ghidra.util.datastruct.LongArray; public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest { private static Schema SCHEMA = - new Schema(0, "addr", new Class[] { StringField.class }, new String[] { "str" }); + new Schema(0, "addr", new Field[] { StringField.INSTANCE }, new String[] { "str" }); private ProgramDB program; private AddressSpace space; @@ -114,7 +114,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator0() throws Exception { + public void testIterator0() throws Exception { AddressKeyIterator it = new AddressKeyIterator(); assertTrue(!it.hasNext()); assertTrue(!it.hasPrevious()); @@ -133,7 +133,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator1() throws Exception { + public void testIterator1() throws Exception { int index = 0; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, true); while (it.hasNext()) { @@ -144,7 +144,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator2() throws Exception { + public void testIterator2() throws Exception { int index = 0x10; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, addr(0x4000), true); while (it.hasNext()) { @@ -155,7 +155,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator3() throws Exception { + public void testIterator3() throws Exception { int index = 0x11; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, addr(0x5000), false); while (it.hasNext()) { @@ -166,7 +166,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator4() throws Exception { + public void testIterator4() throws Exception { int index = 0x10; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, addr(0x5000), true); while (it.hasNext()) { @@ -177,7 +177,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator5() throws Exception { + public void testIterator5() throws Exception { int index = 0x0f; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, addr(0x5000), true); while (it.hasPrevious()) { @@ -188,7 +188,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator6() throws Exception { + public void testIterator6() throws Exception { int index = 0x10; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, addr(0x5000), false); while (it.hasPrevious()) { @@ -199,7 +199,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator7() throws Exception { + public void testIterator7() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -216,7 +216,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator8() throws Exception { + public void testIterator8() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -233,7 +233,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator9() throws Exception { + public void testIterator9() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -247,7 +247,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator10() throws Exception { + public void testIterator10() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -264,7 +264,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator11() throws Exception { + public void testIterator11() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -279,7 +279,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator12() throws Exception { + public void testIterator12() throws Exception { int index = 0x3f; AddressKeyIterator it = new AddressKeyIterator(myTable, addrMap, null, false); while (it.hasPrevious()) { @@ -290,7 +290,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIterator13() throws Exception { + public void testIterator13() throws Exception { AddressSet set = new AddressSet(); set.addRange(addr(0x3008), addr(0x5008)); set.addRange(addr(0x9008), addr(0x10000)); @@ -304,7 +304,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIteratorCheckWrap1() throws Exception { + public void testIteratorCheckWrap1() throws Exception { addRecord(addr(0x0)); addRecord(addr(0x0100)); @@ -320,7 +320,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testIteratorCheckWrap2() throws Exception { + public void testIteratorCheckWrap2() throws Exception { addRecord(addr(0x0)); addRecord(addr(0x0100)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressRangeMapDBTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressRangeMapDBTest.java index bd3009e9ff..c7295d5ffa 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressRangeMapDBTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressRangeMapDBTest.java @@ -35,7 +35,8 @@ import ghidra.util.Lock; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitorAdapter; -public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest implements ErrorHandler { +public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest + implements ErrorHandler { private TestEnv env; // needed to discover languages private ProgramDB program; @@ -83,7 +84,7 @@ public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest i public void testTransaction() { AddressRangeMapDB map = new AddressRangeMapDB(program.getDBHandle(), addrMap, - new Lock("Test"), "TEST", this, LongField.class, true); + new Lock("Test"), "TEST", this, LongField.INSTANCE, true); try { map.paintRange(addr(0), addr(0x1000), ONE); @@ -114,7 +115,7 @@ public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest i public void testPaint() { AddressRangeMapDB map = new AddressRangeMapDB(program.getDBHandle(), addrMap, - new Lock("Test"), "TEST", this, LongField.class, true); + new Lock("Test"), "TEST", this, LongField.INSTANCE, true); int id = program.startTransaction("TEST"); try { @@ -152,7 +153,7 @@ public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest i public void testClear() { AddressRangeMapDB map = new AddressRangeMapDB(program.getDBHandle(), addrMap, - new Lock("Test"), "TEST", this, LongField.class, true); + new Lock("Test"), "TEST", this, LongField.INSTANCE, true); int id = program.startTransaction("TEST"); try { @@ -186,7 +187,7 @@ public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest i public void testAddressRangeIterator() { AddressRangeMapDB map = new AddressRangeMapDB(program.getDBHandle(), addrMap, - new Lock("Test"), "TEST", this, LongField.class, true); + new Lock("Test"), "TEST", this, LongField.INSTANCE, true); int id = program.startTransaction("TEST"); try { @@ -248,7 +249,7 @@ public class AddressRangeMapDBTest extends AbstractGhidraHeadedIntegrationTest i public void testMove() { AddressRangeMapDB map = new AddressRangeMapDB(program.getDBHandle(), addrMap, - new Lock("Test"), "TEST", this, LongField.class, true); + new Lock("Test"), "TEST", this, LongField.INSTANCE, true); int id = program.startTransaction("TEST"); try { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/DatabaseBenchMarks.java b/Ghidra/Features/Base/src/test/java/ghidra/DatabaseBenchMarks.java index 75774b829e..2b4452e7fa 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/DatabaseBenchMarks.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/DatabaseBenchMarks.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,57 +20,59 @@ import java.util.Random; import db.*; - - public class DatabaseBenchMarks { - static int BUFFER_SIZE = 16*1024; - static int CACHE_SIZE = 32*1024*1024; - + static int BUFFER_SIZE = 16 * 1024; + static int CACHE_SIZE = 32 * 1024 * 1024; + public static void main(String[] args) { TestTimer timer = new TestTimer(); - - testOrderedIntInsertions(timer,1000); - testOrderedIntInsertions(timer,10000); - testOrderedIntInsertions(timer,100000); - testOrderedIntInsertions(timer,1000000); - + + testOrderedIntInsertions(timer, 1000); + testOrderedIntInsertions(timer, 10000); + testOrderedIntInsertions(timer, 100000); + testOrderedIntInsertions(timer, 1000000); + System.out.println(""); - testOrderedStringInsertions(timer,1000); - testOrderedStringInsertions(timer,10000); - testOrderedStringInsertions(timer,100000); - testOrderedStringInsertions(timer,1000000); - + testOrderedStringInsertions(timer, 1000); + testOrderedStringInsertions(timer, 10000); + testOrderedStringInsertions(timer, 100000); + testOrderedStringInsertions(timer, 1000000); + System.out.println(""); - testRandomIntInsertions(timer,1000); - testRandomIntInsertions(timer,10000); - testRandomIntInsertions(timer,100000); - testRandomIntInsertions(timer,1000000); - + testRandomIntInsertions(timer, 1000); + testRandomIntInsertions(timer, 10000); + testRandomIntInsertions(timer, 100000); + testRandomIntInsertions(timer, 1000000); + System.out.println(""); testIteration(timer); - + System.out.println(""); testRandomAccess(timer); } + private static void testOrderedIntInsertions(TestTimer timer, int numInsertions) { try { DBHandle dbh = new DBHandle(BUFFER_SIZE, CACHE_SIZE); long transactionID = dbh.startTransaction(); - Schema schema = new Schema(1, "Key", new Class[]{IntField.class}, new String[]{"Value"}); + Schema schema = + new Schema(1, "Key", new Field[] { IntField.INSTANCE }, new String[] { "Value" }); Table table = dbh.createTable("Test", schema); Record record = schema.createRecord(0); - timer.start("Inserting "+numInsertions+" sorted records with long keys and integer values"); - for(int i=0;i valueClass; - - ColumnAdapter(Class c) { - if (c == ByteField.class) { - type = BYTE; - valueClass = Byte.class; - } - else if (c == BooleanField.class) { - type = BOOLEAN; - valueClass = Boolean.class; - } - else if (c == ShortField.class) { - type = SHORT; - valueClass = Short.class; - } - else if (c == IntField.class) { - type = INT; - valueClass = Integer.class; - } - else if (c == LongField.class) { - type = LONG; - //valueClass = Long.class; - valueClass = String.class; - } - else if (c == StringField.class) { - type = STRING; - valueClass = String.class; - } - else if (c == BinaryField.class) { - type = BINARY; - valueClass = String.class; - } - - } - - Class getValueClass() { - return valueClass; - } - - Object getKeyValue(Record rec) { - switch (type) { - case BYTE: - return new Byte(((ByteField) rec.getKeyField()).getByteValue()); - case BOOLEAN: - return new Boolean(((BooleanField) rec.getKeyField()).getBooleanValue()); - case SHORT: - return new Short(((ShortField) rec.getKeyField()).getShortValue()); - case INT: - return new Integer(((IntField) rec.getKeyField()).getIntValue()); - case LONG: - return "0x" + Long.toHexString(rec.getKey()); - //return new Long(rec.getKey()); - case STRING: - return ((StringField) rec.getKeyField()).getString(); - case BINARY: - byte[] bytes = ((BinaryField) rec.getKeyField()).getBinaryData(); - StringBuffer buf = new StringBuffer(" byte[" + bytes.length + "] = "); - if (bytes.length > 0) { - int len = Math.min(bytes.length, 20); - buf.append(bytes[0]); - for (int i = 1; i < len; i++) { - buf.append(","); - buf.append(bytes[i]); - } - if (bytes.length > 20) { - buf.append("..."); - } - } - return buf.toString(); - } - return ""; - } - - Object getValue(Record rec, int col) { - switch (type) { - case BYTE: - return new Byte(rec.getByteValue(col)); - case BOOLEAN: - return Boolean.valueOf(rec.getBooleanValue(col)); - case SHORT: - return new Short(rec.getShortValue(col)); - case INT: - return new Integer(rec.getIntValue(col)); - case LONG: - return "0x" + Long.toHexString(rec.getLongValue(col)); - //return new Long(rec.getLongValue(col)); - case STRING: - return " " + rec.getString(col); - case BINARY: - byte[] bytes = rec.getBinaryData(col); - StringBuffer buf = new StringBuffer(" byte[" + bytes.length + "] = "); - if (bytes.length > 0) { - int len = Math.min(bytes.length, 20); - String str = getByteString(bytes[0]); - buf.append(str); - for (int i = 1; i < len; i++) { - buf.append(","); - buf.append(getByteString(bytes[i])); - } - if (bytes.length > 20) { - buf.append("..."); - } - } - return buf.toString(); - } - return ""; - } - - private String getByteString(byte b) { - String str = Integer.toHexString(b); - if (str.length() > 2) { - str = str.substring(str.length() - 2); - } - return "0x" + str; - } - -// private String format(long l, int size) { -// String hex = Long.toHexString(l); -// if (hex.length() > size) { -// hex = hex.substring(hex.length()-size); -// } -// else if (hex.length() < size) { -// StringBuffer b = new StringBuffer(20); -// for(int i=hex.length();i listeners = new ArrayList<>(); - Table table; - Schema schema; - ColumnAdapter[] colAdapters; - ColumnAdapter keyAdapter; - Record[] records; - - DbSmallTableModel(Table table) { - this.table = table; - schema = table.getSchema(); - - records = new Record[table.getRecordCount()]; - - keyAdapter = new ColumnAdapter(schema.getKeyFieldClass()); - - colAdapters = new ColumnAdapter[schema.getFieldCount()]; - Class[] classes = schema.getFieldClasses(); - for (int i = 0; i < colAdapters.length; i++) { - colAdapters[i] = new ColumnAdapter(classes[i]); - } - - try { - RecordIterator it = table.iterator(); - for (int i = 0; i < records.length; i++) { - records[i] = it.next(); - } - } - catch (IOException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#addTableModelListener(javax.swing.event.TableModelListener) - */ - @Override - public void addTableModelListener(TableModelListener l) { - listeners.add(l); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnClass(int) - */ - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == 0) { - return keyAdapter.getValueClass(); - } - return colAdapters[columnIndex - 1].getValueClass(); - - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnCount() - */ - @Override - public int getColumnCount() { - return schema.getFieldCount() + 1; - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnName(int) - */ - @Override - public String getColumnName(int columnIndex) { - if (columnIndex == 0) { - return schema.getKeyName(); - } - --columnIndex; - int[] indexCols = table.getIndexedColumns(); - boolean isIndexed = false; - for (int indexCol : indexCols) { - if (indexCol == columnIndex) { - isIndexed = true; - break; - } - } - return schema.getFieldNames()[columnIndex] + (isIndexed ? "*" : ""); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getRowCount() - */ - @Override - public int getRowCount() { - return table.getRecordCount(); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getValueAt(int, int) - */ - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - Record rec = records[rowIndex]; - if (columnIndex == 0) { - return keyAdapter.getKeyValue(rec); - } - return colAdapters[columnIndex - 1].getValue(rec, columnIndex - 1); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#isCellEditable(int, int) - */ - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return false; - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#removeTableModelListener(javax.swing.event.TableModelListener) - */ - @Override - public void removeTableModelListener(TableModelListener l) { - listeners.remove(l); - - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int) - */ - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - } - -} - -class DbLargeTableModel implements TableModel { - ArrayList listeners = new ArrayList<>(); - Table table; - Schema schema; - ColumnAdapter keyAdapter; - ColumnAdapter[] colAdapters; - RecordIterator recIt; - Record lastRecord; - int lastIndex; - Field minKey; - Field maxKey; - Field keyType; - - DbLargeTableModel(Table table) { - this.table = table; - schema = table.getSchema(); - keyAdapter = new ColumnAdapter(schema.getKeyFieldClass()); - try { - keyType = schema.getKeyFieldClass().newInstance(); - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - try { - recIt = table.iterator(); - lastRecord = recIt.next(); - lastIndex = 0; - findMaxKey(); - findMinKey(); - } - catch (IOException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - - colAdapters = new ColumnAdapter[schema.getFieldCount()]; - Class[] classes = schema.getFieldClasses(); - for (int i = 0; i < colAdapters.length; i++) { - colAdapters[i] = new ColumnAdapter(classes[i]); - } - - } - - private void findMinKey() throws IOException { - RecordIterator iter = table.iterator(); - Record rec = iter.next(); - minKey = rec.getKeyField(); - } - - private void findMaxKey() throws IOException { - Field max = keyType.newField(); - if (table.useLongKeys()) { - max.setLongValue(Long.MAX_VALUE); - } - else { - byte[] maxBytes = new byte[128]; - Arrays.fill(maxBytes, 0, 128, (byte) 0x7f); - max.setBinaryData(maxBytes); - } - RecordIterator iter = table.iterator(max); - Record rec = iter.previous(); - maxKey = rec.getKeyField(); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#addTableModelListener(javax.swing.event.TableModelListener) - */ - @Override - public void addTableModelListener(TableModelListener l) { - listeners.add(l); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnClass(int) - */ - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == 0) { - return keyAdapter.getValueClass(); - } - return colAdapters[columnIndex - 1].getValueClass(); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnCount() - */ - @Override - public int getColumnCount() { - return schema.getFieldCount() + 1; - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getColumnName(int) - */ - @Override - public String getColumnName(int columnIndex) { - if (columnIndex == 0) { - return schema.getKeyName(); - } - --columnIndex; - int[] indexCols = table.getIndexedColumns(); - boolean isIndexed = false; - for (int indexCol : indexCols) { - if (indexCol == columnIndex) { - isIndexed = true; - break; - } - } - return schema.getFieldNames()[columnIndex] + (isIndexed ? "*" : ""); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getRowCount() - */ - @Override - public int getRowCount() { - return table.getRecordCount(); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#getValueAt(int, int) - */ - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - Record rec = getRecord(rowIndex); - if (rec == null) { - return null; - } - if (columnIndex == 0) { - return keyAdapter.getKeyValue(rec); - } - return colAdapters[columnIndex - 1].getValue(rec, columnIndex - 1); - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#isCellEditable(int, int) - */ - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return false; - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#removeTableModelListener(javax.swing.event.TableModelListener) - */ - @Override - public void removeTableModelListener(TableModelListener l) { - listeners.remove(l); - - } - - /* (non-Javadoc) - * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int) - */ - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - } - - private Record getRecord(int index) { - try { - if (index == lastIndex + 1) { - if (!recIt.hasNext()) { - // do something - } - lastRecord = recIt.next(); - lastIndex = index; - } - else if (index != lastIndex) { - if (index < lastIndex && (lastIndex - index) < 200) { - int backup = lastIndex - index + 1; - for (int i = 0; i < backup; i++) { - if (recIt.hasPrevious()) { - recIt.previous(); - } - } - lastRecord = recIt.next(); - lastIndex = index; - } - else { - findRecord(index); - lastRecord = recIt.next(); - lastIndex = index; - } - } - } - catch (IOException e) { - // XXX Auto-generated catch block - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - - return lastRecord; - } - - private void findRecord(int index) throws IOException { - if (index < 1000) { - recIt = table.iterator(); - for (int i = 0; i < index; i++) { - recIt.next(); - } - } - else if (index > table.getRecordCount() - 1000) { - recIt = table.iterator(maxKey); - if (recIt.hasNext()) { - recIt.next(); - } - for (int i = 0; i < table.getRecordCount() - index; i++) { - recIt.previous(); - } - } - else { - recIt = table.iterator(approxKey(index)); - } - } - - private Field approxKey(int index) { - Field key = keyType.newField(); - if (table.useLongKeys()) { - long min = minKey.getLongValue(); - long max = maxKey.getLongValue(); - long k = min + ((max - min) * index / table.getRecordCount()); - key.setLongValue(k); - } - else { - long min = getLong(minKey.getBinaryData()); - long max = getLong(maxKey.getBinaryData()); - long k = min + ((max - min) * index / table.getRecordCount()); - byte[] bytes = new byte[8]; - for (int i = 7; i >= 0; i--) { - bytes[i] = (byte) k; - k >>= 8; - } - key.setBinaryData(bytes); - } - return key; - } - - private long getLong(byte[] bytes) { - if (bytes == null || bytes.length == 0) { - return 0; - } - long value = 0; - for (int i = 0; i < 8; i++) { - value <<= 8; - if (i < bytes.length) { - value += bytes[i] & 0xff; - } - } - return value; - } -} diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FunctionsTable.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FunctionsTable.java index e1aa4772f7..80573163b3 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FunctionsTable.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/FunctionsTable.java @@ -44,11 +44,11 @@ public class FunctionsTable { static final int CACHE_SIZE = 10000; // @formatter:off - static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "Function ID", new Class[] { - ShortField.class, LongField.class, - ByteField.class, LongField.class, LongField.class, - LongField.class, LongField.class, LongField.class, - ByteField.class + static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "Function ID", new Field[] { + ShortField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE }, new String[] { "Code Unit Size", "Full Hash", "Specific Hash Additional Size", "Specific Hash", "Library ID", @@ -133,14 +133,15 @@ public class FunctionsTable { */ public List getFunctionRecordsByFullHash(long hash) throws IOException { LongField hashField = new LongField(hash); - DBLongIterator iterator = table.indexKeyIterator(FULL_HASH_COL, hashField, hashField, true); + DBFieldIterator iterator = + table.indexKeyIterator(FULL_HASH_COL, hashField, hashField, true); if (!iterator.hasNext()) { return Collections.emptyList(); } List list = new ArrayList<>(); while (iterator.hasNext()) { - long key = iterator.next(); - FunctionRecord functionRecord = functionCache.get(key); + Field key = iterator.next(); + FunctionRecord functionRecord = functionCache.get(key.getLongValue()); if (functionRecord == null) { Record record = table.getRecord(key); functionRecord = new FunctionRecord(fidDb, functionCache, record); @@ -216,15 +217,15 @@ public class FunctionsTable { */ public List getFunctionRecordsByNameSubstring(String nameSearch) throws IOException { - DBLongIterator iterator = table.indexKeyIterator(NAME_ID_COL); + DBFieldIterator iterator = table.indexKeyIterator(NAME_ID_COL); if (!iterator.hasNext()) { return Collections.emptyList(); } List list = new ArrayList<>(); while (iterator.hasNext()) { - long key = iterator.next(); - FunctionRecord functionRecord = functionCache.get(key); + Field key = iterator.next(); + FunctionRecord functionRecord = functionCache.get(key.getLongValue()); if (functionRecord == null) { Record record = table.getRecord(key); long nameID = record.getLongValue(NAME_ID_COL); @@ -255,15 +256,15 @@ public class FunctionsTable { */ public List getFunctionRecordsByNameRegex(String regex) throws IOException { Matcher matcher = Pattern.compile(regex).matcher(""); - DBLongIterator iterator = table.indexKeyIterator(NAME_ID_COL); + DBFieldIterator iterator = table.indexKeyIterator(NAME_ID_COL); if (!iterator.hasNext()) { return Collections.emptyList(); } List list = new ArrayList<>(); while (iterator.hasNext()) { - long key = iterator.next(); - FunctionRecord functionRecord = functionCache.get(key); + Field key = iterator.next(); + FunctionRecord functionRecord = functionCache.get(key.getLongValue()); if (functionRecord == null) { Record record = table.getRecord(key); long nameID = record.getLongValue(NAME_ID_COL); @@ -347,15 +348,15 @@ public class FunctionsTable { return Collections.emptyList(); } LongField field = new LongField(stringID); - DBLongIterator iterator = table.indexKeyIterator(NAME_ID_COL, field, field, true); + DBFieldIterator iterator = table.indexKeyIterator(NAME_ID_COL, field, field, true); if (!iterator.hasNext()) { return Collections.emptyList(); } final long libraryKey = library.getLibraryID(); List list = new ArrayList<>(); while (iterator.hasNext()) { - long key = iterator.next(); - FunctionRecord functionRecord = functionCache.get(key); + Field key = iterator.next(); + FunctionRecord functionRecord = functionCache.get(key.getLongValue()); if (functionRecord == null) { Record record = table.getRecord(key); if (record.getLongValue(LIBRARY_ID_COL) == libraryKey) { diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/LibrariesTable.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/LibrariesTable.java index 6638d94944..f75cad35a1 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/LibrariesTable.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/LibrariesTable.java @@ -49,10 +49,10 @@ public class LibrariesTable { static final int GHIDRA_COMPILER_SPEC_ID_COL = 7; // @formatter:off - static final Schema SCHEMA = new Schema(VERSION, "Library ID", new Class[] { - StringField.class, StringField.class, StringField.class, - StringField.class, StringField.class, IntField.class, IntField.class, - StringField.class + static final Schema SCHEMA = new Schema(VERSION, "Library ID", new Field[] { + StringField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, + StringField.INSTANCE }, new String[] { "Library Family Name", "Library Version", "Library Variant", "Ghidra Version", "Ghidra Language ID", "Ghidra Language Version", "Ghidra Language Minor Version", @@ -90,8 +90,9 @@ public class LibrariesTable { if (libraryVersion != VERSION) { String msg = "Expected version " + VERSION + " for table " + LIBRARIES_TABLE + " but got " + table.getSchema().getVersion(); - throw new VersionException(msg, libraryVersion < VERSION - ? VersionException.OLDER_VERSION : VersionException.NEWER_VERSION, + throw new VersionException(msg, + libraryVersion < VERSION ? VersionException.OLDER_VERSION + : VersionException.NEWER_VERSION, false); } } @@ -155,14 +156,14 @@ public class LibrariesTable { public List getLibrariesByName(String name, String version, String variant) throws IOException { StringField hashField = new StringField(name); - DBLongIterator iterator = + DBFieldIterator iterator = table.indexKeyIterator(LIBRARY_FAMILY_NAME_COL, hashField, hashField, true); if (!iterator.hasNext()) { return Collections.emptyList(); } List list = new ArrayList(); while (iterator.hasNext()) { - long key = iterator.next(); + Field key = iterator.next(); Record record = table.getRecord(key); LibraryRecord libraryRecord = new LibraryRecord(record); if (version != null) { diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/RelationsTable.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/RelationsTable.java index 597d1ec573..1a3684e393 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/RelationsTable.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/RelationsTable.java @@ -27,8 +27,8 @@ public class RelationsTable { // static final int CACHE_SIZE = 10000; // @formatter:off - static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "Relation Smash", new Class[] { - }, new String[] { + static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "Relation Smash", + new Field[] { }, new String[] { }); // @formatter:on diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/StringsTable.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/StringsTable.java index 3f1c4b7b03..a26f9615a8 100644 --- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/StringsTable.java +++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/db/StringsTable.java @@ -35,11 +35,9 @@ public class StringsTable { static final int CACHE_SIZE = 10000; // @formatter:off - static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "String ID", new Class[] { - StringField.class - }, new String[] { - "String Value" - }); + static final Schema SCHEMA = new Schema(LibrariesTable.VERSION, "String ID", + new Field[] { StringField.INSTANCE }, + new String[] { "String Value" }); // @formatter:on static int[] INDEXED_COLUMNS = new int[] { STRING_VALUE_COL }; @@ -69,29 +67,30 @@ public class StringsTable { * @throws IOException if the database has a problem */ long obtainStringID(String value) throws IOException { - long[] records = table.findRecords(new StringField(value), STRING_VALUE_COL); + Field[] records = table.findRecords(new StringField(value), STRING_VALUE_COL); if (records == null || records.length == 0) { // create - Record record = SCHEMA.createRecord(UniversalIdGenerator.nextID().getValue()); + long key = UniversalIdGenerator.nextID().getValue(); + Record record = SCHEMA.createRecord(key); record.setString(STRING_VALUE_COL, value); table.putRecord(record); - return record.getKey(); + return key; } - return records[0]; + return records[0].getLongValue(); } /** * Lookup existing ID or return null for String value. * @param value the string value - * @return the existing interned string primary key, or null if nonexistent + * @return the existing interned string primary key as LongField, or null if nonexistent * @throws IOException if the database has a problem */ Long lookupStringID(String value) throws IOException { - long[] records = table.findRecords(new StringField(value), STRING_VALUE_COL); + Field[] records = table.findRecords(new StringField(value), STRING_VALUE_COL); if (records == null || records.length == 0) { return null; } - return records[0]; + return records[0].getLongValue(); } /** diff --git a/Ghidra/Framework/DB/src/main/java/db/util/TableColumn.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableColumn.java similarity index 65% rename from Ghidra/Framework/DB/src/main/java/db/util/TableColumn.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableColumn.java index 5d12c579ce..33f7030ff4 100644 --- a/Ghidra/Framework/DB/src/main/java/db/util/TableColumn.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableColumn.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,41 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package db.util; +package ghidra.feature.vt.api.db; import db.Field; public class TableColumn { - private final Class columnClass; + private final Field columnField; private boolean indexed; - + private int ordinal; private String name; - - public TableColumn( Class columnClass ) { - this( columnClass, false ); + + public TableColumn(Field columnField) { + this(columnField, false); } - - public TableColumn( Class columnClass, boolean isIndexed ) { - this.columnClass = columnClass; + + public TableColumn(Field columnField, boolean isIndexed) { + this.columnField = columnField; indexed = isIndexed; } - void setName( String name ) { - this.name = name; + void setName(String name) { + this.name = name; } - - void setOrdinal( int ordinal ) { + + void setOrdinal(int ordinal) { this.ordinal = ordinal; } - + public boolean isIndexed() { return indexed; } - - public Class getColumnClass() { - return columnClass; + + public Field getColumnField() { + return columnField; } public String name() { @@ -61,6 +60,6 @@ public class TableColumn { @Override public String toString() { - return name() + "("+ ordinal +")"; + return name() + "(" + ordinal + ")"; } } diff --git a/Ghidra/Framework/DB/src/main/java/db/util/TableDescriptor.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableDescriptor.java similarity index 56% rename from Ghidra/Framework/DB/src/main/java/db/util/TableDescriptor.java rename to Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableDescriptor.java index 7c6847d980..e1fe4bf80a 100644 --- a/Ghidra/Framework/DB/src/main/java/db/util/TableDescriptor.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/TableDescriptor.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,63 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package db.util; - -import ghidra.util.Msg; +package ghidra.feature.vt.api.db; import java.util.*; import db.Field; +import ghidra.util.Msg; public class TableDescriptor { private TableColumn[] columns; - + protected TableDescriptor() { this.columns = discoverTableColumns(); } private TableColumn[] discoverTableColumns() { - - + Class clazz = getClass(); java.lang.reflect.Field[] fields = clazz.getFields(); List list = new ArrayList(fields.length); - for ( java.lang.reflect.Field field : fields ) { + for (java.lang.reflect.Field field : fields) { Class type = field.getType(); - if ( !TableColumn.class.isAssignableFrom( type ) ) { + if (!TableColumn.class.isAssignableFrom(type)) { continue; } - + try { - TableColumn column = (TableColumn) field.get( null ); - column.setName( field.getName() ); - column.setOrdinal( list.size() ); - list.add( column ); + TableColumn column = (TableColumn) field.get(null); + column.setName(field.getName()); + column.setOrdinal(list.size()); + list.add(column); } - catch ( IllegalArgumentException e ) { + catch (IllegalArgumentException e) { // shouldn't happen } - catch ( IllegalAccessException e ) { - Msg.showError( this, null, "Class Usage Error", "You must provide public " + - "static members for your TableColumns" ); + catch (IllegalAccessException e) { + Msg.showError(this, null, "Class Usage Error", + "You must provide public " + "static members for your TableColumns"); } - + } - - return list.toArray( new TableColumn[list.size()] ); + + return list.toArray(new TableColumn[list.size()]); } public int[] getIndexedColumns() { int count = 0; - for ( TableColumn column : columns ) { + for (TableColumn column : columns) { if (column.isIndexed()) { count++; } } int[] indexedColumns = new int[count]; count = 0; - for ( TableColumn column : columns ) { + for (TableColumn column : columns) { if (column.isIndexed()) { indexedColumns[count++] = column.column(); } @@ -80,19 +77,18 @@ public class TableDescriptor { public String[] getColumnNames() { List list = new LinkedList(); - for ( TableColumn column : columns ) { - list.add( column.name() ); + for (TableColumn column : columns) { + list.add(column.name()); } - return list.toArray( new String[ columns.length ] ); + return list.toArray(new String[columns.length]); } - @SuppressWarnings("unchecked") // we know our class types are safe - public Class[] getColumnClasses() { - List> list = new LinkedList>(); - for ( TableColumn column : columns ) { - list.add( column.getColumnClass() ); - } - return list.toArray( new Class[ columns.length ] ); + public Field[] getColumnFields() { + Field[] fields = new Field[columns.length]; + for (int i = 0; i < fields.length; i++) { + fields[i] = columns[i].getColumnField().newField(); + } + return fields; } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAddressCorrelatorAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAddressCorrelatorAdapter.java index 4ab9aff482..ea3aab8b34 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAddressCorrelatorAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAddressCorrelatorAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,48 +16,50 @@ package ghidra.feature.vt.api.db; import static ghidra.feature.vt.api.db.VTAddressCorrelatorAdapter.AddressCorrelationTableDescriptor.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; import java.io.File; import java.io.IOException; import java.util.List; import db.*; -import db.util.TableColumn; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public abstract class VTAddressCorrelatorAdapter { - public static class AddressCorrelationTableDescriptor extends db.util.TableDescriptor { + public static class AddressCorrelationTableDescriptor + extends ghidra.feature.vt.api.db.TableDescriptor { - public static TableColumn SOURCE_ENTRY_COL = new TableColumn(LongField.class, true); - public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.class); - public static TableColumn DESTINATION_ADDRESS_COL = new TableColumn(LongField.class); - - public static AddressCorrelationTableDescriptor INSTANCE = new AddressCorrelationTableDescriptor(); + public static TableColumn SOURCE_ENTRY_COL = new TableColumn(LongField.INSTANCE, true); + public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.INSTANCE); + public static TableColumn DESTINATION_ADDRESS_COL = new TableColumn(LongField.INSTANCE); + + public static AddressCorrelationTableDescriptor INSTANCE = + new AddressCorrelationTableDescriptor(); } - + static String TABLE_NAME = "AddressCorrelationTable"; - static Schema TABLE_SCHEMA = new Schema(0, "Key", - INSTANCE.getColumnClasses(), INSTANCE.getColumnNames()); + static Schema TABLE_SCHEMA = + new Schema(0, "Key", INSTANCE.getColumnFields(), INSTANCE.getColumnNames()); static int[] TABLE_INDEXES = INSTANCE.getIndexedColumns(); private DBHandle dbHandle; - + protected VTAddressCorrelatorAdapter(DBHandle dbHandle) { this.dbHandle = dbHandle; } - + public static VTAddressCorrelatorAdapter createAdapter(DBHandle dbHandle) throws IOException { return new VTAddressCorrelationAdapterV0(dbHandle); } - public static VTAddressCorrelatorAdapter getAdapter(DBHandle dbHandle, TaskMonitor monitor) + public static VTAddressCorrelatorAdapter getAdapter(DBHandle dbHandle, TaskMonitor monitor) throws VersionException { return new VTAddressCorrelationAdapterV0(dbHandle, monitor); } - abstract void createAddressRecord(long sourceEntryLong, long sourceLong, long destinationLong) throws IOException; + abstract void createAddressRecord(long sourceEntryLong, long sourceLong, long destinationLong) + throws IOException; abstract List getAddressRecords(long sourceEntryLong) throws IOException; @@ -69,8 +70,9 @@ public abstract class VTAddressCorrelatorAdapter { void save(TaskMonitor monitor) throws CancelledException, IOException { dbHandle.save("", null, monitor); } + void saveAs(File file, TaskMonitor monitor) throws CancelledException, IOException { dbHandle.saveAs(file, true, monitor); } - + } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAssociationTableDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAssociationTableDBAdapter.java index 6f3276572e..f4e88270bc 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAssociationTableDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAssociationTableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,35 +15,36 @@ */ package ghidra.feature.vt.api.db; -import static ghidra.feature.vt.api.db.VTAssociationTableDBAdapter.AssociationTableDescriptor.INSTANCE; -import ghidra.feature.vt.api.main.VTAssociationStatus; -import ghidra.feature.vt.api.main.VTAssociationType; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; +import static ghidra.feature.vt.api.db.VTAssociationTableDBAdapter.AssociationTableDescriptor.*; import java.io.IOException; import java.util.Set; import db.*; -import db.util.TableColumn; +import ghidra.feature.vt.api.main.VTAssociationStatus; +import ghidra.feature.vt.api.main.VTAssociationType; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public abstract class VTAssociationTableDBAdapter { - public static class AssociationTableDescriptor extends db.util.TableDescriptor { + public static class AssociationTableDescriptor + extends ghidra.feature.vt.api.db.TableDescriptor { - public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.class, true); - public static TableColumn DESTINATION_ADDRESS_COL = new TableColumn(LongField.class, true); - public static TableColumn TYPE_COL = new TableColumn(ByteField.class); - public static TableColumn STATUS_COL = new TableColumn(ByteField.class); - public static TableColumn APPLIED_STATUS_COL = new TableColumn(ByteField.class); - public static TableColumn VOTE_COUNT_COL = new TableColumn(IntField.class); + public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.INSTANCE, true); + public static TableColumn DESTINATION_ADDRESS_COL = + new TableColumn(LongField.INSTANCE, true); + public static TableColumn TYPE_COL = new TableColumn(ByteField.INSTANCE); + public static TableColumn STATUS_COL = new TableColumn(ByteField.INSTANCE); + public static TableColumn APPLIED_STATUS_COL = new TableColumn(ByteField.INSTANCE); + public static TableColumn VOTE_COUNT_COL = new TableColumn(IntField.INSTANCE); public static AssociationTableDescriptor INSTANCE = new AssociationTableDescriptor(); } static String TABLE_NAME = "AssociationTable"; static Schema TABLE_SCHEMA = - new Schema(0, "Key", INSTANCE.getColumnClasses(), INSTANCE.getColumnNames()); + new Schema(0, "Key", INSTANCE.getColumnFields(), INSTANCE.getColumnNames()); static int[] TABLE_INDEXES = INSTANCE.getIndexedColumns(); public static VTAssociationTableDBAdapter createAdapter(DBHandle dbHandle) throws IOException { diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchMarkupItemTableDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchMarkupItemTableDBAdapter.java index 810ce7a27d..728d711fdc 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchMarkupItemTableDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchMarkupItemTableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,36 +15,35 @@ */ package ghidra.feature.vt.api.db; -import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.INSTANCE; -import ghidra.feature.vt.api.impl.MarkupItemStorage; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; +import static ghidra.feature.vt.api.db.VTMatchMarkupItemTableDBAdapter.MarkupTableDescriptor.*; import java.io.IOException; import db.*; -import db.util.TableColumn; +import ghidra.feature.vt.api.impl.MarkupItemStorage; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public abstract class VTMatchMarkupItemTableDBAdapter { - public static class MarkupTableDescriptor extends db.util.TableDescriptor { - public static TableColumn ASSOCIATION_KEY_COL = new TableColumn(LongField.class, true); - public static TableColumn ADDRESS_SOURCE_COL = new TableColumn(StringField.class); - public static TableColumn DESTINATION_ADDRESS_COL = new TableColumn(LongField.class); - public static TableColumn MARKUP_TYPE_COL = new TableColumn(ShortField.class); - public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.class); - public static TableColumn SOURCE_VALUE_COL = new TableColumn(StringField.class); + public static class MarkupTableDescriptor extends ghidra.feature.vt.api.db.TableDescriptor { + public static TableColumn ASSOCIATION_KEY_COL = new TableColumn(LongField.INSTANCE, true); + public static TableColumn ADDRESS_SOURCE_COL = new TableColumn(StringField.INSTANCE); + public static TableColumn DESTINATION_ADDRESS_COL = new TableColumn(LongField.INSTANCE); + public static TableColumn MARKUP_TYPE_COL = new TableColumn(ShortField.INSTANCE); + public static TableColumn SOURCE_ADDRESS_COL = new TableColumn(LongField.INSTANCE); + public static TableColumn SOURCE_VALUE_COL = new TableColumn(StringField.INSTANCE); public static TableColumn ORIGINAL_DESTINATION_VALUE_COL = - new TableColumn(StringField.class); - public static TableColumn STATUS_COL = new TableColumn(ByteField.class); - public static TableColumn STATUS_DESCRIPTION_COL = new TableColumn(StringField.class); + new TableColumn(StringField.INSTANCE); + public static TableColumn STATUS_COL = new TableColumn(ByteField.INSTANCE); + public static TableColumn STATUS_DESCRIPTION_COL = new TableColumn(StringField.INSTANCE); public static MarkupTableDescriptor INSTANCE = new MarkupTableDescriptor(); } protected static String TABLE_NAME = "MatchMarkupItemTable"; static Schema TABLE_SCHEMA = - new Schema(0, "Key", INSTANCE.getColumnClasses(), INSTANCE.getColumnNames()); + new Schema(0, "Key", INSTANCE.getColumnFields(), INSTANCE.getColumnNames()); protected static int[] INDEXED_COLUMNS = INSTANCE.getIndexedColumns(); @@ -71,6 +69,5 @@ public abstract class VTMatchMarkupItemTableDBAdapter { public abstract int getRecordCount(); - public abstract Record createMarkupItemRecord(MarkupItemStorage markupItem) - throws IOException; + public abstract Record createMarkupItemRecord(MarkupItemStorage markupItem) throws IOException; } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapter.java index 082c0a2db8..ca2220602a 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,34 +15,33 @@ */ package ghidra.feature.vt.api.db; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import db.*; import ghidra.feature.vt.api.main.VTProgramCorrelator; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.AddressSet; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import db.*; - public abstract class VTMatchSetTableDBAdapter { public enum ColumnDescription { - CORRELATOR_CLASS_COL(StringField.class), - CORRELATOR_NAME_COL(StringField.class), - OPTIONS_COL(StringField.class); + CORRELATOR_CLASS_COL(StringField.INSTANCE), + CORRELATOR_NAME_COL(StringField.INSTANCE), + OPTIONS_COL(StringField.INSTANCE); - private final Class columnClass; + private final Field columnField; - private ColumnDescription(Class columnClass) { - this.columnClass = columnClass; + private ColumnDescription(Field columnField) { + this.columnField = columnField; } - public Class getColumnClass() { - return columnClass; + public Field getColumnField() { + return columnField; } public int column() { @@ -59,20 +57,18 @@ public abstract class VTMatchSetTableDBAdapter { return list.toArray(new String[columns.length]); } - @SuppressWarnings("unchecked") - // we know our class types are safe - private static Class[] getColumnClasses() { + private static Field[] getColumnFields() { ColumnDescription[] columns = ColumnDescription.values(); - List> list = new LinkedList>(); - for (ColumnDescription column : columns) { - list.add(column.getColumnClass()); + Field[] fields = new Field[columns.length]; + for (int i = 0; i < fields.length; i++) { + fields[i] = columns[i].getColumnField(); } - return list.toArray(new Class[columns.length]); + return fields; } } static String TABLE_NAME = "MatchSetTable"; - static Schema TABLE_SCHEMA = new Schema(0, "Key", ColumnDescription.getColumnClasses(), + static Schema TABLE_SCHEMA = new Schema(0, "Key", ColumnDescription.getColumnFields(), ColumnDescription.getColumnNames()); static VTMatchSetTableDBAdapter createAdapter(DBHandle dbHandle) throws IOException { diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapterV0.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapterV0.java index e76b9443be..f4a623277f 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapterV0.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchSetTableDBAdapterV0.java @@ -16,13 +16,6 @@ package ghidra.feature.vt.api.db; import static ghidra.feature.vt.api.db.VTMatchSetTableDBAdapter.ColumnDescription.*; -import ghidra.feature.vt.api.main.VTProgramCorrelator; -import ghidra.framework.options.ToolOptions; -import ghidra.program.database.map.AddressMap; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.util.exception.VersionException; -import ghidra.util.xml.GenericXMLOutputter; import java.io.IOException; import java.io.StringWriter; @@ -31,13 +24,20 @@ import org.jdom.Element; import org.jdom.output.XMLOutputter; import db.*; +import ghidra.feature.vt.api.main.VTProgramCorrelator; +import ghidra.framework.options.ToolOptions; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.util.exception.VersionException; +import ghidra.util.xml.GenericXMLOutputter; public class VTMatchSetTableDBAdapterV0 extends VTMatchSetTableDBAdapter { private Table table; - private static final Schema STORED_ADDRESS_RANGE_SCHEMA = new Schema(0, "Key", new Class[] { - LongField.class, LongField.class }, new String[] { "addr1", "addr2" }); + private static final Schema STORED_ADDRESS_RANGE_SCHEMA = new Schema(0, "Key", + new Field[] { LongField.INSTANCE, LongField.INSTANCE }, new String[] { "addr1", "addr2" }); private final DBHandle dbHandle; @@ -46,7 +46,8 @@ public class VTMatchSetTableDBAdapterV0 extends VTMatchSetTableDBAdapter { table = dbHandle.createTable(TABLE_NAME, TABLE_SCHEMA); } - public VTMatchSetTableDBAdapterV0(DBHandle dbHandle, OpenMode openMode) throws VersionException { + public VTMatchSetTableDBAdapterV0(DBHandle dbHandle, OpenMode openMode) + throws VersionException { this.dbHandle = dbHandle; table = dbHandle.getTable(TABLE_NAME); if (table == null) { @@ -59,7 +60,8 @@ public class VTMatchSetTableDBAdapterV0 extends VTMatchSetTableDBAdapter { } @Override - public Record createMatchSetRecord(long key, VTProgramCorrelator correlator) throws IOException { + public Record createMatchSetRecord(long key, VTProgramCorrelator correlator) + throws IOException { Record record = TABLE_SCHEMA.createRecord(key); record.setString(CORRELATOR_CLASS_COL.column(), correlator.getClass().getName()); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java index b0af493535..839058c93d 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,36 +15,35 @@ */ package ghidra.feature.vt.api.db; -import ghidra.feature.vt.api.main.VTMatchInfo; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import java.util.LinkedList; import java.util.List; import db.*; +import ghidra.feature.vt.api.main.VTMatchInfo; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; public abstract class VTMatchTableDBAdapter { public enum ColumnDescription { - TAG_KEY_COL(LongField.class), - MATCH_SET_COL(LongField.class), - SIMILARITY_SCORE_COL(StringField.class), - CONFIDENCE_SCORE_COL(StringField.class), - LENGTH_TYPE(StringField.class), - SOURCE_LENGTH_COL(IntField.class), - DESTINATION_LENGTH_COL(IntField.class), - ASSOCIATION_COL(LongField.class); + TAG_KEY_COL(LongField.INSTANCE), + MATCH_SET_COL(LongField.INSTANCE), + SIMILARITY_SCORE_COL(StringField.INSTANCE), + CONFIDENCE_SCORE_COL(StringField.INSTANCE), + LENGTH_TYPE(StringField.INSTANCE), + SOURCE_LENGTH_COL(IntField.INSTANCE), + DESTINATION_LENGTH_COL(IntField.INSTANCE), + ASSOCIATION_COL(LongField.INSTANCE); - private final Class columnClass; + private final Field columnField; - private ColumnDescription(Class columnClass) { - this.columnClass = columnClass; + private ColumnDescription(Field columnField) { + this.columnField = columnField; } - public Class getColumnClass() { - return columnClass; + public Field getColumnField() { + return columnField; } public int column() { @@ -61,22 +59,19 @@ public abstract class VTMatchTableDBAdapter { return list.toArray(new String[columns.length]); } - @SuppressWarnings("unchecked") - // we know our class types are safe - private static Class[] getColumnClasses() { + private static Field[] getColumnFields() { ColumnDescription[] columns = ColumnDescription.values(); - List> list = new LinkedList>(); - for (ColumnDescription column : columns) { - list.add(column.getColumnClass()); + Field[] fields = new Field[columns.length]; + for (int i = 0; i < fields.length; i++) { + fields[i] = columns[i].getColumnField(); } - return list.toArray(new Class[columns.length]); + return fields; } } static String TABLE_NAME = "MatchTable"; - static Schema TABLE_SCHEMA = - new Schema(0, "Key", ColumnDescription.getColumnClasses(), - ColumnDescription.getColumnNames()); + static Schema TABLE_SCHEMA = new Schema(0, "Key", ColumnDescription.getColumnFields(), + ColumnDescription.getColumnNames()); static VTMatchTableDBAdapter createAdapter(DBHandle dbHandle, long tableID) throws IOException { return new VTMatchTableDBAdapterV0(dbHandle, tableID); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTagDBAdapter.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTagDBAdapter.java index adca168c31..973bd587ea 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTagDBAdapter.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTMatchTagDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +15,13 @@ */ package ghidra.feature.vt.api.db; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import java.util.LinkedList; import java.util.List; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Abstract adapter for the database table that holds tags for version tracking matches. @@ -31,16 +29,16 @@ import db.*; public abstract class VTMatchTagDBAdapter { public enum ColumnDescription { - TAG_NAME_COL(StringField.class); + TAG_NAME_COL(StringField.INSTANCE); - private final Class columnClass; + private final Field columnField; - private ColumnDescription(Class columnClass) { - this.columnClass = columnClass; + private ColumnDescription(Field columnField) { + this.columnField = columnField; } - public Class getColumnClass() { - return columnClass; + public Field getColumnField() { + return columnField; } public int column() { @@ -56,22 +54,19 @@ public abstract class VTMatchTagDBAdapter { return list.toArray(new String[columns.length]); } - @SuppressWarnings("unchecked") - // we know our class types are safe - private static Class[] getColumnClasses() { + private static Field[] getColumnFields() { ColumnDescription[] columns = ColumnDescription.values(); - List> list = new LinkedList>(); - for (ColumnDescription column : columns) { - list.add(column.getColumnClass()); + Field[] fields = new Field[columns.length]; + for (int i = 0; i < fields.length; i++) { + fields[i] = columns[i].getColumnField(); } - return list.toArray(new Class[columns.length]); + return fields; } } static String TABLE_NAME = "MatchTagTable"; - static Schema TABLE_SCHEMA = - new Schema(0, LongField.class, "Key", ColumnDescription.getColumnClasses(), - ColumnDescription.getColumnNames()); + static Schema TABLE_SCHEMA = new Schema(0, "Key", ColumnDescription.getColumnFields(), + ColumnDescription.getColumnNames()); static VTMatchTagDBAdapter createAdapter(DBHandle dbHandle) throws IOException { return new VTMatchTagDBAdapterV0(dbHandle); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java index b0f88af9e8..e63f18b26b 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTSessionDB.java @@ -39,10 +39,10 @@ import ghidra.util.exception.*; import ghidra.util.task.*; public class VTSessionDB extends DomainObjectAdapterDB implements VTSession, VTChangeManager { - private final static Class[] COL_CLASS = new Class[] { StringField.class }; + private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE }; private final static String[] COL_TYPES = new String[] { "Value" }; private final static Schema SCHEMA = - new Schema(0, StringField.class, "Key", COL_CLASS, COL_TYPES); + new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_TYPES); private static final String PROGRAM_ID_PROPERTYLIST_NAME = "ProgramIDs"; private static final String SOURCE_PROGRAM_ID_PROPERTY_KEY = "SourceProgramID"; @@ -55,7 +55,24 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession, VTC private static final long IMPLIED_MATCH_SET_ID = -1; private static final String PROPERTY_TABLE_NAME = "PropertyTable"; private static final String DB_VERSION_PROPERTY_NAME = "DB_VERSION"; - private static final int DB_VERSION = 1; + + /** + * DB_VERSION should be incremented any time a change is made to the overall + * database schema associated with any of the adapters. + * 14-Nov-2019 - version 2 - Corrected fixed length indexing implementation causing + * change in index table low-level storage for newly + * created tables. + */ + private static final int DB_VERSION = 2; + + /** + * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION any time the + * latest version requires a forced upgrade (i.e., Read-only mode not supported + * until upgrade is performed). It is assumed that read-only mode is supported + * if the data's version is >= UPGRADE_REQUIRED_BEFORE_VERSION and <= DB_VERSION. + */ + // NOTE: Schema upgrades are not currently supported + private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 1; private VTMatchSetTableDBAdapter matchSetTableAdapter; private AssociationDatabaseManager associationManager; @@ -78,12 +95,11 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession, VTC int ID = session.startTransaction("Constructing New Version Tracking Match Set"); try { - session.propertyTable = createPropertyTable(session.getDBHandle()); - session.matchSetTableAdapter = - VTMatchSetTableDBAdapter.createAdapter(session.getDBHandle()); + session.propertyTable = session.dbh.createTable(PROPERTY_TABLE_NAME, SCHEMA); + session.matchSetTableAdapter = VTMatchSetTableDBAdapter.createAdapter(session.dbh); session.associationManager = - AssociationDatabaseManager.createAssociationManager(session.getDBHandle(), session); - session.matchTagAdapter = VTMatchTagDBAdapter.createAdapter(session.getDBHandle()); + AssociationDatabaseManager.createAssociationManager(session.dbh, session); + session.matchTagAdapter = VTMatchTagDBAdapter.createAdapter(session.dbh); session.initializePrograms(sourceProgram, destinationProgram); session.createMatchSet( new ManualMatchProgramCorrelator(sourceProgram, destinationProgram), @@ -91,6 +107,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession, VTC session.createMatchSet( new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram), IMPLIED_MATCH_SET_ID); + session.updateVersion(); } finally { session.endTransaction(ID, true); @@ -105,21 +122,29 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession, VTC return session; } - private static Table createPropertyTable(DBHandle dbh) throws IOException { - Table table = dbh.createTable(PROPERTY_TABLE_NAME, SCHEMA); + private void updateVersion() throws IOException { Record record = SCHEMA.createRecord(new StringField(DB_VERSION_PROPERTY_NAME)); record.setString(0, Integer.toString(DB_VERSION)); - table.putRecord(record); - return table; + propertyTable.putRecord(record); } public static VTSessionDB getVTSession(DBHandle dbHandle, OpenMode openMode, Object consumer, TaskMonitor monitor) throws VersionException, IOException { VTSessionDB session = new VTSessionDB(dbHandle, consumer); - if (session.getVersion() < DB_VERSION) { - throw new VersionException("Version Tracking Sessions do not support upgrades."); + int storedVersion = session.getVersion(); + + if (storedVersion > DB_VERSION) { + throw new VersionException(VersionException.NEWER_VERSION, false); } + // The following version logic holds true for DB_VERSION=2 which assumes no additional + // DB index tables will be added when open for update/upgrade. This will not hold + // true for future revisions associated with table schema changes in which case the + // UPGRADE_REQUIRED_BEFORE_VERSION value should equal DB_VERSION. + if (storedVersion < UPGRADE_REQUIRED_BEFORE_VERSION) { + throw new VersionException("Version Tracking Sessions do not support schema upgrades."); + } + session.matchSetTableAdapter = VTMatchSetTableDBAdapter.getAdapter(session.getDBHandle(), openMode, monitor); session.associationManager = diff --git a/Ghidra/Framework/DB/src/main/java/db/BTreeNode.java b/Ghidra/Framework/DB/src/main/java/db/BTreeNode.java index ca87241369..171007cc8b 100644 --- a/Ghidra/Framework/DB/src/main/java/db/BTreeNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/BTreeNode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package db; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.buffers.DataBuffer; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; /** * BTreeNode defines a common interface for all types @@ -30,17 +28,22 @@ import db.buffers.DataBuffer; interface BTreeNode { /** - * Return the data buffer ID associated with this node. + * @return the parent node or null if this is the root + */ + public InteriorNode getParent(); + + /** + * @return the data buffer ID associated with this node. */ public int getBufferId(); /** - * Return the data buffer associated with this node. + * @return the data buffer associated with this node. */ public DataBuffer getBuffer(); /** - * Return the number of keys contained within this node. + * @return the number of keys contained within this node. */ public int getKeyCount(); @@ -50,6 +53,26 @@ interface BTreeNode { */ public void setKeyCount(int cnt); + /** + * Get the key value at a specific index. + * @param index key index + * @return key value + * @throws IOException thrown if an IO error occurs + */ + public Field getKeyField(int index) throws IOException; + + /** + * Perform a binary search to locate the specified key and derive an index + * into the Buffer ID storage. This method is intended to find the insertion + * index or exact match for a child key. A negative value will be returned + * when an exact match is not found and may be transformed into an + * insertion index (insetIndex = -returnedIndex-1). + * @param key key to search for + * @return int buffer ID index. + * @throws IOException thrown if an IO error occurs + */ + public int getKeyIndex(Field key) throws IOException; + /** * Delete this node and all child nodes. * @throws IOException thrown if IO error occurs @@ -67,11 +90,12 @@ interface BTreeNode { * Check the consistency of this node and all of its children. * @return true if consistency check passed, else false * @param tableName name of table containing this node - * @param monitor - * @throws IOException + * @param monitor task monitor + * @throws IOException if IO error occured + * @throws CancelledException if task cancelled * @{@link ThrowsTag} CancelledException */ - public boolean isConsistent(String tableName, TaskMonitor monitor) throws IOException, - CancelledException; + public boolean isConsistent(String tableName, TaskMonitor monitor) + throws IOException, CancelledException; } diff --git a/Ghidra/Framework/DB/src/main/java/db/BinaryCodedField.java b/Ghidra/Framework/DB/src/main/java/db/BinaryCodedField.java index ae03cf7851..4ae3ef447a 100644 --- a/Ghidra/Framework/DB/src/main/java/db/BinaryCodedField.java +++ b/Ghidra/Framework/DB/src/main/java/db/BinaryCodedField.java @@ -15,11 +15,11 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import ghidra.util.exception.AssertException; + /** * Allows various non-database supported data types to be * encoded within a BinaryField which may be stored within the @@ -31,63 +31,63 @@ import java.util.ArrayList; * support a byte array. */ public class BinaryCodedField extends BinaryField { - + /** * byte[] data type */ public static final byte BYTE_ARRAY = 0; - + /** * float data type */ public static final byte FLOAT = 1; - + /** * double data type */ public static final byte DOUBLE = 2; - + /** * short data type */ public static final byte SHORT_ARRAY = 3; - + /** * int[] data type */ public static final byte INT_ARRAY = 4; - + /** * long[] data type */ public static final byte LONG_ARRAY = 5; - + /** * float[] data type */ public static final byte FLOAT_ARRAY = 6; - + /** * double[] data type */ public static final byte DOUBLE_ARRAY = 7; - + /** * String[] data type */ public static final byte STRING_ARRAY = 8; - + private static final int DATA_TYPE_OFFSET = 0; private static final int DATA_OFFSET = 1; - + private static final String STRING_ENCODING = "UTF-8"; - + /** * Default constructor */ BinaryCodedField() { } - + /** * Construct a coded field from an existing binary field. * @param binField the binary field @@ -95,7 +95,7 @@ public class BinaryCodedField extends BinaryField { public BinaryCodedField(BinaryField binField) { data = binField.getBinaryData(); } - + /** * Construct a coded field from a double value. * @param value the double value @@ -106,7 +106,7 @@ public class BinaryCodedField extends BinaryField { buffer.putLong(DATA_OFFSET, Double.doubleToLongBits(value)); data = buffer.getData(); } - + /** * Construct a coded field from a float value. * @param value the float value @@ -117,7 +117,7 @@ public class BinaryCodedField extends BinaryField { buffer.putInt(DATA_OFFSET, Float.floatToIntBits(value)); data = buffer.getData(); } - + /** * Construct a coded field from a byte array. * @param values byte array @@ -125,121 +125,121 @@ public class BinaryCodedField extends BinaryField { public BinaryCodedField(byte[] values) { if (values != null) { data = new byte[values.length + 2]; - data[DATA_OFFSET] = (byte)0; + data[DATA_OFFSET] = (byte) 0; System.arraycopy(values, 0, data, 2, values.length); } else { data = new byte[2]; - data[DATA_OFFSET] = (byte)-1; + data[DATA_OFFSET] = (byte) -1; } data[DATA_TYPE_OFFSET] = BYTE_ARRAY; } - + /** * Construct a coded field from a short array. * @param values short array */ public BinaryCodedField(short[] values) { - int len = (values != null ? (2*values.length) : 0) + 2; + int len = (values != null ? (2 * values.length) : 0) + 2; BinaryDataBuffer buffer = new BinaryDataBuffer(len); buffer.putByte(DATA_TYPE_OFFSET, SHORT_ARRAY); if (values != null) { int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); for (int i = 0; i < values.length; i++) { offset = buffer.putShort(offset, values[i]); } } else { - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } data = buffer.getData(); } - + /** * Construct a coded field from a int array. * @param values int array */ public BinaryCodedField(int[] values) { - int len = (values != null ? (4*values.length) : 0) + 2; + int len = (values != null ? (4 * values.length) : 0) + 2; BinaryDataBuffer buffer = new BinaryDataBuffer(len); buffer.putByte(DATA_TYPE_OFFSET, INT_ARRAY); if (values != null) { int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); for (int i = 0; i < values.length; i++) { offset = buffer.putInt(offset, values[i]); } } else { - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } data = buffer.getData(); } - + /** * Construct a coded field from a long array. * @param values long array */ public BinaryCodedField(long[] values) { - int len = (values != null ? (8*values.length) : 0) + 2; + int len = (values != null ? (8 * values.length) : 0) + 2; BinaryDataBuffer buffer = new BinaryDataBuffer(len); buffer.putByte(DATA_TYPE_OFFSET, LONG_ARRAY); if (values != null) { int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); for (int i = 0; i < values.length; i++) { offset = buffer.putLong(offset, values[i]); } } else { - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } data = buffer.getData(); } - + /** * Construct a coded field from a float array. * @param values float array */ public BinaryCodedField(float[] values) { - int len = (values != null ? (4*values.length) : 0) + 2; + int len = (values != null ? (4 * values.length) : 0) + 2; BinaryDataBuffer buffer = new BinaryDataBuffer(len); buffer.putByte(DATA_TYPE_OFFSET, FLOAT_ARRAY); if (values != null) { int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); for (int i = 0; i < values.length; i++) { offset = buffer.putInt(offset, Float.floatToIntBits(values[i])); } } else { - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } data = buffer.getData(); } - + /** * Construct a coded field from a double array. * @param values double array */ public BinaryCodedField(double[] values) { - int len = (values != null ? (8*values.length) : 0) + 2; + int len = (values != null ? (8 * values.length) : 0) + 2; BinaryDataBuffer buffer = new BinaryDataBuffer(len); buffer.putByte(DATA_TYPE_OFFSET, DOUBLE_ARRAY); if (values != null) { int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); for (int i = 0; i < values.length; i++) { offset = buffer.putLong(offset, Double.doubleToLongBits(values[i])); } } else { - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } data = buffer.getData(); } - + /** * Construct a coded field from a String array. * @param strings String array @@ -256,29 +256,31 @@ public class BinaryCodedField extends BinaryField { } buffer = new BinaryDataBuffer(len); int offset = DATA_OFFSET; - buffer.putByte(offset++, (byte)0); + buffer.putByte(offset++, (byte) 0); try { for (int i = 0; i < strings.length; i++) { if (strings[i] == null) { offset = buffer.putInt(offset, -1); - } else { + } + else { byte[] bytes = strings[i].getBytes(STRING_ENCODING); offset = buffer.putInt(offset, bytes.length); offset = buffer.put(offset, bytes); } } - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { throw new AssertException(); } } else { buffer = new BinaryDataBuffer(2); - buffer.putByte(DATA_OFFSET, (byte)-1); + buffer.putByte(DATA_OFFSET, (byte) -1); } buffer.putByte(DATA_TYPE_OFFSET, STRING_ARRAY); data = buffer.getData(); } - + /** * Get the data type associated with this field. * @return data type @@ -286,7 +288,7 @@ public class BinaryCodedField extends BinaryField { public byte getDataType() { return data[DATA_TYPE_OFFSET]; } - + /** * Get the double value contained with this field. * @return double value @@ -299,7 +301,7 @@ public class BinaryCodedField extends BinaryField { BinaryDataBuffer buffer = new BinaryDataBuffer(data); return Double.longBitsToDouble(buffer.getLong(DATA_OFFSET)); } - + /** * Get the float value contained with this field. * @return float value @@ -312,7 +314,7 @@ public class BinaryCodedField extends BinaryField { BinaryDataBuffer buffer = new BinaryDataBuffer(data); return Float.intBitsToFloat(buffer.getInt(DATA_OFFSET)); } - + /** * Get the byte array contained with this field. * @return byte array @@ -329,7 +331,7 @@ public class BinaryCodedField extends BinaryField { System.arraycopy(data, 2, values, 0, values.length); return values; } - + /** * Get the short array contained with this field. * @return short array @@ -342,7 +344,7 @@ public class BinaryCodedField extends BinaryField { if (data[DATA_OFFSET] < 0) { return null; } - short[] values = new short[(data.length -2) / 2]; + short[] values = new short[(data.length - 2) / 2]; BinaryDataBuffer buffer = new BinaryDataBuffer(data); int offset = DATA_OFFSET + 1; for (int i = 0; i < values.length; i++) { @@ -351,7 +353,7 @@ public class BinaryCodedField extends BinaryField { } return values; } - + /** * Get the int array contained with this field. * @return int array @@ -364,7 +366,7 @@ public class BinaryCodedField extends BinaryField { if (data[DATA_OFFSET] < 0) { return null; } - int[] values = new int[(data.length -2) / 4]; + int[] values = new int[(data.length - 2) / 4]; BinaryDataBuffer buffer = new BinaryDataBuffer(data); int offset = DATA_OFFSET + 1; for (int i = 0; i < values.length; i++) { @@ -373,7 +375,7 @@ public class BinaryCodedField extends BinaryField { } return values; } - + /** * Get the long array contained with this field. * @return long array @@ -386,7 +388,7 @@ public class BinaryCodedField extends BinaryField { if (data[DATA_OFFSET] < 0) { return null; } - long[] values = new long[(data.length -2) / 8]; + long[] values = new long[(data.length - 2) / 8]; BinaryDataBuffer buffer = new BinaryDataBuffer(data); int offset = DATA_OFFSET + 1; for (int i = 0; i < values.length; i++) { @@ -395,7 +397,7 @@ public class BinaryCodedField extends BinaryField { } return values; } - + /** * Get the float array contained with this field. * @return float array @@ -408,7 +410,7 @@ public class BinaryCodedField extends BinaryField { if (data[DATA_OFFSET] < 0) { return null; } - float[] values = new float[(data.length -2) / 4]; + float[] values = new float[(data.length - 2) / 4]; BinaryDataBuffer buffer = new BinaryDataBuffer(data); int offset = DATA_OFFSET + 1; for (int i = 0; i < values.length; i++) { @@ -417,7 +419,7 @@ public class BinaryCodedField extends BinaryField { } return values; } - + /** * Get the double array contained with this field. * @return double array @@ -430,7 +432,7 @@ public class BinaryCodedField extends BinaryField { if (data[DATA_OFFSET] < 0) { return null; } - double[] values = new double[(data.length -2) / 8]; + double[] values = new double[(data.length - 2) / 8]; BinaryDataBuffer buffer = new BinaryDataBuffer(data); int offset = DATA_OFFSET + 1; for (int i = 0; i < values.length; i++) { @@ -439,7 +441,7 @@ public class BinaryCodedField extends BinaryField { } return values; } - + /** * Get the String array contained with this field. * @return String array @@ -463,11 +465,13 @@ public class BinaryCodedField extends BinaryField { byte[] bytes = buffer.get(offset, len); strList.add(new String(bytes, STRING_ENCODING)); offset += len; - } else { - strList.add(null); + } + else { + strList.add(null); } } - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { throw new AssertException(); } String[] strings = new String[strList.size()]; diff --git a/Ghidra/Framework/DB/src/main/java/db/BinaryField.java b/Ghidra/Framework/DB/src/main/java/db/BinaryField.java index d3b94ed1a3..322b300b55 100644 --- a/Ghidra/Framework/DB/src/main/java/db/BinaryField.java +++ b/Ghidra/Framework/DB/src/main/java/db/BinaryField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +18,21 @@ package db; import java.io.IOException; import java.util.Arrays; +import db.buffers.DataBuffer; + /** * BinaryField provides a wrapper for variable length binary data which is read or * written to a Record. */ public class BinaryField extends Field { + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final BinaryField INSTANCE = new BinaryField(null, true); + protected byte[] data; + private Integer hashcode; /** * Construct a binary data field with an initial value of null. @@ -38,36 +45,41 @@ public class BinaryField extends Field { * @param data initial value */ public BinaryField(byte[] data) { + this(data, false); + } + + /** + * Construct a binary data field with an initial value of data. + * @param data initial value + * @param immutable true if field value is immutable + */ + BinaryField(byte[] data, boolean immutable) { + super(immutable); this.data = data; } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ + @Override + void checkImmutable() { + super.checkImmutable(); + hashcode = null; + } + @Override public byte[] getBinaryData() { return data; } - /* - * @see ghidra.framework.store.db.Field#setBinaryData(byte[]) - */ @Override public void setBinaryData(byte[] data) { + checkImmutable(); this.data = data; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return (data == null) ? 4 : (data.length + 4); } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { if (data == null) { @@ -77,11 +89,9 @@ public class BinaryField extends Field { return buf.put(offset, data); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); int len = buf.getInt(offset); offset += 4; if (len < 0) { @@ -94,97 +104,25 @@ public class BinaryField extends Field { return offset; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { int len = buf.getInt(offset); return (len < 0 ? 0 : len) + 4; } - /* - * @see ghidra.framework.store.db.Field#isVariableLength() - */ @Override public boolean isVariableLength() { return true; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return BINARY_OBJ_TYPE; } - /* - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - if (data == null) { - return "BinaryField: null"; - } - return "BinaryField[" + data.length + "] = " + getValueAsString(); - } - - @Override - public String getValueAsString() { - StringBuffer buf = new StringBuffer(); - int i = 0; - for (; i < 24 && i < data.length; i++) { - String b = Integer.toHexString(data[i] & 0xff); - if (b.length() == 1) { - buf.append('0'); - } - buf.append(b); - buf.append(' '); - } - if (i < data.length) { - buf.append("..."); - } - return buf.toString(); - } - - /* - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof BinaryField)) - return false; - BinaryField f = (BinaryField) obj; - return Arrays.equals(f.data, data); - } - -// /** -// * Get first 8 bytes of data as long value. -// * First data byte corresponds to most significant byte -// * of long value so that proper sign is preserved. -// * If data is null, Long.MIN_VALUE is returned. -// * @see ghidra.framework.store.db.Field#getLongValue() -// */ -// public long getLongValue() { -// long value = 0; -// if (data == null) { -// return Long.MIN_VALUE; -// } -// for (int i = 0; i < 8 && i < data.length; i++) { -// value = (value << 8) | ((long)data[i] & 0x000000ff); -// } -// if (data.length < 8) { -// value = value << (8 * (8 - data.length)); -// } -// return value; -// } - - /* - * @see ghidra.framework.store.db.Field#truncate(int) - */ @Override void truncate(int length) { + checkImmutable(); int maxLen = length - 4; if (data != null && data.length > maxLen) { byte[] newData = new byte[maxLen]; @@ -193,9 +131,6 @@ public class BinaryField extends Field { } } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { BinaryField f = (BinaryField) o; @@ -224,28 +159,105 @@ public class BinaryField extends Field { return len1 - len2; } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - return new BinaryField(fieldValue.getBinaryData()); + int compareTo(DataBuffer buffer, int offset) { + int len = buffer.getInt(offset); + if (data == null) { + if (len < 0) { + return 0; + } + return -1; + } + else if (len < 0) { + return 1; + } + + return -buffer.unsignedCompareTo(data, offset + 4, len); } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public BinaryField copyField() { + return new BinaryField(getBinaryData().clone()); + } + + @Override + public BinaryField newField() { return new BinaryField(); } - /* - * @see java.lang.Object#hashCode() - */ + @Override + BinaryField getMinValue() { + throw new UnsupportedOperationException(); + } + + @Override + BinaryField getMaxValue() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) + return false; + BinaryField f = (BinaryField) obj; + return Arrays.equals(f.data, data); + } + @Override public int hashCode() { - return data.hashCode(); + if (hashcode == null) { + int h = 0; + if (data != null) { + for (byte b : data) { + h = 31 * h + (b & 0xff); + } + } + hashcode = h; + } + return hashcode; + } + + /// Methods below should not use data field directly + + @Override + public String toString() { + String classname = getClass().getSimpleName(); + byte[] d = getBinaryData(); + if (d == null) { + return classname + ": null"; + } + return classname = "[" + d.length + "] = 0x" + getValueAsString(d); + } + + @Override + public String getValueAsString() { + byte[] d = getBinaryData(); + if (d == null) { + return "null"; + } + return "{" + getValueAsString(d) + "}"; + } + + /** + * Get format value string for byte array + * @param data byte array + * @return formatted value string + */ + public static String getValueAsString(byte[] data) { + StringBuffer buf = new StringBuffer(); + int i = 0; + for (; i < 24 && i < data.length; i++) { + String b = Integer.toHexString(data[i] & 0xff); + if (b.length() == 1) { + buf.append('0'); + } + buf.append(b); + buf.append(' '); + } + if (i < data.length) { + buf.append("..."); + } + return buf.toString(); } } diff --git a/Ghidra/Framework/DB/src/main/java/db/BooleanField.java b/Ghidra/Framework/DB/src/main/java/db/BooleanField.java index 01192ed368..26f7e05f7f 100644 --- a/Ghidra/Framework/DB/src/main/java/db/BooleanField.java +++ b/Ghidra/Framework/DB/src/main/java/db/BooleanField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,30 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import db.buffers.DataBuffer; + /** * BooleanField provides a wrapper for boolean data which is read or * written to a Record. */ -public class BooleanField extends Field { +public final class BooleanField extends Field { + + /** + * Minimum boolean field value (FALSE) + */ + public static final BooleanField MIN_VALUE = new BooleanField(false, true); + + /** + * Maximum boolean field value (TRUE) + */ + public static final BooleanField MAX_VALUE = new BooleanField(true, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final BooleanField INSTANCE = MIN_VALUE; private byte value; @@ -39,70 +53,57 @@ public class BooleanField extends Field { * @param b initial value */ public BooleanField(boolean b) { + this(b, false); + } + + /** + * Construct a boolean data field with an initial value of b. + * @param b initial value + * @param immutable true if field value is immutable + */ + BooleanField(boolean b, boolean immutable) { + super(immutable); value = b ? (byte) 1 : (byte) 0; } - /* - * @see ghidra.framework.store.db.Field#getBooleanValue() - */ @Override public boolean getBooleanValue() { return (value == 0) ? false : true; } - /* - * @see ghidra.framework.store.db.Field#setBooleanValue(boolean) - */ @Override public void setBooleanValue(boolean b) { - + checkImmutable(); this.value = b ? (byte) 1 : (byte) 0; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return 1; } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { return buf.putByte(offset, value); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); value = buf.getByte(offset); return offset + 1; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { return 1; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return BOOLEAN_TYPE; } - /* - * @see java.lang.Object#toString() - */ @Override public String toString() { return "BooleanField: " + Boolean.toString(getBooleanValue()); @@ -113,9 +114,6 @@ public class BooleanField extends Field { return Boolean.toString(getBooleanValue()); } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof BooleanField)) @@ -124,9 +122,6 @@ public class BooleanField extends Field { return otherField.value == value; } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { BooleanField f = (BooleanField) o; @@ -137,44 +132,58 @@ public class BooleanField extends Field { return 1; } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue.isVariableLength()) - throw new AssertException(); - return new BooleanField(fieldValue.getLongValue() != 0); + int compareTo(DataBuffer buffer, int offset) { + byte otherValue = buffer.getByte(offset); + if (value == otherValue) + return 0; + else if (value < otherValue) + return -1; + return 1; } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public BooleanField copyField() { + return new BooleanField(getLongValue() != 0); + } + + @Override + public BooleanField newField() { return new BooleanField(); } - /* - * @see ghidra.framework.store.db.Field#getLongValue() - */ @Override public long getLongValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return new byte[] { value }; } + @Override + public void setBinaryData(byte[] bytes) { + checkImmutable(); + if (bytes.length != 1) { + throw new IllegalFieldAccessException(); + } + value = bytes[0]; + } + @Override public int hashCode() { - // TODO Auto-generated method stub return value; } + @Override + BooleanField getMinValue() { + return MIN_VALUE; + } + + @Override + BooleanField getMaxValue() { + return MAX_VALUE; + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/Buffer.java b/Ghidra/Framework/DB/src/main/java/db/Buffer.java index 2a139c6447..7a5107badd 100644 --- a/Ghidra/Framework/DB/src/main/java/db/Buffer.java +++ b/Ghidra/Framework/DB/src/main/java/db/Buffer.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,20 +22,20 @@ import java.io.IOException; * providing various data access methods. */ public interface Buffer { - + /** * Get the buffer ID for this buffer. * @return int */ public int getId(); - + /** * Get the length of the buffer in bytes. The length reflects the number of * bytes which have been allocated to the buffer. * @return length of allocated buffer. */ public int length(); - + /** * Get the byte data located at the specified offset and store into the * bytes array provided. @@ -62,10 +61,11 @@ public interface Buffer { * underlying storage. */ public void get(int offset, byte[] data, int dataOffset, int length) throws IOException; - + /** * Get the byte data located at the specified offset. * @param offset byte offset from start of buffer. + * @param length number of bytes to be read and returned * @return the byte array. * @throws ArrayIndexOutOfBoundsException is thrown if an invalid offset is * specified or the end of the buffer was encountered while reading the @@ -74,7 +74,7 @@ public interface Buffer { * underlying storage. */ public byte[] get(int offset, int length) throws IOException; - + /** * Get the 8-bit byte value located at the specified offset. * @param offset byte offset from start of buffer. @@ -85,7 +85,7 @@ public interface Buffer { * underlying storage. */ public byte getByte(int offset) throws IOException; - + /** * Get the 32-bit integer value located at the specified offset. * @param offset byte offset from start of buffer. @@ -97,7 +97,7 @@ public interface Buffer { * underlying storage. */ public int getInt(int offset) throws IOException; - + /** * Get the 16-bit short value located at the specified offset. * @param offset byte offset from start of buffer. @@ -109,7 +109,7 @@ public interface Buffer { * underlying storage. */ public short getShort(int offset) throws IOException; - + /** * Get the 64-bit long value located at the specified offset. * @param offset byte offset from start of buffer. @@ -121,7 +121,7 @@ public interface Buffer { * underlying storage. */ public long getLong(int offset) throws IOException; - + /** * Put a specified number of bytes from the array provided into the buffer * at the specified offset. The number of bytes stored is specified by the @@ -153,7 +153,7 @@ public interface Buffer { * underlying storage. */ public int put(int offset, byte[] bytes) throws IOException; - + /** * Put the 8-bit byte value into the buffer at the specified offset. * @param offset byte offset from start of buffer. @@ -165,7 +165,7 @@ public interface Buffer { * underlying storage. */ public int putByte(int offset, byte b) throws IOException; - + /** * Put the 32-bit integer value into the buffer at the specified offset. * @param offset byte offset from start of buffer. @@ -178,7 +178,7 @@ public interface Buffer { * underlying storage. */ public int putInt(int offset, int v) throws IOException; - + /** * Put the 16-bit short value into the buffer at the specified offset. * @param offset byte offset from start of buffer. @@ -191,7 +191,7 @@ public interface Buffer { * underlying storage. */ public int putShort(int offset, short v) throws IOException; - + /** * Put the 64-bit long value into the buffer at the specified offset. * @param offset byte offset from start of buffer. @@ -204,5 +204,5 @@ public interface Buffer { * underlying storage. */ public int putLong(int offset, long v) throws IOException; - + } diff --git a/Ghidra/Framework/DB/src/main/java/db/ByteField.java b/Ghidra/Framework/DB/src/main/java/db/ByteField.java index 7c0bde31c9..cf52558201 100644 --- a/Ghidra/Framework/DB/src/main/java/db/ByteField.java +++ b/Ghidra/Framework/DB/src/main/java/db/ByteField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,30 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import db.buffers.DataBuffer; + /** * ByteField provides a wrapper for single signed byte data * which is read or written to a Record. */ -public class ByteField extends Field { +public final class ByteField extends Field { + + /** + * Minimum byte field value + */ + public static final ByteField MIN_VALUE = new ByteField(Byte.MIN_VALUE, true); + + /** + * Maximum byte field value + */ + public static final ByteField MAX_VALUE = new ByteField(Byte.MAX_VALUE, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final ByteField INSTANCE = MIN_VALUE; private byte value; @@ -39,69 +53,57 @@ public class ByteField extends Field { * @param b initial value */ public ByteField(byte b) { + this(b, false); + } + + /** + * Construct a byte field with an initial value of b. + * @param b initial value + * @param immutable true if field value is immutable + */ + ByteField(byte b, boolean immutable) { + super(immutable); value = b; } - /* - * @see ghidra.framework.store.db.Field#getByteValue() - */ @Override public byte getByteValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#setByteValue(byte) - */ @Override public void setByteValue(byte value) { + checkImmutable(); this.value = value; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return 1; } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { return buf.putByte(offset, value); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); value = buf.getByte(offset); return offset + 1; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { return 1; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return BYTE_TYPE; } - /* - * @see java.lang.Object#toString() - */ @Override public String toString() { return "Byte: " + Byte.toString(value); @@ -109,12 +111,9 @@ public class ByteField extends Field { @Override public String getValueAsString() { - return Integer.toHexString(value); + return "0x" + Integer.toHexString(value); } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof ByteField)) @@ -122,9 +121,6 @@ public class ByteField extends Field { return ((ByteField) obj).value == value; } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { ByteField f = (ByteField) o; @@ -135,54 +131,63 @@ public class ByteField extends Field { return 1; } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue.isVariableLength()) - throw new AssertException(); - return new ByteField((byte) fieldValue.getLongValue()); + int compareTo(DataBuffer buffer, int offset) { + byte otherValue = buffer.getByte(offset); + if (value == otherValue) + return 0; + else if (value < otherValue) + return -1; + return 1; } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public ByteField copyField() { + return new ByteField((byte) getLongValue()); + } + + @Override + public ByteField newField() { return new ByteField(); } - /* - * @see ghidra.framework.store.db.Field#getLongValue() - */ @Override public long getLongValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#setLongValue(long) - */ @Override public void setLongValue(long value) { - this.value = (byte) value; + setByteValue((byte) value); } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return new byte[] { value }; } - /* - * @see java.lang.Object#hashCode() - */ + @Override + public void setBinaryData(byte[] bytes) { + checkImmutable(); + if (bytes.length != 1) { + throw new IllegalFieldAccessException(); + } + value = bytes[0]; + } + @Override public int hashCode() { return value; } + @Override + ByteField getMinValue() { + return MIN_VALUE; + } + + @Override + ByteField getMaxValue() { + return MAX_VALUE; + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/ChainedBuffer.java b/Ghidra/Framework/DB/src/main/java/db/ChainedBuffer.java index 8fc65bf01c..d209c94408 100644 --- a/Ghidra/Framework/DB/src/main/java/db/ChainedBuffer.java +++ b/Ghidra/Framework/DB/src/main/java/db/ChainedBuffer.java @@ -123,7 +123,7 @@ public class ChainedBuffer implements Buffer { * @param unintializedDataSourceOffset uninitialized data source offset which corresponds to * this buffers contents. * @param bufferMgr database buffer manager - * @throws IOException + * @throws IOException thrown if an IO error occurs */ public ChainedBuffer(int size, boolean enableObfuscation, Buffer uninitializedDataSource, int unintializedDataSourceOffset, BufferMgr bufferMgr) throws IOException { @@ -171,7 +171,7 @@ public class ChainedBuffer implements Buffer { * @param size {@literal buffer size (0 < size <= 0x7fffffff)} * @param enableObfuscation true to enable xor-ing of stored data to facilitate data obfuscation. * @param bufferMgr database buffer manager - * @throws IOException + * @throws IOException thrown if an IO error occurs */ public ChainedBuffer(int size, boolean enableObfuscation, BufferMgr bufferMgr) throws IOException { @@ -183,7 +183,7 @@ public class ChainedBuffer implements Buffer { * This method may only be invoked while a database transaction is in progress. * @param size {@literal buffer size (0 < size <= 0x7fffffff)} * @param bufferMgr database buffer manager - * @throws IOException + * @throws IOException thrown if an IO error occurs */ public ChainedBuffer(int size, BufferMgr bufferMgr) throws IOException { this(size, false, null, 0, bufferMgr); @@ -198,7 +198,7 @@ public class ChainedBuffer implements Buffer { * This should not be specified if buffer will be completely filled/initialized. * @param unintializedDataSourceOffset uninitialized data source offset which corresponds to * this buffers contents. - * @throws IOException + * @throws IOException thrown if an IO error occurs */ public ChainedBuffer(BufferMgr bufferMgr, int bufferId, Buffer uninitializedDataSource, int unintializedDataSourceOffset) throws IOException { @@ -238,6 +238,7 @@ public class ChainedBuffer implements Buffer { * Construct an existing chained buffer. * @param bufferMgr database buffer manager * @param bufferId database buffer ID which corresponds to a stored ChainedBuffer + * @throws IOException thrown if an IO error occurs */ public ChainedBuffer(BufferMgr bufferMgr, int bufferId) throws IOException { this(bufferMgr, bufferId, null, 0); @@ -249,12 +250,12 @@ public class ChainedBuffer implements Buffer { } /** - * Generate the XOR value for the specified byteValue which is located at the + * Generate the XOR'd value for the specified byteValue which is located at the * specified bufferOffset. * @param bufferOffset offset within a single chained buffer, valid values are in the - * range 0 to (dataSpace-1). - * @param byteValue - * @return + * range 0 to (dataSpace-1). This value is used to determine the appropriate XOR mask. + * @param byteValue value to be XOR'd against appropriate mask value + * @return XOR'd value */ private byte xorMaskByte(int bufferOffset, byte byteValue) { byte maskByte = XOR_MASK_BYTES[bufferOffset % XOR_MASK_BYTES.length]; @@ -267,7 +268,7 @@ public class ChainedBuffer implements Buffer { * @param bufferOffset offset within a single chained buffer, valid values are in the * range 0 to (dataSpace-1). The value (bufferOffset+len-1) must be less than dataSpace. * @param len mask length (2, 4, or 8) - * @return + * @return XOR mask of specified length which corresponds to specified bufferOffset. */ private long getXorMask(int bufferOffset, int len) { long mask = 0; @@ -284,8 +285,9 @@ public class ChainedBuffer implements Buffer { * The same uninitialized read-only dataSource used for a chained buffer should be re-applied * anytime this chained buffer is re-instantiated. * - * @param dataSource - * @param dataSourceOffset + * @param dataSource data source for unitilized bytes + * @param dataSourceOffset offset within dataSource which corresponds to first byte of + * this chained buffer. */ private void setUnintializedDataSource(Buffer dataSource, int dataSourceOffset) { @@ -321,6 +323,7 @@ public class ChainedBuffer implements Buffer { /** * Return the maximum number of buffers consumed by the storage of this DBBuffer object. * The actual number may be less if data has not been written to the entire buffer. + * @return total number of buffers consumed by this ChaninedBuffer. */ int getBufferCount() { return dataBufferIdTable.length + @@ -734,7 +737,7 @@ public class ChainedBuffer implements Buffer { * The index buffer provided is always released. * @param indexBuffer the last index buffer. * @return DataBuffer - * @throws IOException + * @throws IOException thrown if an IO error occurs */ private DataBuffer appendIndexBuffer(DataBuffer indexBuffer) throws IOException { try { @@ -856,6 +859,7 @@ public class ChainedBuffer implements Buffer { /** * Delete and release all underlying DataBuffers. + * @throws IOException thrown if an IO error occurs */ public synchronized void delete() throws IOException { if (readOnly) { @@ -1115,6 +1119,7 @@ public class ChainedBuffer implements Buffer { * @param startOffset starting offset, inclusive * @param endOffset ending offset, exclusive * @param fillByte byte value + * @throws IOException thrown if an IO error occurs */ public synchronized void fill(int startOffset, int endOffset, byte fillByte) throws IOException { @@ -1160,7 +1165,7 @@ public class ChainedBuffer implements Buffer { * @return int actual number of bytes written. * This could be smaller than length if the end of buffer is * encountered while writing data. - * @throws IOException + * @throws IOException thrown if an IO error occurs */ private int putBytes(int index, int bufferDataOffset, byte[] data, int dataOffset, int length) throws IOException { @@ -1370,9 +1375,6 @@ public class ChainedBuffer implements Buffer { return offset + 8; } - /* - * @see ghidra.framework.store.Buffer#putShort(int, short) - */ @Override public synchronized int putShort(int offset, short v) throws IOException { if (readOnly) { @@ -1406,7 +1408,7 @@ public class ChainedBuffer implements Buffer { * Get a data buffer. * @param index index of within buffer chain * @return requested data buffer. - * @throws IOException + * @throws IOException thrown if an IO error occurs */ private DataBuffer getBuffer(int index) throws IOException { // if databufferIdTable is null, index must be null. let it throw null pointer in this case. @@ -1425,7 +1427,7 @@ public class ChainedBuffer implements Buffer { * Initialize specified DataBuffer which corresponds to the chain index. * @param chainBufferIndex chain buffer index * @param buf newly allocated database buffer - * @throws IOException + * @throws IOException thrown if an IO error occurs */ private void initializeAllocatedBuffer(int chainBufferIndex, DataBuffer buf) throws IOException { @@ -1455,7 +1457,7 @@ public class ChainedBuffer implements Buffer { * Add a new data buffer as an indexed buffer. * @param index buffer index. * @param buf new data buffer. - * @throws IOException + * @throws IOException thrown if an IO error occurs */ private void addBuffer(int index, DataBuffer buf) throws IOException { buf.putByte(NODE_TYPE_OFFSET, NodeMgr.CHAINED_BUFFER_DATA_NODE); diff --git a/Ghidra/Framework/DB/src/main/java/db/DBFieldMap.java b/Ghidra/Framework/DB/src/main/java/db/DBFieldMap.java deleted file mode 100644 index c2f3e696ec..0000000000 --- a/Ghidra/Framework/DB/src/main/java/db/DBFieldMap.java +++ /dev/null @@ -1,239 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 db; - -import ghidra.util.LongIterator; - -import java.io.IOException; -import java.util.NoSuchElementException; - -/** - * DBFieldMap provides a database-backed map of non-unique Field values to long values. - */ -public class DBFieldMap { - - private static final Class[] fieldClasses = { - }; - - private static final String[] fieldNames = { - }; - - private static final int BUFFER_SIZE = 16 * 1024; - - private DBHandle dbh; - private Schema schema; - private Table indexTable; - private Class fieldClass; - - /** - * Construct a new map. - * A temporary database is used to provide storage for the map. - * @param fieldClass specifies class of Field values to be stored in this map. - * @param cacheSizeMB size of data cache in MBytes. - */ - public DBFieldMap(Class fieldClass, int cacheSizeMB) { - - if (!Field.class.isAssignableFrom(fieldClass)) { - throw new IllegalArgumentException("Field class expected"); - } - - this.fieldClass = fieldClass; - int indexFieldType; - try { - indexFieldType = Field.INDEX_TYPE_FLAG | - fieldClass.newInstance().getFieldType(); - } catch (Exception e) { - throw new IllegalArgumentException("Bad Field class: " + e.getMessage()); - } - Field indexKeyField = IndexField.getIndexField((byte)indexFieldType); - schema = new Schema(0, indexKeyField.getClass(), "MapKey", fieldClasses, fieldNames); - - boolean success = false; - try { - dbh = new DBHandle(BUFFER_SIZE, cacheSizeMB * 1024 * 1024); - long txId = dbh.startTransaction(); - indexTable = dbh.createTable("DBFieldMap", schema); - dbh.endTransaction(txId, true); - success = true; - } - catch (IOException e) { - throw new RuntimeException(e); - } - finally { - if (!success && dbh != null) { - dbh.close(); - dbh = null; - } - } - - } - - /** - * Dispose all resources associated with this map. - * This method should be invoked when the map is no longer needed. - */ - public void dispose() { - if (dbh != null) { - dbh.close(); - dbh = null; - } - } - - /* - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable { - dispose(); - } - - /** - * Add the specified value pair to this map. - * If the entry already exists, this method has no affect. - * @param fieldValue - * @param longValue - */ - public void addEntry(Field fieldValue, long longValue) { - if (!fieldClass.isInstance(fieldValue)) { - throw new IllegalArgumentException("Instance of " + fieldClass.getName() + " expected"); - } - IndexField indexField = IndexField.getIndexField(fieldValue, longValue); - Record rec = schema.createRecord(indexField); - try { - long txId = dbh.startTransaction(); - indexTable.putRecord(rec); - dbh.endTransaction(txId, true); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - - } - } - - /** - * Delete the specified value pair from this map. - * @param fieldValue - * @param longValue - * @return true if entry exists and was deleted - */ - public boolean deleteEntry(Field fieldValue, long longValue) { - if (!fieldClass.isInstance(fieldValue)) { - throw new IllegalArgumentException("Instance of " + fieldClass.getName() + " expected"); - } - IndexField indexField = IndexField.getIndexField(fieldValue, longValue); - try { - long txId = dbh.startTransaction(); - boolean success = indexTable.deleteRecord(indexField); - dbh.endTransaction(txId, true); - return success; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Determine if the specified value pair exists within this map. - * (This method provided for test purposes). - * @param fieldValue - * @param longValue - * @return - */ - boolean hasEntry(Field fieldValue, long longValue) { - if (!fieldClass.isInstance(fieldValue)) { - throw new IllegalArgumentException("Instance of " + fieldClass.getName() + " expected"); - } - IndexField indexField = IndexField.getIndexField(fieldValue, longValue); - try { - return indexTable.hasRecord(indexField); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public LongIterator iterator() { - return new MapLongIterator(); - } - - private class MapLongIterator implements LongIterator { - - DBFieldIterator indexIterator; - - MapLongIterator() { - try { - indexIterator = indexTable.fieldKeyIterator(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /* - * @see ghidra.util.LongIterator#hasNext() - */ - public boolean hasNext() { - try { - return indexIterator.hasNext(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /* - * @see ghidra.util.LongIterator#next() - */ - public long next() { - try { - IndexField indexField = (IndexField) indexIterator.next(); - if (indexField == null) { - throw new NoSuchElementException(); - } - return indexField.getPrimaryKey(); - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /* - * @see ghidra.util.LongIterator#hasPrevious() - */ - public boolean hasPrevious() { - try { - return indexIterator.hasPrevious(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /* - * @see ghidra.util.LongIterator#previous() - */ - public long previous() { - try { - IndexField indexField = (IndexField) indexIterator.previous(); - if (indexField == null) { - throw new NoSuchElementException(); - } - return indexField.getPrimaryKey(); - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - } - -} diff --git a/Ghidra/Framework/DB/src/main/java/db/Field.java b/Ghidra/Framework/DB/src/main/java/db/Field.java index 41ee96ba00..3bcc70d504 100644 --- a/Ghidra/Framework/DB/src/main/java/db/Field.java +++ b/Ghidra/Framework/DB/src/main/java/db/Field.java @@ -17,11 +17,24 @@ package db; import java.io.IOException; +import db.buffers.DataBuffer; + /** * Field is an abstract data wrapper for use with Records. + * Note that when comparing two Field instances both must be of the same + * class. */ public abstract class Field implements Comparable { + public static final Field[] EMPTY_ARRAY = new Field[0]; + + /** + * 8-bit Field Type Encoding (PPPPFFFF) + * where: + * FFFF - normal/indexed field type + * PPPP - indexed table primary key type (1000b indicates LegacyIndexField) + */ + /** * Field type for ByteField * @see db.ByteField @@ -65,19 +78,56 @@ public abstract class Field implements Comparable { static final byte BOOLEAN_TYPE = 6; /** - * Field type flag mask used to isolate flag bits + * Field type for 10-byte binary FixedField(10) + * @see db.FixedField */ - static final byte TYPE_FLAG_MASK = (byte) 0xC0; + static final byte FIXED_10_TYPE = 7; /** - * Field base type mask used to isolate base type + * Legacy Index Primary Key Field type for LongField + * which was previously a boolean indicator for an index + * field with assumed long primary key. + * (see {@link LegacyIndexField}) */ - static final byte BASE_TYPE_MASK = (byte) 0x3F; + static final byte LEGACY_INDEX_LONG_TYPE = 8; /** - * Field type flag bit shared by all Index type fields + * Field base type mask */ - static final byte INDEX_TYPE_FLAG = (byte) 0x80; + static final byte FIELD_TYPE_MASK = (byte) 0x0F; + + /** + * Field index primary key type mask + */ + static final byte INDEX_PRIMARY_KEY_TYPE_MASK = (byte) ~FIELD_TYPE_MASK; + + /** + * Index Primary Key Field Type Shift + */ + static final int INDEX_FIELD_TYPE_SHIFT = 4; + + private final boolean immutable; + + /** + * Abstract Field Constructor for a mutable instance + */ + Field() { + immutable = false; + } + + /** + * Abstract Field Constructor + * @param immutable true if field value is immutable + */ + Field(boolean immutable) { + this.immutable = immutable; + } + + void checkImmutable() { + if (immutable) { + throw new IllegalFieldAccessException("immutable field instance"); + } + } /** * Get field as a long value. @@ -191,10 +241,11 @@ public abstract class Field implements Comparable { * Set data from binary byte array. * All variable-length fields must implement this method. * @param bytes field data + * @throws IllegalFieldAccessException if error occurs while reading bytes + * into field which will generally be caused by the incorrect number of + * bytes provided to a fixed-length field. */ - public void setBinaryData(byte[] bytes) { - throw new IllegalFieldAccessException(); - } + abstract public void setBinaryData(byte[] bytes); /** * Get field as a String value. @@ -219,10 +270,10 @@ public abstract class Field implements Comparable { /** * Truncate a variable length field to the specified length. * If current length is shorterm, this method has no affect. - * @param length + * @param length truncated length */ void truncate(int length) { - throw new IllegalFieldAccessException(); + throw new UnsupportedOperationException("Field may not be truncated"); } /** @@ -233,22 +284,31 @@ public abstract class Field implements Comparable { } /** - * Create new instance of this field type. - * @param fieldValue initial field value. - * @return long + * Determine if specified field is same type as this field + * @param field a Field instance + * @return true if field is same type as this field */ - public abstract Field newField(Field fieldValue); + public boolean isSameType(Field field) { + return field != null && field.getClass() == getClass(); + } + + /** + * Create new instance of this field with the same value. + * @return new field instance with same value + */ + public abstract Field copyField(); /** * Create new instance of this field type. - * @return long + * @return new field instance with undefined initial value */ public abstract Field newField(); /** - * Return Field instance type as an integer value + * Return Field instance type as an integer value. + * @return encoded field type */ - protected abstract byte getFieldType(); + abstract byte getFieldType(); /** * Write the field to buf at the specified offset. When writing variable length @@ -292,40 +352,73 @@ public abstract class Field implements Comparable { */ abstract int length(); - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public abstract boolean equals(Object obj); @Override public abstract int hashCode(); + /** + * Get field value as a formatted string + * @return field value string + */ public abstract String getValueAsString(); + /** + * Get minimum field value. + * + * Supported for fixed-length fields only. + * @return minimum value + * @throws UnsupportedOperationException if field is not fixed-length + */ + abstract Field getMinValue(); + + /** + * Get maximum field value. + * + * Supported for fixed-length fields only. + * @return maximum value + * @throws UnsupportedOperationException if field is not fixed-length + */ + abstract Field getMaxValue(); + + /** + * Performs a fast in-place comparison of this field value with another + * field value stored within the specified buffer at the the specified offset. + * @param buffer data buffer + * @param offset field value offset within buffer + * @return comparison value, zero if equal, -1 if this field has a value + * less than the stored field, or +1 if this field has a value greater than + * the stored field located at keyIndex. + */ + abstract int compareTo(DataBuffer buffer, int offset); + /** * Get the field associated with the specified type value. - * @param fieldType encoded Field type - * @return Field + * @param fieldType field type index + * @return Field field instance which corresponds to the specified fieldType * @throws UnsupportedFieldException if unsupported fieldType specified */ static Field getField(byte fieldType) throws UnsupportedFieldException { - if ((fieldType & INDEX_TYPE_FLAG) == 0) { - switch (fieldType & BASE_TYPE_MASK) { + + if ((fieldType & INDEX_PRIMARY_KEY_TYPE_MASK) == 0) { + switch (fieldType & FIELD_TYPE_MASK) { case LONG_TYPE: - return new LongField(); + return LongField.INSTANCE; case INT_TYPE: - return new IntField(); + return IntField.INSTANCE; case STRING_TYPE: - return new StringField(); + return StringField.INSTANCE; case SHORT_TYPE: - return new ShortField(); + return ShortField.INSTANCE; case BYTE_TYPE: - return new ByteField(); + return ByteField.INSTANCE; case BOOLEAN_TYPE: - return new BooleanField(); + return BooleanField.INSTANCE; case BINARY_OBJ_TYPE: - return new BinaryField(); + return BinaryField.INSTANCE; + case FIXED_10_TYPE: + return FixedField10.INSTANCE; } } else { @@ -340,4 +433,53 @@ public abstract class Field implements Comparable { } } + /** + * Get the type index value of the FixedField type which corresponds + * to the specified fixed-length; + * @param fixedLength fixed length + * @return FixedLength field type index + */ + static byte getFixedType(int fixedLength) { + if (fixedLength == 10) { + return FIXED_10_TYPE; + } + throw new IllegalArgumentException( + "Unsupported fixed-length binary type size: " + fixedLength); + } + + /** + * Get a fixed-length field of the specified size + * @param size fixed-field length (supported sizes: 1, 4, 8, 10) + * @return fixed field instance + * @throws IllegalArgumentException if unsupported fixed field length + */ + static Field getFixedField(int size) { + switch (size) { + case 1: + return new ByteField(); + case 4: + return new IntField(); + case 8: + return new LongField(); + case 10: + return new FixedField10(); + } + throw new IllegalArgumentException("Unsupported fixed-field length: " + size); + } + + /** + * Determine if a specified field instance may be indexed + * @param field field to be checked + * @return true if field can be indexed + */ + public static boolean canIndex(Field field) { + if (field == null) { + return false; + } + if (field instanceof IndexField) { + return false; + } + return !field.isSameType(BooleanField.INSTANCE) && !field.isSameType(ByteField.INSTANCE); + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/FieldIndexTable.java b/Ghidra/Framework/DB/src/main/java/db/FieldIndexTable.java index 2686f14710..b38a7ed159 100644 --- a/Ghidra/Framework/DB/src/main/java/db/FieldIndexTable.java +++ b/Ghidra/Framework/DB/src/main/java/db/FieldIndexTable.java @@ -19,21 +19,27 @@ import java.io.IOException; import java.util.ArrayList; import java.util.NoSuchElementException; +/** + * FieldIndexTable provides a simplified index table whoose key is + * a fixed or variable length {@link IndexField} which consists of a concatenation of + * the index field value and associated primary table key. + */ public class FieldIndexTable extends IndexTable { - private static final Class[] fieldClasses = {}; + private static final Field[] fields = {}; private static final String[] fieldNames = {}; - private final Schema indexSchema; private final int indexColumn; + private final IndexField indexKeyType; + /** - * Construct a new secondary index which is based upon a specific field within the - * primary table specified by name. - * @param primaryTable primary table. - * @param colIndex identifies the indexed column within the primary table. - * @throws IOException thrown if an IO error occurs + * Construct a new secondary index which is based upon a specific field column within the + * primary table. + * @param primaryTable primary table + * @param colIndex field column index + * @throws IOException thrown if IO error occurs */ FieldIndexTable(Table primaryTable, int colIndex) throws IOException { this(primaryTable, primaryTable.getDBHandle().getMasterTable().createTableRecord( @@ -49,27 +55,35 @@ public class FieldIndexTable extends IndexTable { */ FieldIndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException { super(primaryTable, indexTableRecord); - this.indexSchema = indexTable.getSchema(); this.indexColumn = indexTableRecord.getIndexedColumn(); + indexKeyType = (IndexField) indexTable.getSchema().getKeyFieldType(); } - private static Schema getIndexTableSchema(Table primaryTable, int colIndex) { - byte fieldType = primaryTable.getSchema().getField(colIndex).getFieldType(); - IndexField indexKeyField = IndexField.getIndexField(fieldType); - return new Schema(0, indexKeyField.getClass(), "IndexKey", fieldClasses, fieldNames); - } - - /* - * @see ghidra.framework.store.db.IndexTable#findPrimaryKeys(ghidra.framework.store.db.Field) + /** + * Generate index table schema for specified primaryTable and index column + * @param primaryTable primary table + * @param colIndex index column + * @return index table schema */ + private static Schema getIndexTableSchema(Table primaryTable, int colIndex) { + Schema primarySchema = primaryTable.getSchema(); + Field indexedField = primarySchema.getField(colIndex); + Field primaryKeyType = primarySchema.getKeyFieldType(); + IndexField indexKeyField = new IndexField(indexedField, primaryKeyType); + return new Schema(0, indexKeyField, "IndexKey", fields, fieldNames); + } + @Override - long[] findPrimaryKeys(Field indexValue) throws IOException { - IndexField indexField = IndexField.getIndexField(indexValue, Long.MIN_VALUE); + Field[] findPrimaryKeys(Field indexValue) throws IOException { + + IndexField indexField = + indexKeyType.newIndexField(indexValue, getPrimaryTableKeyType().getMinValue()); + DBFieldIterator iter = indexTable.fieldKeyIterator(indexField); ArrayList list = new ArrayList<>(20); while (iter.hasNext()) { IndexField f = (IndexField) iter.next(); - if (!f.hasSameIndex(indexField)) { + if (!f.hasSameIndexValue(indexField)) { break; } if (indexField.usesTruncatedFieldValue()) { @@ -82,7 +96,7 @@ public class FieldIndexTable extends IndexTable { } list.add(f); } - long[] keys = new long[list.size()]; + Field[] keys = new Field[list.size()]; for (int i = 0; i < keys.length; i++) { IndexField f = list.get(i); keys[i] = f.getPrimaryKey(); @@ -90,55 +104,37 @@ public class FieldIndexTable extends IndexTable { return keys; } - /* - * @see ghidra.framework.store.db.IndexTable#getKeyCount(ghidra.framework.store.db.Field) - */ @Override int getKeyCount(Field indexValue) throws IOException { return findPrimaryKeys(indexValue).length; } - /* - * @see ghidra.framework.store.db.IndexTable#addEntry(ghidra.framework.store.db.Record) - */ @Override void addEntry(Record record) throws IOException { Field indexedField = record.getField(colIndex); - IndexField f = IndexField.getIndexField(indexedField, record.getKey()); - Record rec = indexSchema.createRecord(f); + IndexField f = indexKeyType.newIndexField(indexedField, record.getKeyField()); + Record rec = indexTable.getSchema().createRecord(f); indexTable.putRecord(rec); } - /* - * @see ghidra.framework.store.db.IndexTable#deleteEntry(ghidra.framework.store.db.Record) - */ @Override void deleteEntry(Record record) throws IOException { Field indexedField = record.getField(colIndex); - IndexField f = IndexField.getIndexField(indexedField, record.getKey()); + IndexField f = indexKeyType.newIndexField(indexedField, record.getKeyField()); indexTable.deleteRecord(f); } - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator() - */ @Override DBFieldIterator indexIterator() throws IOException { return new IndexFieldIterator(); } - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator(ghidra.framework.store.db.Field, ghidra.framework.store.db.Field, boolean) - */ @Override DBFieldIterator indexIterator(Field minField, Field maxField, boolean before) throws IOException { return new IndexFieldIterator(minField, maxField, before); } - /* - * @see db.IndexTable#indexIterator(db.Field, db.Field, db.Field, boolean) - */ @Override DBFieldIterator indexIterator(Field minField, Field maxField, Field startField, boolean before) throws IOException { @@ -161,19 +157,19 @@ public class FieldIndexTable extends IndexTable { /** * Construct an index field iterator starting with the minimum index value. + * @throws IOException an IO error occurred */ IndexFieldIterator() throws IOException { this(null, null, true); } /** - * Construct an index field iterator. The iterator is positioned at index - * value identified by startValue. + * Construct an index field iterator. * @param minValue minimum index value or null if no minimum * @param maxValue maximum index value or null if no maximum * @param before if true initial position is before minValue, else position * after maxValue - * @throws IOException + * @throws IOException an IO error occurred */ IndexFieldIterator(Field minValue, Field maxValue, boolean before) throws IOException { @@ -182,8 +178,13 @@ public class FieldIndexTable extends IndexTable { "Due to potential truncation issues, operation not permitted on variable length fields"); } - min = minValue != null ? IndexField.getIndexField(minValue, Long.MIN_VALUE) : null; - max = maxValue != null ? IndexField.getIndexField(maxValue, Long.MAX_VALUE) : null; + Field primaryKeyType = getPrimaryTableKeyType(); + min = minValue != null + ? indexKeyType.newIndexField(minValue, primaryKeyType.getMinValue()) + : null; + max = maxValue != null + ? indexKeyType.newIndexField(maxValue, primaryKeyType.getMaxValue()) + : null; IndexField start = null; if (before && minValue != null) { @@ -209,13 +210,16 @@ public class FieldIndexTable extends IndexTable { } /** - * @param minField - * @param maxField - * @param startField - * @param before - * @throws IOException + * Construct an index field iterator. The iterator is positioned at index + * value identified by startValue. + * @param minValue minimum index value or null if no minimum + * @param maxValue maximum index value or null if no maximum + * @param startValue initial index value position + * @param before if true initial position is before minValue, else position + * after maxValue + * @throws IOException an IO error occurred */ - public IndexFieldIterator(Field minValue, Field maxValue, Field startValue, boolean before) + IndexFieldIterator(Field minValue, Field maxValue, Field startValue, boolean before) throws IOException { if (primaryTable.getSchema().getField(indexColumn).isVariableLength()) { @@ -226,17 +230,23 @@ public class FieldIndexTable extends IndexTable { if (startValue == null) { throw new IllegalArgumentException("starting index value required"); } - min = minValue != null ? IndexField.getIndexField(minValue, Long.MIN_VALUE) : null; - max = maxValue != null ? IndexField.getIndexField(maxValue, Long.MAX_VALUE) : null; - IndexField start = - IndexField.getIndexField(startValue, before ? Long.MIN_VALUE : Long.MAX_VALUE); + Field primaryKeyType = getPrimaryTableKeyType(); + min = minValue != null + ? indexKeyType.newIndexField(minValue, primaryKeyType.getMinValue()) + : null; + max = maxValue != null + ? indexKeyType.newIndexField(maxValue, primaryKeyType.getMaxValue()) + : null; + + IndexField start = indexKeyType.newIndexField(startValue, + before ? primaryKeyType.getMinValue() : primaryKeyType.getMaxValue()); indexIterator = indexTable.fieldKeyIterator(min, max, start); if (indexIterator.hasNext()) { IndexField f = (IndexField) indexIterator.next(); - if (before || !f.getIndexField().equals(startValue)) { + if (before || !f.getIndexedField().equals(startValue)) { indexIterator.previous(); } } @@ -250,11 +260,12 @@ public class FieldIndexTable extends IndexTable { hasPrev = false; // TODO ??? indexKey = (IndexField) indexIterator.next(); int skipCnt = 0; - while (indexKey != null && indexKey.hasSameIndex(lastKey)) { + while (indexKey != null && indexKey.hasSameIndexValue(lastKey)) { if (++skipCnt > 10) { // Reinit iterator to skip large number of same index value - indexIterator = indexTable.fieldKeyIterator(min, max, - IndexField.getIndexField(indexKey.getIndexField(), Long.MAX_VALUE)); + indexIterator = + indexTable.fieldKeyIterator(min, max, indexKeyType.newIndexField( + indexKey.getIndexedField(), getPrimaryTableKeyType().getMaxValue())); skipCnt = 0; } indexKey = (IndexField) indexIterator.next(); @@ -276,11 +287,12 @@ public class FieldIndexTable extends IndexTable { hasNext = false; // TODO ??? indexKey = (IndexField) indexIterator.previous(); int skipCnt = 0; - while (indexKey != null && indexKey.hasSameIndex(lastKey)) { + while (indexKey != null && indexKey.hasSameIndexValue(lastKey)) { if (++skipCnt > 10) { // Reinit iterator to skip large number of same index value - indexIterator = indexTable.fieldKeyIterator(min, max, - IndexField.getIndexField(indexKey.getIndexField(), Long.MIN_VALUE)); + indexIterator = + indexTable.fieldKeyIterator(min, max, indexKeyType.newIndexField( + indexKey.getIndexedField(), getPrimaryTableKeyType().getMinValue())); skipCnt = 0; } indexKey = (IndexField) indexIterator.previous(); @@ -300,8 +312,7 @@ public class FieldIndexTable extends IndexTable { hasNext = false; hasPrev = true; lastKey = indexKey; - Field f = indexKey.getIndexField(); - return f.newField(f); + return indexKey.getIndexedField(); } return null; } @@ -312,8 +323,7 @@ public class FieldIndexTable extends IndexTable { hasNext = true; hasPrev = false; lastKey = indexKey; - Field f = indexKey.getIndexField(); - return f.newField(f); + return indexKey.getIndexedField(); } return null; } @@ -329,8 +339,8 @@ public class FieldIndexTable extends IndexTable { return false; } synchronized (db) { - long[] keys = findPrimaryKeys(lastKey.getIndexField()); - for (long key : keys) { + Field[] keys = findPrimaryKeys(lastKey.getIndexedField()); + for (Field key : keys) { primaryTable.deleteRecord(key); } lastKey = null; @@ -339,16 +349,14 @@ public class FieldIndexTable extends IndexTable { } } - /* (non-Javadoc) - * @see ghidra.framework.store.db.IndexTable#hasRecord(ghidra.framework.store.db.Field) - */ @Override boolean hasRecord(Field field) throws IOException { - IndexField indexField = IndexField.getIndexField(field, Long.MIN_VALUE); + IndexField indexField = + indexKeyType.newIndexField(field, getPrimaryTableKeyType().getMinValue()); DBFieldIterator iter = indexTable.fieldKeyIterator(indexField); while (iter.hasNext()) { IndexField f = (IndexField) iter.next(); - if (!f.hasSameIndex(indexField)) { + if (!f.hasSameIndexValue(indexField)) { return false; } if (indexField.usesTruncatedFieldValue()) { @@ -364,109 +372,54 @@ public class FieldIndexTable extends IndexTable { return false; } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIterator() throws IOException { + DBFieldIterator keyIterator() throws IOException { return new PrimaryKeyIterator(); } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * The iterator is initially positioned before the first index buffer whose index key - * is greater than or equal to the specified startField value. - * @param startField index key value which determines initial position of iterator - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIteratorBefore(Field startField) throws IOException { + DBFieldIterator keyIteratorBefore(Field startField) throws IOException { return new PrimaryKeyIterator(startField, false); } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * The iterator is initially positioned after the index buffer whose index key - * is equal to the specified startField value or immediately before the first - * index buffer whose index key is greater than the specified startField value. - * @param startField index key value which determines initial position of iterator - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIteratorAfter(Field startField) throws IOException { + DBFieldIterator keyIteratorAfter(Field startField) throws IOException { return new PrimaryKeyIterator(startField, true); } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * The iterator is initially positioned before the primaryKey within the index buffer - * whose index key is equal to the specified startField value or immediately before the first - * index buffer whose index key is greater than the specified startField value. - * @param startField index key value which determines initial position of iterator - * @param primaryKey initial position within index buffer if index key matches startField value. - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIteratorBefore(Field startField, long primaryKey) throws IOException { + DBFieldIterator keyIteratorBefore(Field startField, Field primaryKey) throws IOException { return new PrimaryKeyIterator(null, null, startField, primaryKey, false); } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * The iterator is initially positioned after the primaryKey within the index buffer - * whose index key is equal to the specified startField value or immediately before the first - * index buffer whose index key is greater than the specified startField value. - * @param startField index key value which determines initial position of iterator - * @param primaryKey initial position within index buffer if index key matches startField value. - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIteratorAfter(Field startField, long primaryKey) throws IOException { + DBFieldIterator keyIteratorAfter(Field startField, Field primaryKey) throws IOException { return new PrimaryKeyIterator(null, null, startField, primaryKey, true); } - /** - * Iterate over all primary keys sorted based upon the associated index key. - * The iterator is limited to range of index keys of startField through endField, inclusive. - * If atStart is true, the iterator is initially positioned before the first index - * buffer whose index key is greater than or equal to the specified startField value. - * If atStart is false, the iterator is initially positioned after the first index - * buffer whose index key is less than or equal to the specified endField value. - * @param startField minimum index key value - * @param endField maximum index key value - * @param atStart if true, position iterator before start value. - * Otherwise, position iterator after end value. - * @return primary key iterator - * @throws IOException thrown if IO error occurs - */ @Override - DBLongIterator keyIterator(Field startField, Field endField, boolean atStart) + DBFieldIterator keyIterator(Field startField, Field endField, boolean atStart) throws IOException { return new PrimaryKeyIterator(startField, endField, atStart ? startField : endField, - atStart ? Long.MIN_VALUE : Long.MAX_VALUE, !atStart); + atStart ? getPrimaryTableKeyType().getMinValue() + : getPrimaryTableKeyType().getMaxValue(), + !atStart); } - /** - * @see db.IndexTable#keyIterator(db.Field, db.Field, db.Field, boolean) - */ @Override - DBLongIterator keyIterator(Field minField, Field maxField, Field startField, boolean before) + DBFieldIterator keyIterator(Field minField, Field maxField, Field startField, boolean before) throws IOException { return new PrimaryKeyIterator(minField, maxField, startField, - before ? Long.MIN_VALUE : Long.MAX_VALUE, !before); + before ? getPrimaryTableKeyType().getMinValue() + : getPrimaryTableKeyType().getMaxValue(), + !before); } /** * Iterates over primary keys which correspond to index field values within a specified range. * NOTE: Primary keys corresponding to index fields which have been truncated may be returned out of order. */ - private class PrimaryKeyIterator implements DBLongIterator { + private class PrimaryKeyIterator implements DBFieldIterator { private IndexField min; private IndexField max; @@ -479,6 +432,7 @@ public class FieldIndexTable extends IndexTable { /** * Construct a key iterator starting with the minimum secondary key. + * @throws IOException thrown if IO error occurs */ PrimaryKeyIterator() throws IOException { indexIterator = indexTable.fieldKeyIterator(); @@ -490,9 +444,12 @@ public class FieldIndexTable extends IndexTable { * @param startValue indexed field value. * @param after if true the iterator is positioned immediately after * the last occurance of the specified startValue position. + * @throws IOException thrown if IO error occurs */ PrimaryKeyIterator(Field startValue, boolean after) throws IOException { - this(null, null, startValue, after ? Long.MAX_VALUE : Long.MIN_VALUE, after); + this(null, null, startValue, after ? getPrimaryTableKeyType().getMaxValue() + : getPrimaryTableKeyType().getMinValue(), + after); } /** @@ -505,13 +462,18 @@ public class FieldIndexTable extends IndexTable { * @param after if true iterator is positioned immediately after * the startValue/primaryKey, * otherwise immediately before. - * @throws IOException + * @throws IOException thrown if IO error occurs */ - PrimaryKeyIterator(Field minValue, Field maxValue, Field startValue, long primaryKey, + PrimaryKeyIterator(Field minValue, Field maxValue, Field startValue, Field primaryKey, boolean after) throws IOException { - min = minValue != null ? IndexField.getIndexField(minValue, Long.MIN_VALUE) : null; - max = maxValue != null ? IndexField.getIndexField(maxValue, Long.MAX_VALUE) : null; + Field primaryKeyType = getPrimaryTableKeyType(); + min = minValue != null + ? indexKeyType.newIndexField(minValue, primaryKeyType.getMinValue()) + : null; + max = maxValue != null + ? indexKeyType.newIndexField(maxValue, primaryKeyType.getMaxValue()) + : null; IndexField start = null; if (after && startValue == null && maxValue == null) { @@ -522,7 +484,7 @@ public class FieldIndexTable extends IndexTable { } else { start = - startValue != null ? IndexField.getIndexField(startValue, primaryKey) : null; + startValue != null ? indexKeyType.newIndexField(startValue, primaryKey) : null; indexIterator = indexTable.fieldKeyIterator(min, max, start); if (indexIterator.hasNext()) { Field f = indexIterator.next(); @@ -540,18 +502,18 @@ public class FieldIndexTable extends IndexTable { * @return true if field value corresponding to f is outside the min/max range. * It is assumed that the underlying table iterator will not return index values * out of range which do not have the same truncated index value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private boolean indexValueOutOfRange(IndexField f) throws IOException { Field val = null; - if (min != null && min.usesTruncatedFieldValue() && min.hasSameIndex(f)) { + if (min != null && min.usesTruncatedFieldValue() && min.hasSameIndexValue(f)) { Record rec = primaryTable.getRecord(f.getPrimaryKey()); val = rec.getField(indexColumn); if (val.compareTo(min.getNonTruncatedIndexField()) < 0) { return true; } } - if (max != null && max.usesTruncatedFieldValue() && max.hasSameIndex(f)) { + if (max != null && max.usesTruncatedFieldValue() && max.hasSameIndexValue(f)) { if (val == null) { Record rec = primaryTable.getRecord(f.getPrimaryKey()); val = rec.getField(indexColumn); @@ -563,9 +525,6 @@ public class FieldIndexTable extends IndexTable { return false; } - /* (non-Javadoc) - * @see ghidra.framework.store.db.DBLongIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { if (hasNext) { @@ -582,9 +541,6 @@ public class FieldIndexTable extends IndexTable { return hasNext; } - /* (non-Javadoc) - * @see ghidra.framework.store.db.DBLongIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { if (hasPrev) { @@ -601,11 +557,8 @@ public class FieldIndexTable extends IndexTable { return hasPrev; } - /* (non-Javadoc) - * @see ghidra.framework.store.db.DBLongIterator#next() - */ @Override - public long next() throws IOException { + public Field next() throws IOException { if (hasNext()) { lastKey = key; hasNext = false; @@ -614,11 +567,8 @@ public class FieldIndexTable extends IndexTable { throw new NoSuchElementException(); } - /* (non-Javadoc) - * @see ghidra.framework.store.db.DBLongIterator#previous() - */ @Override - public long previous() throws IOException { + public Field previous() throws IOException { if (hasPrevious()) { lastKey = key; hasPrev = false; @@ -627,13 +577,10 @@ public class FieldIndexTable extends IndexTable { throw new NoSuchElementException(); } - /* (non-Javadoc) - * @see ghidra.framework.store.db.DBLongIterator#delete() - */ @Override public boolean delete() throws IOException { if (lastKey != null) { - long primaryKey = lastKey.getPrimaryKey(); + Field primaryKey = lastKey.getPrimaryKey(); lastKey = null; return primaryTable.deleteRecord(primaryKey); } diff --git a/Ghidra/Framework/DB/src/main/java/db/FieldKeyInteriorNode.java b/Ghidra/Framework/DB/src/main/java/db/FieldKeyInteriorNode.java new file mode 100644 index 0000000000..f88cbddde3 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FieldKeyInteriorNode.java @@ -0,0 +1,35 @@ +/* ### + * 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 db; + +import java.io.IOException; + +/** + * FieldKeyInteriorNode defines a common interface for {@link FieldKeyNode} + * implementations which are also an {@link InteriorNode}. + */ +public interface FieldKeyInteriorNode extends InteriorNode, FieldKeyNode { + + /** + * Callback method for when a child node's leftmost key changes. + * @param oldKey previous leftmost key. + * @param newKey new leftmost key. + * @param childNode child node containing oldKey (null if not a VarKeyNode) + * @throws IOException if IO error occurs + */ + void keyChanged(Field oldKey, Field newKey, FieldKeyNode childNode) throws IOException; + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FieldKeyNode.java b/Ghidra/Framework/DB/src/main/java/db/FieldKeyNode.java new file mode 100644 index 0000000000..a2c9e98052 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FieldKeyNode.java @@ -0,0 +1,65 @@ +/* ### + * 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 db; + +import java.io.IOException; + +/** + * FieldKeyNode defines a common interface for {@link BTreeNode} + * implementations which utilize a {@link Field} key. + */ +interface FieldKeyNode extends BTreeNode { + + /** + * @return the parent node or null if this is the root + */ + @Override + public FieldKeyInteriorNode getParent(); + + /** + * Get the leaf node which contains the specified key. + * @param key key value + * @return leaf node + * @throws IOException thrown if an IO error occurs + */ + public FieldKeyRecordNode getLeafNode(Field key) throws IOException; + + /** + * Get the left-most leaf node within the tree. + * @return left-most leaf node. + * @throws IOException thrown if IO error occurs + */ + abstract FieldKeyRecordNode getLeftmostLeafNode() throws IOException; + + /** + * Get the right-most leaf node within the tree. + * @return right-most leaf node. + * @throws IOException thrown if IO error occurs + */ + abstract FieldKeyRecordNode getRightmostLeafNode() throws IOException; + + /** + * Performs a fast in-place key comparison of the specified key + * value with a key stored within this node at the specified keyIndex. + * @param k key value to be compared + * @param keyIndex key index to another key within this node's buffer + * @return comparison value, zero if equal, -1 if k has a value less than + * the store key, or +1 if k has a value greater than the stored key located + * at keyIndex. + */ + abstract int compareKeyField(Field k, int keyIndex); + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FieldKeyRecordNode.java b/Ghidra/Framework/DB/src/main/java/db/FieldKeyRecordNode.java new file mode 100644 index 0000000000..d551fda916 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FieldKeyRecordNode.java @@ -0,0 +1,145 @@ +/* ### + * 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 db; + +import java.io.IOException; + +/** + * FieldKeyRecordNode defines a common interface for {@link FieldKeyNode} + * implementations which are also a {@link RecordNode} (i.e., leaf node). + */ +interface FieldKeyRecordNode extends RecordNode, FieldKeyNode { + + /** + * Get the record located at the specified index. + * @param schema record data schema + * @param index key index + * @return Record + * @throws IOException thrown if IO error occurs + */ + Record getRecord(Schema schema, int index) throws IOException; + + /** + * Insert or Update a record. + * @param record data record with long key + * @param table table which will be notified when record is inserted or updated. + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + FieldKeyNode putRecord(Record record, Table table) throws IOException; + + /** + * Remove the record identified by index. + * This will never be the last record within the node. + * @param index record index + * @throws IOException thrown if IO error occurs + */ + void remove(int index) throws IOException; + + /** + * Determine if this record node has a right sibling. + * @return true if right sibling exists + * @throws IOException if IO error occurs + */ + boolean hasNextLeaf() throws IOException; + + /** + * Get this leaf node's right sibling + * @return this leaf node's right sibling or null if right sibling does not exist. + * @throws IOException if an IO error occurs + */ + FieldKeyRecordNode getNextLeaf() throws IOException; + + /** + * Determine if this record node has a left sibling. + * @return true if left sibling exists + * @throws IOException if IO error occurs + */ + boolean hasPreviousLeaf() throws IOException; + + /** + * Get this leaf node's left sibling + * @return this leaf node's left sibling or null if left sibling does not exist. + * @throws IOException if an IO error occurs + */ + FieldKeyRecordNode getPreviousLeaf() throws IOException; + + /** + * Remove this leaf from the tree. + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + FieldKeyNode removeLeaf() throws IOException; + + /** + * Delete the record identified by the specified key. + * @param key record key + * @param table table which will be notified when record is deleted. + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + FieldKeyNode deleteRecord(Field key, Table table) throws IOException; + + /** + * Get the record with the minimum key value which is greater than or equal + * to the specified key. + * @param key search key + * @param schema record data schema + * @return Record requested or null if record not found. + * @throws IOException thrown if IO error occurs + */ + Record getRecordAtOrAfter(Field key, Schema schema) throws IOException; + + /** + * Get the record with the maximum key value which is less than or equal + * to the specified key. + * @param key search key + * @param schema record data schema + * @return Record requested or null if record not found. + * @throws IOException thrown if IO error occurs + */ + Record getRecordAtOrBefore(Field key, Schema schema) throws IOException; + + /** + * Get the record with the minimum key value which is greater than + * the specified key. + * @param key search key + * @param schema record data schema + * @return Record requested or null if record not found. + * @throws IOException thrown if IO error occurs + */ + Record getRecordAfter(Field key, Schema schema) throws IOException; + + /** + * Get the record with the maximum key value which is less than + * the specified key. + * @param key search key + * @param schema record data schema + * @return Record requested or null if record not found. + * @throws IOException thrown if IO error occurs + */ + Record getRecordBefore(Field key, Schema schema) throws IOException; + + /** + * Get the record identified by the specified key. + * @param key search key + * @param schema record data schema + * @return Record requested or null if record not found. + * @throws IOException thrown if IO error occurs + */ + Record getRecord(Field key, Schema schema) throws IOException; + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedField.java b/Ghidra/Framework/DB/src/main/java/db/FixedField.java new file mode 100644 index 0000000000..acfa6dfb2f --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedField.java @@ -0,0 +1,55 @@ +/* ### + * 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 db; + +/** + * FixedField provides an abstract implementation of a fixed-length + * binary field. + */ +public abstract class FixedField extends BinaryField { + + /** + * Construct a fixed-length field + * @param data initial value + * @param immutable true if field value is immutable + */ + FixedField(byte[] data, boolean immutable) { + super(data, immutable); + } + + @Override + public final boolean isVariableLength() { + return false; + } + + @Override + void truncate(int length) { + throw new UnsupportedOperationException("Field may not be truncated"); + } + + @Override + public abstract FixedField copyField(); + + @Override + public abstract FixedField newField(); + + @Override + abstract FixedField getMinValue(); + + @Override + abstract FixedField getMaxValue(); + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedField10.java b/Ghidra/Framework/DB/src/main/java/db/FixedField10.java new file mode 100644 index 0000000000..1adf694b89 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedField10.java @@ -0,0 +1,215 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import generic.util.UnsignedDataUtils; +import ghidra.util.BigEndianDataConverter; + +/** + * FixedField10 is a 10-byte fixed-length binary field. + */ +public class FixedField10 extends FixedField { + + /** + * Minimum long field value + */ + public static FixedField10 MIN_VALUE = new FixedField10(0L, (short) 0, true); + + /** + * Maximum long field value + */ + public static FixedField10 MAX_VALUE = new FixedField10(-1L, (short) -1, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + @SuppressWarnings("hiding") + public static final FixedField10 INSTANCE = MIN_VALUE; + + // This implementation uses both a data byte array and short+long variables + // for data storage. While the short+long is always available, the data + // byte array is only set when needed or supplied during construction. + // The use of the short+long is done to speed-up comparison with other + // FixedField10 instances or directly from a DataBuffer. + private short lo2; + private long hi8; + + /** + * Construct a 10-byte fixed-length field with an initial value of 0. + */ + public FixedField10() { + super(null, false); + } + + /** + * Construct a 10-byte fixed-length field with an initial value of data. + * @param data initial 10-byte binary value + * @throws IllegalArgumentException thrown if data is not 10-bytes in length + */ + public FixedField10(byte[] data) { + this(data, false); + } + + /** + * Construct a 10-byte fixed-length binary field with an initial value of data. + * @param data initial 10-byte binary value + * @param immutable true if field value is immutable + * @throws IllegalArgumentException thrown if data is not 10-bytes in length + */ + public FixedField10(byte[] data, boolean immutable) { + super(null, immutable); + setBinaryData(data); + } + + FixedField10(long hi8, short lo2, boolean immutable) { + super(null, immutable); + this.hi8 = hi8; + this.lo2 = lo2; + } + + @Override + public int compareTo(Field o) { + if (!(o instanceof FixedField10)) { + throw new UnsupportedOperationException("may only compare similar Field types"); + } + FixedField10 f = (FixedField10) o; + if (hi8 != f.hi8) { + return UnsignedDataUtils.unsignedLessThan(hi8, f.hi8) ? -1 : 1; + } + if (lo2 != f.lo2) { + return UnsignedDataUtils.unsignedLessThan(lo2, f.lo2) ? -1 : 1; + } + return 0; + } + + @Override + int compareTo(DataBuffer buffer, int offset) { + long otherHi8 = buffer.getLong(offset); + if (hi8 != otherHi8) { + return UnsignedDataUtils.unsignedLessThan(hi8, otherHi8) ? -1 : 1; + } + short otherLo2 = buffer.getShort(offset + 8); + if (lo2 != otherLo2) { + return UnsignedDataUtils.unsignedLessThan(lo2, otherLo2) ? -1 : 1; + } + return 0; + } + + @Override + public FixedField copyField() { + return new FixedField10(hi8, lo2, false); + } + + @Override + public FixedField newField() { + return new FixedField10(); + } + + @Override + FixedField getMinValue() { + return MIN_VALUE; + } + + @Override + FixedField getMaxValue() { + return MAX_VALUE; + } + + @Override + public byte[] getBinaryData() { + if (data != null) { + return data; + } + data = new byte[10]; + BigEndianDataConverter.INSTANCE.putLong(data, 0, hi8); + BigEndianDataConverter.INSTANCE.putShort(data, 8, lo2); + return data; + } + + @Override + public void setBinaryData(byte[] data) { + if (data.length != 10) { + throw new IllegalArgumentException("Invalid FixedField10 length: " + data.length); + } + this.data = data; + hi8 = BigEndianDataConverter.INSTANCE.getLong(data, 0); + lo2 = BigEndianDataConverter.INSTANCE.getShort(data, 8); + } + + @Override + byte getFieldType() { + return FIXED_10_TYPE; + } + + @Override + int write(Buffer buf, int offset) throws IOException { + if (data != null) { + return buf.put(offset, data); + } + offset = buf.putLong(offset, hi8); + return buf.putShort(offset, lo2); + } + + @Override + int read(Buffer buf, int offset) throws IOException { + checkImmutable(); + data = null; // be lazy + hi8 = buf.getLong(offset); + lo2 = buf.getShort(offset + 8); + return offset + 10; + } + + @Override + int readLength(Buffer buf, int offset) throws IOException { + return 10; + } + + @Override + int length() { + return 10; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = (int) (hi8 ^ (hi8 >>> 32)); + result = prime * result + lo2; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + FixedField10 other = (FixedField10) obj; + if (hi8 != other.hi8) + return false; + if (lo2 != other.lo2) + return false; + return true; + } + + @Override + public String getValueAsString() { + return "{" + BinaryField.getValueAsString(getBinaryData()) + "}"; + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedIndexTable.java b/Ghidra/Framework/DB/src/main/java/db/FixedIndexTable.java deleted file mode 100644 index 9d74cf44a8..0000000000 --- a/Ghidra/Framework/DB/src/main/java/db/FixedIndexTable.java +++ /dev/null @@ -1,328 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package db; - -import java.io.IOException; -import java.util.NoSuchElementException; - -/** - * The FixedIndexTable provides a secondary index on a fixed-length table column - * (e.g., IntField, LongField, etc.). For each unique secondary index value, an IndexBuffer is - * stored within an underlying index Table record. The secondary index value is used as the long - * key to access this record. Within a single IndexBuffer is stored all primary keys which - * correspond to an index value. - */ -class FixedIndexTable extends IndexTable { - - private static final Class[] fieldClasses = { BinaryField.class, // index data - }; - - private static final String[] fieldNames = { "IndexBuffer" }; - - private static Schema indexSchema = new Schema(0, "IndexKey", fieldClasses, fieldNames); - - /** - * Construct a new secondary index which is based upon a field within the - * primary table specified by name. - * @param primaryTable primary table. - * @param colIndex identifies the indexed column within the primary table. - * @throws IOException thrown if an IO error occurs - */ - FixedIndexTable(Table primaryTable, int colIndex) throws IOException { - this(primaryTable, primaryTable.getDBHandle().getMasterTable().createTableRecord( - primaryTable.getName(), indexSchema, colIndex)); - } - - /** - * Construct a new or existing secondary index. An existing index must have - * its root ID specified within the tableRecord. - * @param primaryTable primary table. - * @param indexTableRecord specifies the index parameters. - * @throws IOException thrown if an IO error occurs - */ - FixedIndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException { - super(primaryTable, indexTableRecord); - } - - /** - * Find all primary keys which correspond to the specified indexed field - * value. - * @param indexValue the field value to search for. - * @return list of primary keys - * @throws IOException thrown if an IO error occurs - */ - @Override - long[] findPrimaryKeys(Field indexValue) throws IOException { - if (!indexValue.getClass().equals(fieldType.getClass())) { - throw new IllegalArgumentException("Incorrect indexed field type"); - } - Record indexRecord = indexTable.getRecord(indexValue.getLongValue()); - if (indexRecord == null) { - return emptyKeyArray; - } - IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0)); - return indexBuffer.getPrimaryKeys(); - } - - /** - * Get the number of primary keys which correspond to the specified indexed field - * value. - * @param indexValue the field value to search for. - * @return key count - */ - @Override - int getKeyCount(Field indexValue) throws IOException { - if (!indexValue.getClass().equals(fieldType.getClass())) { - throw new IllegalArgumentException("Incorrect indexed field type"); - } - Record indexRecord = indexTable.getRecord(indexValue.getLongValue()); - if (indexRecord == null) { - return 0; - } - IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0)); - return indexBuffer.keyCount; - } - - /* - * @see ghidra.framework.store.db.IndexTable#addEntry(ghidra.framework.store.db.Record) - */ - @Override - void addEntry(Record record) throws IOException { - Field indexField = record.getField(colIndex); - long secondaryKey = indexField.getLongValue(); - Record indexRecord = indexTable.getRecord(secondaryKey); - if (indexRecord == null) { - indexRecord = indexSchema.createRecord(secondaryKey); - } - IndexBuffer indexBuffer = new IndexBuffer(indexField, indexRecord.getBinaryData(0)); - indexBuffer.addEntry(record.getKey()); - indexRecord.setBinaryData(0, indexBuffer.getData()); - indexTable.putRecord(indexRecord); - } - - /* - * @see ghidra.framework.store.db.IndexTable#deleteEntry(ghidra.framework.store.db.Record) - */ - @Override - void deleteEntry(Record record) throws IOException { - Field indexField = record.getField(colIndex); - long secondaryKey = indexField.getLongValue(); - Record indexRecord = indexTable.getRecord(secondaryKey); - if (indexRecord != null) { - IndexBuffer indexBuffer = new IndexBuffer(indexField, indexRecord.getBinaryData(0)); - indexBuffer.deleteEntry(record.getKey()); - byte[] data = indexBuffer.getData(); - if (data == null) { - indexTable.deleteRecord(secondaryKey); - } - else { - indexRecord.setBinaryData(0, data); - indexTable.putRecord(indexRecord); - } - } - } - - /** - * Get the index buffer associated with the specified index key - * @param indexKey index key - * @return index buffer or null if not found - * @throws IOException thrown if IO error occurs - */ - private IndexBuffer getIndexBuffer(Field indexKey) throws IOException { - Record indexRec = indexTable.getRecord(indexKey.getLongValue()); - return indexRec != null ? new IndexBuffer(indexKey, indexRec.getBinaryData(0)) : null; - } - - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator() - */ - @Override - DBFieldIterator indexIterator() throws IOException { - return new IndexLongIterator(); - } - - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator(ghidra.framework.store.db.Field, ghidra.framework.store.db.Field, boolean) - */ - @Override - DBFieldIterator indexIterator(Field minField, Field maxField, boolean atMin) - throws IOException { - long min = minField != null ? minField.getLongValue() : Long.MIN_VALUE; - long max = maxField != null ? maxField.getLongValue() : Long.MAX_VALUE; - return new IndexLongIterator(min, max, atMin); - } - - /* - * @see db.IndexTable#indexIterator(db.Field, db.Field, db.Field, boolean) - */ - @Override - DBFieldIterator indexIterator(Field minField, Field maxField, Field startField, boolean before) - throws IOException { - if (startField == null) { - throw new IllegalArgumentException("starting index value required"); - } - long min = minField != null ? minField.getLongValue() : Long.MIN_VALUE; - long max = maxField != null ? maxField.getLongValue() : Long.MAX_VALUE; - return new IndexLongIterator(min, max, startField.getLongValue(), before); - } - - /** - * Iterates over index field values within a specified range. - */ - class IndexLongIterator implements DBFieldIterator { - - private Field lastKey; - private Field keyField; - private DBLongIterator indexIterator; - private boolean hasNext = false; - private boolean hasPrev = false; - - /** - * Construct an index field iterator starting with the minimum index value. - */ - IndexLongIterator() throws IOException { - indexIterator = indexTable.longKeyIterator(); - } - - /** - * Construct an index field iterator. The iterator is positioned at index - * value identified by startValue. - * @param startValue minimum index value or null if no minimum - * @param endValue maximum index value or null if no maximum - * @param atStart if true initial position is before startValue, else position - * is after endValue - * @throws IOException - */ - IndexLongIterator(long minValue, long maxValue, boolean atMin) throws IOException { - - long start = atMin ? minValue : maxValue; - indexIterator = indexTable.longKeyIterator(minValue, maxValue, start); - - if (indexIterator.hasNext()) { - indexIterator.next(); - if (atMin) { - indexIterator.previous(); - } - } - } - - /** - * @param min - * @param max - * @param longValue - * @param before - */ - public IndexLongIterator(long minValue, long maxValue, long start, boolean before) - throws IOException { - - indexIterator = indexTable.longKeyIterator(minValue, maxValue, start); - - if (indexIterator.hasNext()) { - long val = indexIterator.next(); - if (before || val != start) { - indexIterator.previous(); - } - } - } - - @Override - public boolean hasNext() throws IOException { - if (hasNext) { - return true; - } - try { - long key = indexIterator.next(); - keyField = fieldType.newField(); - keyField.setLongValue(key); - hasNext = true; - hasPrev = false; - } - catch (NoSuchElementException e) { - return false; - } - return true; - } - - @Override - public boolean hasPrevious() throws IOException { - if (hasPrev) { - return true; - } - try { - long key = indexIterator.previous(); - keyField = fieldType.newField(); - keyField.setLongValue(key); - hasNext = false; - hasPrev = true; - } - catch (NoSuchElementException e) { - return false; - } - return true; - } - - @Override - public Field next() throws IOException { - if (hasNext || hasNext()) { - hasNext = false; - hasPrev = true; - lastKey = keyField; - return keyField; - } - return null; - } - - @Override - public Field previous() throws IOException { - if (hasPrev || hasPrevious()) { - hasNext = true; - hasPrev = false; - lastKey = keyField; - return keyField; - } - return null; - } - - /** - * Delete all primary records which have the current - * index value (lastKey). - * @see db.DBFieldIterator#delete() - */ - @Override - public boolean delete() throws IOException { - if (lastKey == null) { - return false; - } - synchronized (db) { - IndexBuffer indexBuf = getIndexBuffer(lastKey); - if (indexBuf != null) { - long[] keys = indexBuf.getPrimaryKeys(); - for (long key : keys) { - primaryTable.deleteRecord(key); - } - // The following does not actually delete the index record since it - // should already have been removed with the removal of all associated - // primary records. Invoking this method allows the iterator to - // recover from the index table change. - indexIterator.delete(); - } - lastKey = null; - return true; - } - } - } - -} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedKeyFixedRecNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedKeyFixedRecNode.java new file mode 100644 index 0000000000..f6074252c8 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedKeyFixedRecNode.java @@ -0,0 +1,199 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; + +/** + * FixedKeyFixedRecNode is an implementation of a BTree leaf node + * which utilizes fixed-length key values and stores fixed-length records. + *

+ * This type of node has the following layout within a single DataBuffer + * (field size in bytes, where 'L' is the fixed length of the fixed-length + * key as specified by key type in associated Schema): + *

+ *   | NodeType(1) | KeyCount(4) | PrevLeafId(4) | NextLeafId(4) | Key0(L) | Rec0 | ...
+ * 
+ *   | KeyN(L) | RecN |
+ * 
+ */ +class FixedKeyFixedRecNode extends FixedKeyRecordNode { + + private static final int HEADER_SIZE = RECORD_LEAF_HEADER_SIZE; + + private static final int ENTRY_BASE_OFFSET = HEADER_SIZE; + + private static final int[] EMPTY_ID_LIST = new int[0]; + + private int entrySize; + private int recordLength; + + /** + * Construct an existing fixed-length key fixed-length record leaf node. + * @param nodeMgr table node manager instance + * @param buf node buffer + * @throws IOException if IO error occurs + */ + FixedKeyFixedRecNode(NodeMgr nodeMgr, DataBuffer buf) throws IOException { + super(nodeMgr, buf); + this.recordLength = nodeMgr.getTableSchema().getFixedLength(); + entrySize = keySize + recordLength; + } + + /** + * Construct a new fixed-length key fixed-length record leaf node. + * @param nodeMgr table node manager instance + * @param prevLeafId node buffer id for previous leaf ( < 0: no leaf) + * @param nextLeafId node buffer id for next leaf ( < 0 : no leaf) + * @throws IOException if IO error occurs + */ + FixedKeyFixedRecNode(NodeMgr nodeMgr, int prevLeafId, int nextLeafId) throws IOException { + super(nodeMgr, NodeMgr.FIXEDKEY_FIXED_REC_NODE, prevLeafId, nextLeafId); + this.recordLength = nodeMgr.getTableSchema().getFixedLength(); + entrySize = keySize + recordLength; + } + + @Override + FixedKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { + return new FixedKeyFixedRecNode(nodeMgr, prevLeafId, nextLeafId); + } + + @Override + public int getKeyOffset(int index) { + return ENTRY_BASE_OFFSET + (index * entrySize); + } + + /** + * Get the record offset within the buffer + * @param index key index + * @return record offset + */ + @Override + public int getRecordOffset(int index) { + return ENTRY_BASE_OFFSET + (index * entrySize); + } + + /** + * Shift all records by one starting with index to the end. + * @param index the smaller key index (0 <= index1) + * @param rightShift shift right by one record if true, else shift left by + * one record. + */ + private void shiftRecords(int index, boolean rightShift) { + + // No movement needed for appended record + if (index == keyCount) + return; + + // Determine block to be moved + int start = getRecordOffset(index); + int end = getRecordOffset(keyCount); + int len = end - start; + + // Move record data + int offset = start + (rightShift ? entrySize : -entrySize); + buffer.move(start, offset, len); + } + + @Override + public void remove(int index) { + + if (index < 0 || index >= keyCount) + throw new AssertException(); + + shiftRecords(index + 1, false); + setKeyCount(keyCount - 1); + } + + @Override + boolean insertRecord(int index, Record record) throws IOException { + + // Check for use of indirect chained record node(s) +// int len = record.length(); + + if (keyCount == ((buffer.length() - HEADER_SIZE) / entrySize)) + return false; // insufficient space for record storage + + // Make room for new record + shiftRecords(index, true); + + // Store new record + int offset = getRecordOffset(index); + record.getKeyField().write(buffer, offset); + record.write(buffer, offset + keySize); + setKeyCount(keyCount + 1); + + return true; + } + + @Override + FixedKeyNode updateRecord(int index, Record record) throws IOException { + int offset = getRecordOffset(index) + keySize; + record.write(buffer, offset); + return getRoot(); + } + + @Override + public Record getRecord(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) + return null; + Record record = schema.createRecord(key); + record.read(buffer, getRecordOffset(index) + keySize); + return record; + } + + @Override + public Record getRecord(Schema schema, int index) throws IOException { + Field key = getKeyField(index); + Record record = schema.createRecord(key); + record.read(buffer, getRecordOffset(index) + keySize); + return record; + } + + @Override + void splitData(FixedKeyRecordNode newRightLeaf) { + + FixedKeyFixedRecNode rightNode = (FixedKeyFixedRecNode) newRightLeaf; + + int splitIndex = keyCount / 2; + int count = keyCount - splitIndex; + int start = getRecordOffset(splitIndex); // start of block to be moved + int end = getRecordOffset(keyCount); // end of block to be moved + int splitLen = end - start; // length of block to be moved + + // Copy data to new leaf node + rightNode.buffer.copy(ENTRY_BASE_OFFSET, buffer, start, splitLen); + + // Adjust key counts + setKeyCount(keyCount - count); + rightNode.setKeyCount(count); + } + + @Override + public void delete() throws IOException { + nodeMgr.deleteNode(this); + } + + @Override + public int[] getBufferReferences() { + return EMPTY_ID_LIST; + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedKeyInteriorNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedKeyInteriorNode.java new file mode 100644 index 0000000000..2961313e9b --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedKeyInteriorNode.java @@ -0,0 +1,610 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import ghidra.util.Msg; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * FixedKeyInteriorNode stores a BTree node for use as an interior + * node when searching for Table records within the database. This type of node + * has the following layout within a single DataBuffer (field size in bytes, + * where 'L' is the fixed length of the fixed-length key as specified by + * key type in associated Schema): + *
+ *   | NodeType(1) | KeyCount(4) | Key0(L) | ID0(4) | ... | KeyN(L) | IDN(4) |
+ * 
+ */ +class FixedKeyInteriorNode extends FixedKeyNode implements FieldKeyInteriorNode { + + private static final int BASE = FIXEDKEY_NODE_HEADER_SIZE; + + private static final int ID_SIZE = 4; // int + + private final int maxKeyCount; + private final int entrySize; + + /** + * Construct an existing fixed-length key interior node. + * @param nodeMgr table node manager instance + * @param buf node buffer + * @throws IOException thrown if IO error occurs + */ + FixedKeyInteriorNode(NodeMgr nodeMgr, DataBuffer buf) throws IOException { + super(nodeMgr, buf); + entrySize = keySize + ID_SIZE; + maxKeyCount = (buffer.length() - BASE) / entrySize; + } + + /** + * Construct a new fixed-length key interior node with two child nodes. + * @param nodeMgr table node manager. + * @param keyType key Field type + * @param key1 left child node left-most key + * @param id1 left child node buffer ID + * @param key2 right child node left-most key + * @param id2 right child node buffer ID + * @throws IOException thrown if IO error occurs + */ + FixedKeyInteriorNode(NodeMgr nodeMgr, Field keyType, byte[] key1, int id1, byte[] key2, int id2) + throws IOException { + super(nodeMgr, NodeMgr.FIXEDKEY_INTERIOR_NODE); + if (keySize != key1.length || keySize != key2.length) { + throw new IllegalArgumentException("mismatched fixed-length key sizes"); + } + entrySize = keySize + ID_SIZE; + maxKeyCount = (buffer.length() - BASE) / entrySize; + setKeyCount(2); + + // Store key and node ids + putEntry(0, key1, id1); + putEntry(1, key2, id2); + } + + /** + * Construct a new empty fixed-length key interior node. + * Node must be initialized with a minimum of two keys. + * @param nodeMgr table node manager. + * @param keyType key Field type + * @throws IOException thrown if IO error occurs + */ + private FixedKeyInteriorNode(NodeMgr nodeMgr, Field keyType) throws IOException { + super(nodeMgr, NodeMgr.FIXEDKEY_INTERIOR_NODE); + entrySize = keySize + ID_SIZE; + maxKeyCount = (buffer.length() - BASE) / entrySize; + } + + void logConsistencyError(String tableName, String msg, Throwable t) { + Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); + Msg.debug(this, " parent.key[0]=" + BinaryField.getValueAsString(getKey(0)) + + " bufferID=" + getBufferId()); + if (t != null) { + Msg.error(this, "Consistency Error (" + tableName + ")", t); + } + } + + @Override + public boolean isConsistent(String tableName, TaskMonitor monitor) + throws IOException, CancelledException { + boolean consistent = true; + Field lastMinKey = null; + Field lastMaxKey = null; + for (int i = 0; i < keyCount; i++) { + + // Compare each key entry with the previous entries key-range + Field key = getKeyField(i); + if (lastMinKey != null && key.compareTo(lastMinKey) <= 0) { + consistent = false; + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].minKey", null); + Msg.debug(this, " child[" + i + "].minKey = " + key.getValueAsString() + + " bufferID=" + getBufferId(i)); + Msg.debug(this, " child[" + (i - 1) + "].minKey = " + + lastMinKey.getValueAsString() + " bufferID=" + getBufferId(i - 1)); + } + else if (lastMaxKey != null && key.compareTo(lastMaxKey) <= 0) { + consistent = false; + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].maxKey", null); + Msg.debug(this, " child[" + i + "].minKey = " + key.getValueAsString() + + " bufferID=" + getBufferId(i)); + Msg.debug(this, " child[" + (i - 1) + "].maxKey = " + + lastMaxKey.getValueAsString() + " bufferID=" + getBufferId(i - 1)); + } + + lastMinKey = key; + + FixedKeyNode node = null; + try { + try { + node = nodeMgr.getFixedKeyNode(getBufferId(i)); + node.parent = this; + } + catch (IOException e) { + logConsistencyError(tableName, "failed to fetch child node: " + e.getMessage(), + e); + } + catch (RuntimeException e) { + logConsistencyError(tableName, "failed to fetch child node: " + e.getMessage(), + e); + } + + if (node == null) { + consistent = false; + lastMaxKey = key; // for lack of a better solution + continue; // skip child + } + + lastMaxKey = node.getKeyField(node.getKeyCount() - 1); + + // Verify key matchup between parent and child + Field childKey0 = node.getKeyField(0); + if (!key.equals(childKey0)) { + consistent = false; + logConsistencyError(tableName, + "parent key entry mismatch with child[" + i + "].minKey", null); + Msg.debug(this, " child[" + i + "].minKey = " + childKey0.getValueAsString() + + " bufferID=" + getBufferId(i - 1)); + Msg.debug(this, " parent key entry = " + key.getValueAsString()); + } + + consistent &= node.isConsistent(tableName, monitor); + monitor.checkCanceled(); + } + finally { + if (node != null) { + // Release nodes as we go - this is not the norm! + nodeMgr.releaseReadOnlyNode(node.getBufferId()); + } + } + } + monitor.checkCanceled(); + return consistent; + } + + /** + * Perform a binary search to locate the specified key and derive an index + * into the Buffer ID storage. This method is intended to locate the child + * node which contains the specified key. The returned index corresponds + * to a child's stored buffer/node ID and may correspond to another interior + * node or a leaf record node. Each stored key within this interior node + * effectively identifies the maximum key contained within the corresponding + * child node. + * @param key key to search for + * @return int buffer ID index of child node. An existing positive index + * value will always be returned. + */ + int getIdIndex(Field key) { + + int min = 1; + int max = keyCount - 1; + + while (min <= max) { + int i = (min + max) / 2; + int c = compareKeyField(key, i); + if (c == 0) { + return i; + } + else if (c > 0) { + min = i + 1; + } + else { + max = i - 1; + } + } + return max; + } + + @Override + public int getKeyIndex(Field key) { + + int min = 0; + int max = keyCount - 1; + + while (min <= max) { + int i = (min + max) / 2; + int rc = compareKeyField(key, i); + if (rc == 0) { + return i; + } + else if (rc > 0) { + min = i + 1; + } + else { + max = i - 1; + } + } + return -(min + 1); + } + + @Override + byte[] getKey(int index) { + byte[] key = new byte[keySize]; + buffer.get(BASE + (index * entrySize), key); + return key; + } + + @Override + public int compareKeyField(Field k, int keyIndex) { + return k.compareTo(buffer, BASE + (keyIndex * entrySize)); + } + + /** + * Store a key at the specified index + * @param index key index + * @param key key value + */ + private void putKey(int index, byte[] key) { + buffer.put(BASE + (index * entrySize), key); + } + + /** + * Get the child node buffer ID associated with the specified key index + * @param index child key index + * @return child node buffer ID + */ + private int getBufferId(int index) { + return buffer.getInt(BASE + (index * entrySize) + keySize); + } + + /** + * Store the child node entry (key and buffer ID) associated with the specified key index. + * The entry at index is overwritten. Since each entry is a fixed length, movement of + * existing entries is not necessary. + * @param index child key index + * @param key child node key + * @param bufferId child node buffer ID + */ + private void putEntry(int index, byte[] key, int bufferId) { + int offset = BASE + (index * entrySize); + buffer.put(offset, key); + buffer.putInt(offset + keySize, bufferId); + } + + /** + * Insert the child node entry (key and buffer ID) associated with the specified key index. + * All entries at and after index are shifted right to make space for new entry. + * The node key count is adjusted to reflect the addition of a child. + * @param index child key index + * @param key child node key + * @param bufferId child node buffer ID + */ + private void insertEntry(int index, byte[] key, int bufferId) { + + int start = BASE + (index * entrySize); + int end = BASE + (keyCount * entrySize); + buffer.move(start, start + entrySize, end - start); + buffer.put(start, key); + buffer.putInt(start + keySize, bufferId); + + setKeyCount(keyCount + 1); + } + + /** + * Delete the child node entry (key and buffer ID) associated with the specified key index. + * All entries after index are shifted left. + * The node key count is adjusted to reflect the removal of a child. + * @param index child key index + */ + private void deleteEntry(int index) { + + if (keyCount < 3 || index >= keyCount) + throw new AssertException(); + + ++index; + if (index < keyCount) { + int start = BASE + (index * entrySize); + int end = BASE + (keyCount * entrySize); + buffer.move(start, start - entrySize, end - start); + } + setKeyCount(keyCount - 1); + } + + /** + * Callback method for when a child node's leftmost key changes. + * @param oldKey previous leftmost key. + * @param newKeyData new leftmost key. + */ + void keyChanged(Field oldKey, byte[] newKeyData) { + + int index = getKeyIndex(oldKey); + if (index < 0) { + throw new AssertException(); + } + // Update key + putKey(index, newKeyData); + if (index == 0 && parent != null) { + parent.keyChanged(oldKey, newKeyData); + } + } + + @Override + public void keyChanged(Field oldKey, Field newKey, FieldKeyNode childNode) throws IOException { + keyChanged(oldKey, newKey.getBinaryData()); + } + + /** + * Insert a new node into this node. + * @param id id of new node + * @param key leftmost key associated with new node. + * @return root node. + * @throws IOException thrown if an IO error occurs + */ + FixedKeyNode insert(int id, Field key) throws IOException { + + // Split this node if full + if (keyCount == maxKeyCount) { + return split(key, id); + } + + // Insert key into this node + int index = -(getKeyIndex(key) + 1); + if (index < 0 || id == 0) + throw new AssertException(); + byte[] keyData = key.getBinaryData(); + insertEntry(index, keyData, id); + + if (index == 0 && parent != null) { + parent.keyChanged(getKeyField(1), keyData); + } + + return getRoot(); + } + + /** + * Split this interior node and insert new child entry (key and buffer ID). + * Assumes 3 or more child keys exist in this node. + * @param newKey new child key + * @param newId new child node's buffer ID + * @return root node. + * @throws IOException thrown if IO error occurs + */ + private FixedKeyNode split(Field newKey, int newId) throws IOException { + + // Create new interior node + FixedKeyInteriorNode newNode = new FixedKeyInteriorNode(nodeMgr, keyType); + + moveKeysRight(this, newNode, keyCount / 2); + + // Insert new key/id + Field rightKey = newNode.getKeyField(0); + if (newKey.compareTo(rightKey) < 0) { + insert(newId, newKey); + } + else { + newNode.insert(newId, newKey); + } + + if (parent != null) { + // Ask parent to insert new node and return root + return parent.insert(newNode.getBufferId(), rightKey); + } + + // New parent node becomes root + return new FixedKeyInteriorNode(nodeMgr, keyType, getKey(0), buffer.getId(), + rightKey.getBinaryData(), newNode.getBufferId()); + } + + @Override + public FixedKeyRecordNode getLeafNode(Field key) throws IOException { + FixedKeyNode node = nodeMgr.getFixedKeyNode(getBufferId(getIdIndex(key))); + node.parent = this; + return (FixedKeyRecordNode) node.getLeafNode(key); + } + + @Override + public FieldKeyRecordNode getLeftmostLeafNode() throws IOException { + FixedKeyNode node = nodeMgr.getFixedKeyNode(getBufferId(0)); + return node.getLeftmostLeafNode(); + } + + @Override + public FieldKeyRecordNode getRightmostLeafNode() throws IOException { + FixedKeyNode node = nodeMgr.getFixedKeyNode(getBufferId(keyCount - 1)); + return node.getRightmostLeafNode(); + } + + /** + * Callback method allowing child node to remove itself from parent. + * Rebalancing of the tree is performed if the interior node falls + * below the half-full point. + * @param key child node key + * @return root node + * @throws IOException thrown if IO error occurs + */ + FixedKeyNode deleteChild(Field key) throws IOException { + + int index = getKeyIndex(key); + if (index < 0) + throw new AssertException(); + + // Handle ellimination of this node + if (keyCount == 2) { + if (parent != null) + throw new AssertException(); + FixedKeyNode rootNode = nodeMgr.getFixedKeyNode(getBufferId(1 - index)); + rootNode.parent = null; + nodeMgr.deleteNode(this); + return rootNode; + } + + // Delete child entry + deleteEntry(index); + if (index == 0 && parent != null) { + parent.keyChanged(key, getKey(0)); + } + + return (parent != null) ? parent.balanceChild(this) : this; + } + + /** + * Callback method allowing a child interior node to request balancing of its + * content with its sibling nodes. Balancing is only done if the specified node + * is half-full or less. + * @param node child interior node + * @return root node + */ + private FixedKeyNode balanceChild(FixedKeyInteriorNode node) throws IOException { + + // Do nothing if node more than half full + if (node.keyCount > maxKeyCount / 2) { + return getRoot(); + } + + // balance with right sibling except if node corresponds to the right-most + // key within this interior node - in that case balance with left sibling. + int index = getIdIndex(node.getKeyField(0)); + if (index == (keyCount - 1)) { + return balanceChild( + (FixedKeyInteriorNode) nodeMgr.getFixedKeyNode(getBufferId(index - 1)), node); + } + return balanceChild(node, + (FixedKeyInteriorNode) nodeMgr.getFixedKeyNode(getBufferId(index + 1))); + } + + /** + * Balance the entries contained within two adjacent child interior nodes. + * One of the two nodes must be half-full or less. + * This could result in the removal of a child node if entries will fit within + * one node. + * @param leftNode left child interior node + * @param rightNode right child interior node + * @return new root + * @throws IOException thrown if an IO error occurs + */ + private FixedKeyNode balanceChild(FixedKeyInteriorNode leftNode, FixedKeyInteriorNode rightNode) + throws IOException { + + Field rightKey = rightNode.getKeyField(0); + int leftKeyCount = leftNode.keyCount; + int rightKeyCount = rightNode.keyCount; + int newLeftKeyCount = leftKeyCount + rightKeyCount; + + // Can right keys fit within left node + if (newLeftKeyCount <= maxKeyCount) { + // Right node is elliminated and all entries stored in left node + moveKeysLeft(leftNode, rightNode, rightKeyCount); + nodeMgr.deleteNode(rightNode); + return deleteChild(rightKey); + } + + newLeftKeyCount = newLeftKeyCount / 2; + if (newLeftKeyCount < leftKeyCount) { + moveKeysRight(leftNode, rightNode, leftKeyCount - newLeftKeyCount); + } + else if (newLeftKeyCount > leftKeyCount) { + moveKeysLeft(leftNode, rightNode, newLeftKeyCount - leftKeyCount); + } + this.keyChanged(rightKey, rightNode.getKey(0)); + return getRoot(); + } + + /** + * Move some (not all) of the entries from the left node into the right node. + * @param leftNode + * @param rightNode + * @param count + */ + private static void moveKeysRight(FixedKeyInteriorNode leftNode, FixedKeyInteriorNode rightNode, + int count) { + + if (leftNode.keySize != rightNode.keySize) { + throw new IllegalArgumentException("mismatched fixed key sizes"); + } + int leftKeyCount = leftNode.keyCount; + int rightKeyCount = rightNode.keyCount; + int leftOffset = BASE + ((leftKeyCount - count) * leftNode.entrySize); + int len = count * leftNode.entrySize; + rightNode.buffer.move(BASE, BASE + len, rightKeyCount * leftNode.entrySize); + rightNode.buffer.copy(BASE, leftNode.buffer, leftOffset, len); + leftNode.setKeyCount(leftKeyCount - count); + rightNode.setKeyCount(rightKeyCount + count); + } + + /** + * Move some or all of the entries from the right node into the left node. + * If all keys are moved, the caller is responsible for deleting the right + * node. + * @param leftNode + * @param rightNode + * @param count + */ + private static void moveKeysLeft(FixedKeyInteriorNode leftNode, FixedKeyInteriorNode rightNode, + int count) { + if (leftNode.keySize != rightNode.keySize) { + throw new IllegalArgumentException("mismatched fixed key sizes"); + } + int leftKeyCount = leftNode.keyCount; + int rightKeyCount = rightNode.keyCount; + int leftOffset = BASE + (leftKeyCount * leftNode.entrySize); + int len = count * leftNode.entrySize; + leftNode.buffer.copy(leftOffset, rightNode.buffer, BASE, len); + leftNode.setKeyCount(leftKeyCount + count); + if (count < rightKeyCount) { + // Only need to update right node if partial move + rightKeyCount -= count; + rightNode.buffer.move(BASE + len, BASE, rightKeyCount * leftNode.entrySize); + rightNode.setKeyCount(rightKeyCount); + } + } + + @Override + public void delete() throws IOException { + + // Delete all child nodes + for (int index = 0; index < keyCount; index++) { + nodeMgr.getFixedKeyNode(getBufferId(index)).delete(); + } + + // Remove this node + nodeMgr.deleteNode(this); + } + + @Override + public int[] getBufferReferences() { + int[] ids = new int[keyCount]; + for (int i = 0; i < keyCount; i++) { + ids[i] = getBufferId(i); + } + return ids; + } + + boolean isLeftmostKey(Field key) { + if (getIdIndex(key) == 0) { + if (parent != null) { + return parent.isLeftmostKey(key); + } + return true; + } + return false; + } + + boolean isRightmostKey(Field key) { + if (getIdIndex(key) == (keyCount - 1)) { + if (parent != null) { + return parent.isRightmostKey(getKeyField(0)); + } + return true; + } + return false; + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedKeyNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedKeyNode.java new file mode 100644 index 0000000000..f06a1cd6c8 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedKeyNode.java @@ -0,0 +1,136 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; + +/** + * FixedKeyNode is an abstract implementation of a BTree node + * which utilizes fixed-length key values. + *
+ *   | NodeType(1) | KeyCount(4) | ...
+ * 
+ */ +abstract class FixedKeyNode implements FieldKeyNode { + + private static final int KEY_COUNT_SIZE = 4; + private static final int KEY_COUNT_OFFSET = NodeMgr.NODE_HEADER_SIZE; + + static final int FIXEDKEY_NODE_HEADER_SIZE = NodeMgr.NODE_HEADER_SIZE + KEY_COUNT_SIZE; + + protected final Field keyType; + protected final int keySize; + + protected NodeMgr nodeMgr; + protected DataBuffer buffer; + protected FixedKeyInteriorNode parent; + protected int keyCount; + + /** + * Construct an existing fixed-length key node. + * @param nodeMgr table node manager instance + * @param buf node buffer + * @throws IOException thrown if IO error occurs + */ + FixedKeyNode(NodeMgr nodeMgr, DataBuffer buf) throws IOException { + this.nodeMgr = nodeMgr; + buffer = buf; + Schema schema = nodeMgr.getTableSchema(); + if (!schema.useFixedKeyNodes()) { + throw new AssertException("unsupported schema"); + } + keyType = schema.getKeyFieldType(); + keySize = keyType.length(); + keyCount = buffer.getInt(KEY_COUNT_OFFSET); + nodeMgr.addNode(this); + } + + /** + * Construct a new fixed-length key node. + * @param nodeMgr table node manager. + * @param nodeType node type + * @throws IOException thrown if IO error occurs + */ + FixedKeyNode(NodeMgr nodeMgr, byte nodeType) throws IOException { + this.nodeMgr = nodeMgr; + buffer = nodeMgr.getBufferMgr().createBuffer(); + NodeMgr.setNodeType(buffer, nodeType); + Schema schema = nodeMgr.getTableSchema(); + if (!schema.useFixedKeyNodes()) { + throw new AssertException("unsupported schema"); + } + keyType = schema.getKeyFieldType(); + keySize = keyType.length(); + setKeyCount(0); + nodeMgr.addNode(this); + } + + @Override + public FixedKeyInteriorNode getParent() { + return parent; + } + + @Override + public int getBufferId() { + return buffer.getId(); + } + + @Override + public DataBuffer getBuffer() { + return buffer; + } + + /** + * Get the root for this node. If setParent has not been invoked, this node + * is assumed to be the root. + * @return root node + */ + FixedKeyNode getRoot() { + if (parent != null) { + return parent.getRoot(); + } + return this; + } + + @Override + public int getKeyCount() { + return keyCount; + } + + @Override + public void setKeyCount(int cnt) { + keyCount = cnt; + buffer.putInt(KEY_COUNT_OFFSET, keyCount); + } + + /** + * Get the key value at a specific index. + * @param index key index + * @return key value + */ + abstract byte[] getKey(int index); + + @Override + public final Field getKeyField(int index) { + Field key = keyType.newField(); + key.setBinaryData(getKey(index)); + return key; + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedKeyRecordNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedKeyRecordNode.java new file mode 100644 index 0000000000..5680eab651 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedKeyRecordNode.java @@ -0,0 +1,508 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * FixedKeyRecordNode is an abstract implementation of a BTree leaf node + * which utilizes fixed-length binary key values and stores records. + *

+ * This type of node has the following partial layout within a single DataBuffer + * (field size in bytes): + *

+ *   | NodeType(1) | KeyCount(4) | PrevLeafId(4) | NextLeafId(4) ...
+ * 
+ */ +abstract class FixedKeyRecordNode extends FixedKeyNode implements FieldKeyRecordNode { + + private static final int ID_SIZE = 4; + + private static final int PREV_LEAF_ID_OFFSET = FIXEDKEY_NODE_HEADER_SIZE; + private static final int NEXT_LEAF_ID_OFFSET = PREV_LEAF_ID_OFFSET + ID_SIZE; + + static final int RECORD_LEAF_HEADER_SIZE = FIXEDKEY_NODE_HEADER_SIZE + (2 * ID_SIZE); + + /** + * Construct an existing fixed-length key record leaf node. + * @param nodeMgr table node manager instance + * @param buf node buffer + * @throws IOException thrown if an IO error occurs + */ + FixedKeyRecordNode(NodeMgr nodeMgr, DataBuffer buf) throws IOException { + super(nodeMgr, buf); + } + + /** + * Construct a new fixed-length key record leaf node. + * @param nodeMgr table node manager instance + * @param nodeType node type + * @param prevLeafId node buffer id for previous leaf - left sibling ( < 0: no leaf) + * @param nextLeafId node buffer id for next leaf - right sibling ( < 0 : no leaf) + * @throws IOException thrown if an IO error occurs + */ + FixedKeyRecordNode(NodeMgr nodeMgr, byte nodeType, int prevLeafId, int nextLeafId) + throws IOException { + super(nodeMgr, nodeType); + + // Initialize header + buffer.putInt(PREV_LEAF_ID_OFFSET, prevLeafId); + buffer.putInt(NEXT_LEAF_ID_OFFSET, nextLeafId); + } + + void logConsistencyError(String tableName, String msg, Throwable t) { + Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); + Msg.debug(this, + " bufferID=" + getBufferId() + " key[0]=" + BinaryField.getValueAsString(getKey(0))); + if (t != null) { + Msg.error(this, "Consistency Error (" + tableName + ")", t); + } + } + + @Override + public boolean isConsistent(String tableName, TaskMonitor monitor) + throws IOException, CancelledException { + boolean consistent = true; + Field prevKey = null; + for (int i = 0; i < keyCount; i++) { + // Compare each key entry with the previous key + Field key = getKeyField(i); + if (prevKey != null && key.compareTo(prevKey) <= 0) { + consistent = false; + logConsistencyError(tableName, "key[" + i + "] <= key[" + (i - 1) + "]", null); + Msg.debug(this, " key[" + i + "].minKey = " + key.getValueAsString()); + Msg.debug(this, " key[" + (i - 1) + "].minKey = " + prevKey.getValueAsString()); + } + prevKey = key; + } + + Field key0 = getKeyField(0); + if ((parent == null || parent.isLeftmostKey(key0)) && getPreviousLeaf() != null) { + consistent = false; + logConsistencyError(tableName, "previous-leaf should not exist", null); + } + + FixedKeyRecordNode node = getNextLeaf(); + if (node != null) { + if (parent == null || parent.isRightmostKey(key0)) { + consistent = false; + logConsistencyError(tableName, "next-leaf should not exist", null); + } + else { + FixedKeyRecordNode me = node.getPreviousLeaf(); + if (me != this) { + consistent = false; + logConsistencyError(tableName, "next-leaf is not linked to this leaf", null); + } + } + } + else if (parent != null && !parent.isRightmostKey(key0)) { + consistent = false; + logConsistencyError(tableName, "this leaf is not linked to next-leaf", null); + } + + return consistent; + } + + @Override + byte[] getKey(int index) { + byte[] key = new byte[keySize]; + buffer.get(getKeyOffset(index), key); + return key; + } + + @Override + public int compareKeyField(Field k, int keyIndex) { + return k.compareTo(buffer, getKeyOffset(keyIndex)); + } + + /** + * Get the key offset within the node's data buffer + * @param index key/record index + * @return positive record offset within buffer + */ + @Override + public abstract int getKeyOffset(int index); + + @Override + public FixedKeyRecordNode getLeafNode(Field key) throws IOException { + return this; + } + + @Override + public FixedKeyRecordNode getLeftmostLeafNode() throws IOException { + FixedKeyRecordNode leaf = getPreviousLeaf(); + return leaf != null ? leaf.getLeftmostLeafNode() : this; + } + + @Override + public FixedKeyRecordNode getRightmostLeafNode() throws IOException { + FixedKeyRecordNode leaf = getNextLeaf(); + return leaf != null ? leaf.getRightmostLeafNode() : this; + } + + @Override + public boolean hasNextLeaf() throws IOException { + int nextLeafId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + return (nextLeafId >= 0); + } + + @Override + public FixedKeyRecordNode getNextLeaf() throws IOException { + FixedKeyRecordNode leaf = null; + int nextLeafId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + if (nextLeafId >= 0) { + leaf = (FixedKeyRecordNode) nodeMgr.getFixedKeyNode(nextLeafId); + } + return leaf; + } + + @Override + public boolean hasPreviousLeaf() throws IOException { + int prevLeafId = buffer.getInt(PREV_LEAF_ID_OFFSET); + return (prevLeafId >= 0); + } + + @Override + public FixedKeyRecordNode getPreviousLeaf() throws IOException { + FixedKeyRecordNode leaf = null; + int prevLeafId = buffer.getInt(PREV_LEAF_ID_OFFSET); + if (prevLeafId >= 0) { + leaf = (FixedKeyRecordNode) nodeMgr.getFixedKeyNode(prevLeafId); + } + return leaf; + } + + @Override + public int getKeyIndex(Field key) { + + int min = 0; + int max = keyCount - 1; + + while (min <= max) { + int i = (min + max) / 2; + int rc = compareKeyField(key, i); + if (rc == 0) { + return i; + } + else if (rc > 0) { + min = i + 1; + } + else { + max = i - 1; + } + } + return -(min + 1); + } + + /** + * Split this leaf node in half and update tree. + * When a split is performed, the next operation must be performed + * from the root node since the tree may have been restructured. + * @return root node which may have changed. + * @throws IOException thrown if an IO error occurs + */ + FixedKeyNode split() throws IOException { + + // Create new leaf + int oldSiblingId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + FixedKeyRecordNode newLeaf = createNewLeaf(buffer.getId(), oldSiblingId); + DataBuffer newBuf = newLeaf.buffer; + int newBufId = newBuf.getId(); + buffer.putInt(NEXT_LEAF_ID_OFFSET, newBufId); + + if (oldSiblingId >= 0) { + FixedKeyRecordNode leaf = (FixedKeyRecordNode) nodeMgr.getFixedKeyNode(oldSiblingId); + leaf.buffer.putInt(PREV_LEAF_ID_OFFSET, newBufId); + } + + // Split node creating two balanced leaves + splitData(newLeaf); + + if (parent != null) { + // Ask parent to insert new node and return root + return parent.insert(newBufId, newLeaf.getKeyField(0)); + } + + // New parent node becomes root + return new FixedKeyInteriorNode(nodeMgr, keyType, getKey(0), buffer.getId(), + newLeaf.getKey(0), newBufId); + } + + /** + * Append a leaf which contains one or more keys and update tree. Leaf is inserted + * as the new right sibling of this leaf. + * @param leaf new right sibling leaf (must be same node type as this leaf) + * @return root node which may have changed. + * @throws IOException thrown if an IO error occurs + */ + FixedKeyNode appendLeaf(FixedKeyRecordNode leaf) throws IOException { + + // Create new leaf and link + leaf.buffer.putInt(PREV_LEAF_ID_OFFSET, buffer.getId()); + int rightLeafBufId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + leaf.buffer.putInt(NEXT_LEAF_ID_OFFSET, rightLeafBufId); + + // Adjust this node + int newBufId = leaf.buffer.getId(); + buffer.putInt(NEXT_LEAF_ID_OFFSET, newBufId); + + // Adjust old right node if present + if (rightLeafBufId >= 0) { + FixedKeyNode rightLeaf = nodeMgr.getFixedKeyNode(rightLeafBufId); + rightLeaf.buffer.putInt(PREV_LEAF_ID_OFFSET, newBufId); + } + + if (parent != null) { + // Ask parent to insert new node and return root - leaf parent is unknown + return parent.insert(newBufId, leaf.getKeyField(0)); + } + + // New parent node becomes root + return new FixedKeyInteriorNode(nodeMgr, keyType, getKey(0), buffer.getId(), leaf.getKey(0), + newBufId); + } + + /** + * Remove this leaf from the tree. + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + @Override + public FixedKeyNode removeLeaf() throws IOException { + + Field key = getKeyField(0); + int prevBufferId = buffer.getInt(PREV_LEAF_ID_OFFSET); + int nextBufferId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + if (prevBufferId >= 0) { + FixedKeyRecordNode prevNode = + (FixedKeyRecordNode) nodeMgr.getFixedKeyNode(prevBufferId); + prevNode.getBuffer().putInt(NEXT_LEAF_ID_OFFSET, nextBufferId); + } + if (nextBufferId >= 0) { + FixedKeyRecordNode nextNode = + (FixedKeyRecordNode) nodeMgr.getFixedKeyNode(nextBufferId); + nextNode.getBuffer().putInt(PREV_LEAF_ID_OFFSET, prevBufferId); + } + + nodeMgr.deleteNode(this); + if (parent == null) { + return null; + } + return parent.deleteChild(key); + } + + /** + * Split the contents of this leaf node; placing the right half of the records into the + * empty leaf node provided. + * @param newRightLeaf empty right sibling leaf + */ + abstract void splitData(FixedKeyRecordNode newRightLeaf); + + /** + * Create a new leaf and add to the node manager. + * The new leaf's parent is unknown. + * @param prevNodeId node buffer id for previous leaf - left sibling ( < 0: no leaf) + * @param nextNodeId node buffer id for next leaf - right sibling ( < 0 : no leaf) + * @return new leaf node. + * @throws IOException thrown if IO error occurs + */ + abstract FixedKeyRecordNode createNewLeaf(int prevNodeId, int nextNodeId) throws IOException; + + @Override + public FixedKeyNode putRecord(Record record, Table table) throws IOException { + + Field key = record.getKeyField(); + int index = getKeyIndex(key); + + // Handle record update case + if (index >= 0) { + if (table != null) { + table.updatedRecord(getRecord(table.getSchema(), index), record); + } + FixedKeyNode newRoot = updateRecord(index, record); + return newRoot; + } + + // Handle new record - see if we have room in this leaf + index = -index - 1; + if (insertRecord(index, record)) { + if (index == 0 && parent != null) { + parent.keyChanged(getKeyField(1), key, null); + } + if (table != null) { + table.insertedRecord(record); + } + return getRoot(); + } + + // Special Case - append new leaf to right + if (index == keyCount) { + FixedKeyNode newRoot = appendNewLeaf(record); + if (table != null) { + table.insertedRecord(record); + } + return newRoot; + } + + // Split leaf and complete insertion + FixedKeyRecordNode leaf = (FixedKeyRecordNode) split().getLeafNode(key); + return leaf.putRecord(record, table); + } + + /** + * Append a new leaf and insert the specified record. + * @param record data record with long key + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + FixedKeyNode appendNewLeaf(Record record) throws IOException { + FixedKeyRecordNode newLeaf = createNewLeaf(-1, -1); + newLeaf.insertRecord(0, record); + return appendLeaf(newLeaf); + } + + @Override + public FieldKeyNode deleteRecord(Field key, Table table) throws IOException { + + // Handle non-existent key - do nothing + int index = getKeyIndex(key); + if (index < 0) { + return getRoot(); + } + + if (table != null) { + table.deletedRecord(getRecord(table.getSchema(), index)); + } + + // Handle removal of last record in node + if (keyCount == 1) { + FixedKeyNode newRoot = removeLeaf(); + return newRoot; + } + + // Remove record within this node + remove(index); + + // Notify parent of leftmost key change + if (index == 0 && parent != null) { + parent.keyChanged(key, getKey(0)); + } + + return getRoot(); + } + + /** + * Inserts the record at the given index if there is sufficient space in + * the buffer. + * @param index insertion index + * @param record record to be inserted + * @return true if the record was successfully inserted. + * @throws IOException thrown if IO error occurs + */ + abstract boolean insertRecord(int index, Record record) throws IOException; + + /** + * Updates the record at the given index. + * @param index record index + * @param record new record + * @return root node which may have changed. + * @throws IOException thrown if IO error occurs + */ + abstract FixedKeyNode updateRecord(int index, Record record) throws IOException; + + @Override + public db.Record getRecordBefore(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) { + index = -index - 2; + } + else { + --index; + } + if (index < 0) { + FixedKeyRecordNode nextLeaf = getPreviousLeaf(); + return nextLeaf != null ? nextLeaf.getRecord(schema, nextLeaf.keyCount - 1) : null; + } + return getRecord(schema, index); + } + + @Override + public db.Record getRecordAfter(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) { + index = -(index + 1); + } + else { + ++index; + } + if (index == keyCount) { + FixedKeyRecordNode nextLeaf = getNextLeaf(); + return nextLeaf != null ? nextLeaf.getRecord(schema, 0) : null; + } + return getRecord(schema, index); + } + + @Override + public Record getRecordAtOrBefore(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) { + index = -index - 2; + } + if (index < 0) { + FixedKeyRecordNode nextLeaf = getPreviousLeaf(); + return nextLeaf != null ? nextLeaf.getRecord(schema, nextLeaf.keyCount - 1) : null; + } + return getRecord(schema, index); + } + + @Override + public Record getRecordAtOrAfter(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) { + index = -(index + 1); + } + if (index == keyCount) { + FixedKeyRecordNode nextLeaf = getNextLeaf(); + return nextLeaf != null ? nextLeaf.getRecord(schema, 0) : null; + } + return getRecord(schema, index); + } + + /** + * Create a new record node with no siblings attached. + * @param nodeMgr table node manager instance + * @return new record leaf node + * @throws IOException thrown if IO error occurs + */ + static FixedKeyRecordNode createRecordNode(NodeMgr nodeMgr) throws IOException { + Schema schema = nodeMgr.getTableSchema(); + FixedKeyRecordNode node = null; + if (schema.isVariableLength()) { + node = new FixedKeyVarRecNode(nodeMgr, -1, -1); + } + else { + node = new FixedKeyFixedRecNode(nodeMgr, -1, -1); + } + return node; + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedKeyVarRecNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedKeyVarRecNode.java new file mode 100644 index 0000000000..a5c82125df --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/FixedKeyVarRecNode.java @@ -0,0 +1,454 @@ +/* ### + * 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 db; + +import java.io.IOException; + +import db.buffers.DataBuffer; +import ghidra.util.datastruct.IntArrayList; +import ghidra.util.exception.AssertException; + +/** + * FixedKeyVarRecNode is an implementation of a BTree leaf node + * which utilizes fixed-length key values and stores variable-length records. + *

+ * This type of node has the following layout within a single DataBuffer + * (field size in bytes, where 'L' is the fixed length of the fixed-length + * key as specified by key type in associated Schema):: + *

+ *   | NodeType(1) | KeyCount(4) | PrevLeafId(4) | NextLeafId(4) | Key0(L) | RecOffset0(4) | IndFlag0(1) |...  
+ *     
+ *   | KeyN(L) | RecOffsetN(4) | IndFlagN(1) |...<FreeSpace>... | RecN |... | Rec1 |
+ * 
+ * IndFlag - if not zero the record has been stored within a chained DBBuffer + * whose 4-byte integer buffer ID has been stored within this leaf at the record offset. + */ +class FixedKeyVarRecNode extends FixedKeyRecordNode { + + private static final int HEADER_SIZE = RECORD_LEAF_HEADER_SIZE; + + private static final int OFFSET_SIZE = 4; + private static final int INDIRECT_OPTION_SIZE = 1; + + private static final int KEY_BASE_OFFSET = HEADER_SIZE; + + private final int entrySize; + private final int dataOffsetBaseOffset; + private final int indirectOptionBaseOffset; + + /** + * Construct an existing fixed-length key variable-length record leaf node. + * @param nodeMgr table node manager instance + * @param buf node buffer + * @throws IOException if IO error occurs + */ + FixedKeyVarRecNode(NodeMgr nodeMgr, DataBuffer buf) throws IOException { + super(nodeMgr, buf); + entrySize = keySize + OFFSET_SIZE + INDIRECT_OPTION_SIZE; + dataOffsetBaseOffset = KEY_BASE_OFFSET + keySize; + indirectOptionBaseOffset = dataOffsetBaseOffset + OFFSET_SIZE; + } + + /** + * Construct a new fixed-length key variable-length record leaf node. + * @param nodeMgr table node manager instance + * @param prevLeafId node buffer id for previous leaf ( < 0: no leaf) + * @param nextLeafId node buffer id for next leaf ( < 0 : no leaf) + * @throws IOException if IO error occurs + */ + FixedKeyVarRecNode(NodeMgr nodeMgr, int prevLeafId, int nextLeafId) throws IOException { + super(nodeMgr, NodeMgr.FIXEDKEY_VAR_REC_NODE, prevLeafId, nextLeafId); + entrySize = keySize + OFFSET_SIZE + INDIRECT_OPTION_SIZE; + dataOffsetBaseOffset = KEY_BASE_OFFSET + keySize; + indirectOptionBaseOffset = dataOffsetBaseOffset + OFFSET_SIZE; + } + + @Override + FixedKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { + return new FixedKeyVarRecNode(nodeMgr, prevLeafId, nextLeafId); + } + + @Override + public int getKeyOffset(int index) { + return KEY_BASE_OFFSET + (index * entrySize); + } + + /** + * Get the record offset within the buffer + * @param index key index + * @return record offset + */ + public int getRecordDataOffset(int index) { + return buffer.getInt(dataOffsetBaseOffset + (index * entrySize)); + } + + /** + * Store the record offset within the buffer for the specified key index + * @param index key index + * @param offset record offset + */ + private void putRecordDataOffset(int index, int offset) { + buffer.putInt(dataOffsetBaseOffset + (index * entrySize), offset); + } + + /** + * Determine if a record is utilizing a chained DBBuffer for data storage + * @param index key index + * @return true if indirect storage is used for record, else false + */ + private boolean hasIndirectStorage(int index) { + return buffer.getByte(indirectOptionBaseOffset + (index * entrySize)) != 0; + } + + /** + * Set the indirect storage flag associated with a record + * @param index key index + * @param state indirect storage used (true) or not used (false) + */ + private void enableIndirectStorage(int index, boolean state) { + buffer.putByte(indirectOptionBaseOffset + (index * entrySize), state ? (byte) 1 : (byte) 0); + } + + /** + * @return unused free space within node + */ + private int getFreeSpace() { + return (keyCount == 0 ? buffer.length() : getRecordDataOffset(keyCount - 1)) - + (keyCount * entrySize) - RECORD_LEAF_HEADER_SIZE; + } + + /** + * Get the length of a stored record. + * @param index index associated with record. + */ + private int getRecordLength(int index) { + if (index == 0) { + return buffer.length() - getRecordDataOffset(0); + } + return getRecordDataOffset(index - 1) - getRecordDataOffset(index); + } + + /** + * Get the length of a stored record. Optimized if record offset + * already known. + * @param index index associated with record. + * @param offset record offset + */ + private int getRecordLength(int index, int offset) { + if (index == 0) { + return buffer.length() - offset; + } + return getRecordDataOffset(index - 1) - offset; + } + + /** + * Move all record data, starting with index, by the specified offset amount. + * If the node contains 5 records, an index of 3 would shift the record data + * for indexes 3 and 4 left by the spacified offset amount. This is used to + * make space for a new or updated record. + * @param index the smaller key/record index (0 <= index1) + * @param offset movement offset in bytes + * @return insertion offset immediately following moved block. + */ + private int moveRecords(int index, int offset) { + + int lastIndex = keyCount - 1; + + // No movement needed for appended record + if (index == keyCount) { + if (index == 0) { + return buffer.length() + offset; + } + return getRecordDataOffset(lastIndex) + offset; + } + + // Determine block to be moved + int start = getRecordDataOffset(lastIndex); + int end = (index == 0) ? buffer.length() : getRecordDataOffset(index - 1); + int len = end - start; + + // Move record data + buffer.move(start, start + offset, len); + + // Adjust stored offsets + for (int i = index; i < keyCount; i++) { + putRecordDataOffset(i, getRecordDataOffset(i) + offset); + } + return end + offset; + } + + @Override + public Record getRecord(Schema schema, int index) throws IOException { + Field key = getKeyField(index); + Record record = schema.createRecord(key); + if (hasIndirectStorage(index)) { + int bufId = buffer.getInt(getRecordDataOffset(index)); + ChainedBuffer chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), bufId); + record.read(chainedBuffer, 0); + } + else { + record.read(buffer, getRecordDataOffset(index)); + } + return record; + } + + @Override + public int getRecordOffset(int index) throws IOException { + if (hasIndirectStorage(index)) { + return -buffer.getInt(getRecordDataOffset(index)); + } + return getRecordDataOffset(index); + } + + @Override + public Record getRecord(Field key, Schema schema) throws IOException { + int index = getKeyIndex(key); + if (index < 0) + return null; + return getRecord(schema, index); + } + + /** + * Find the index which represents the halfway point within the record data. + * @returns key index. + */ + private int getSplitIndex() { + + int halfway = ((keyCount == 0 ? buffer.length() : getRecordDataOffset(keyCount - 1)) + + buffer.length()) / 2; + int min = 1; + int max = keyCount - 1; + + while (min < max) { + int i = (min + max) / 2; + int offset = getRecordDataOffset(i); + if (offset == halfway) { + return i; + } + else if (offset < halfway) { + max = i - 1; + } + else { + min = i + 1; + } + } + return min; + } + + @Override + void splitData(FixedKeyRecordNode newRightLeaf) { + + FixedKeyVarRecNode rightNode = (FixedKeyVarRecNode) newRightLeaf; + + int splitIndex = getSplitIndex(); + int count = keyCount - splitIndex; + int start = getRecordDataOffset(keyCount - 1); // start of block to be moved + int end = getRecordDataOffset(splitIndex - 1); // end of block to be moved + int splitLen = end - start; // length of block to be moved + int rightOffset = buffer.length() - splitLen; // data offset within new leaf node + + // Copy data to new leaf node + DataBuffer newBuf = rightNode.buffer; + newBuf.copy(rightOffset, buffer, start, splitLen); + newBuf.copy(KEY_BASE_OFFSET, buffer, KEY_BASE_OFFSET + (splitIndex * entrySize), + count * entrySize); + + // Fix record offsets in new leaf node + int offsetCorrection = buffer.length() - end; + for (int i = 0; i < count; i++) { + rightNode.putRecordDataOffset(i, rightNode.getRecordDataOffset(i) + offsetCorrection); + } + + // Adjust key counts + setKeyCount(keyCount - count); + rightNode.setKeyCount(count); + } + + @Override + FixedKeyNode updateRecord(int index, Record record) throws IOException { + + int offset = getRecordDataOffset(index); + int oldLen = getRecordLength(index, offset); + int len = record.length(); + + // Check for use of indirect chained record node(s) + int maxRecordLength = ((buffer.length() - HEADER_SIZE) >> 2) - entrySize; // min 4 records per node + boolean wasIndirect = hasIndirectStorage(index); + boolean useIndirect = (len > maxRecordLength); + + if (useIndirect) { + // Store record in chained buffers + len = 4; + ChainedBuffer chainedBuffer = null; + if (wasIndirect) { + chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), buffer.getInt(offset)); + chainedBuffer.setSize(record.length(), false); + } + else { + chainedBuffer = new ChainedBuffer(record.length(), nodeMgr.getBufferMgr()); + buffer.putInt(offset + oldLen - 4, chainedBuffer.getId()); // assumes old len is always > 4 + enableIndirectStorage(index, true); + } + record.write(chainedBuffer, 0); + } + else if (wasIndirect) { + removeChainedBuffer(buffer.getInt(offset)); + enableIndirectStorage(index, false); + } + + // See if updated record will fit in current buffer + if (useIndirect || len <= (getFreeSpace() + oldLen)) { + + // Overwrite record data - move other data if needed + int dataShift = oldLen - len; + if (dataShift != 0) { + offset = moveRecords(index + 1, dataShift); + putRecordDataOffset(index, offset); + } + if (!useIndirect) { + record.write(buffer, offset); + } + return getRoot(); + } + + // Insufficient room for updated record - remove and re-add + Field key = record.getKeyField(); + FixedKeyRecordNode leaf = (FixedKeyRecordNode) deleteRecord(key, null).getLeafNode(key); + return leaf.putRecord(record, null); + } + + /** + * Insert the specified record at the specified key index. + * Existing data may be shifted within the buffer to make room for + * the new record. Parent must be notified if this changes the leftmost + * key. + * @param index insertion index for stored key + * @param record record to be inserted + * @throws IOException thrown if an IO error occurs + */ + @Override + boolean insertRecord(int index, Record record) throws IOException { + + // Check for use of indirect chained record node(s) + int len = record.length(); + int maxRecordLength = ((buffer.length() - HEADER_SIZE) >> 2) - entrySize; // min 4 records per node + boolean useIndirect = (len > maxRecordLength); + if (useIndirect) { + len = 4; + } + + if ((len + entrySize) > getFreeSpace()) + return false; // insufficient space for record storage + + // Make room for new record + int offset = moveRecords(index, -len); + + // Make room for new key/offset entry + int start = KEY_BASE_OFFSET + (index * entrySize); + len = (keyCount - index) * entrySize; + buffer.move(start, start + entrySize, len); + + // Store new record key/offset + buffer.put(start, record.getKeyField().getBinaryData()); + buffer.putInt(start + keySize, offset); + setKeyCount(keyCount + 1); + + // Store record data + if (useIndirect) { + ChainedBuffer chainedBuffer = + new ChainedBuffer(record.length(), nodeMgr.getBufferMgr()); + buffer.putInt(offset, chainedBuffer.getId()); + record.write(chainedBuffer, 0); + } + else { + record.write(buffer, offset); + } + enableIndirectStorage(index, useIndirect); + + return true; + } + + @Override + public void remove(int index) throws IOException { + + if (index < 0 || index >= keyCount) + throw new AssertException(); + + if (hasIndirectStorage(index)) { + removeChainedBuffer(buffer.getInt(getRecordDataOffset(index))); + enableIndirectStorage(index, false); + } + + int len = getRecordLength(index); + moveRecords(index + 1, len); + + int start = KEY_BASE_OFFSET + ((index + 1) * entrySize); + len = (keyCount - index - 1) * entrySize; + buffer.move(start, start - entrySize, len); + setKeyCount(keyCount - 1); + } + + @Override + public FixedKeyNode removeLeaf() throws IOException { + + // Remove all chained buffers associated with this leaf + for (int index = 0; index < keyCount; ++index) { + if (hasIndirectStorage(index)) { + removeChainedBuffer(buffer.getInt(getRecordDataOffset(index))); + } + } + return super.removeLeaf(); + } + + /** + * Remove a chained buffer. + * @param bufferId chained buffer ID + */ + private void removeChainedBuffer(int bufferId) throws IOException { + ChainedBuffer chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), bufferId); + chainedBuffer.delete(); + } + + @Override + public void delete() throws IOException { + + // Remove all chained buffers associated with this node. + for (int index = 0; index < keyCount; index++) { + if (hasIndirectStorage(index)) { + int offset = getRecordDataOffset(index); + int bufferId = buffer.getInt(offset); + removeChainedBuffer(bufferId); + buffer.putInt(offset, -1); + } + } + + // Remove this node + nodeMgr.deleteNode(this); + } + + @Override + public int[] getBufferReferences() { + IntArrayList idList = new IntArrayList(); + for (int i = 0; i < keyCount; i++) { + if (hasIndirectStorage(i)) { + int offset = getRecordDataOffset(i); + idList.add(buffer.getInt(offset)); + } + } + return idList.toArray(); + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/FixedRecNode.java b/Ghidra/Framework/DB/src/main/java/db/FixedRecNode.java index ef8015c0ab..9b47e429ad 100644 --- a/Ghidra/Framework/DB/src/main/java/db/FixedRecNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/FixedRecNode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; /** * FixedRecNode is an implementation of a BTree leaf node @@ -37,75 +35,68 @@ import db.buffers.DataBuffer; class FixedRecNode extends LongKeyRecordNode { private static final int HEADER_SIZE = RECORD_LEAF_HEADER_SIZE; - + private static final int ENTRY_BASE_OFFSET = HEADER_SIZE; - + private static final int KEY_SIZE = 8; - + private static final int[] EMPTY_ID_LIST = new int[0]; private int entrySize; private int recordLength; - + /** * Construct an existing long-key fixed-length record leaf node. * @param nodeMgr table node manager instance * @param buf node buffer + * @param recordLength fixed record length */ FixedRecNode(NodeMgr nodeMgr, DataBuffer buf, int recordLength) { super(nodeMgr, buf); this.recordLength = recordLength; entrySize = KEY_SIZE + recordLength; } - + /** * Construct a new long-key fixed-length record leaf node. * @param nodeMgr table node manager instance * @param recordLength fixed record length * @param prevLeafId node buffer id for previous leaf ( < 0: no leaf) * @param nextLeafId node buffer id for next leaf ( < 0 : no leaf) - * @throws IOException + * @throws IOException thrown if IO error occurs */ - FixedRecNode(NodeMgr nodeMgr, int recordLength, int prevLeafId, int nextLeafId) throws IOException { + FixedRecNode(NodeMgr nodeMgr, int recordLength, int prevLeafId, int nextLeafId) + throws IOException { super(nodeMgr, NodeMgr.LONGKEY_FIXED_REC_NODE, prevLeafId, nextLeafId); this.recordLength = recordLength; entrySize = KEY_SIZE + recordLength; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#createNewLeaf(int, int) - */ @Override - LongKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { + LongKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { return new FixedRecNode(nodeMgr, recordLength, prevLeafId, nextLeafId); } - - /* - * @see ghidra.framework.store.db.LongKeyNode#getKey(int) - */ + @Override - long getKey(int index) { - return buffer.getLong(ENTRY_BASE_OFFSET + (index * entrySize)); + long getKey(int index) { + return buffer.getLong(getKeyOffset(index)); } - -// /** -// * Store a key at the specified index -// * @param index key index -// * @param key key value -// */ -// private void putKey(int index, long key) { -// buffer.putLong(ENTRY_BASE_OFFSET + (index * entrySize), key); -// } - + + @Override + public int getKeyOffset(int index) { + return ENTRY_BASE_OFFSET + (index * entrySize); + } + /** * Get the record offset within the buffer * @param index key index * @return record offset */ - private int getRecordOffset(int index) { + @Override + public int getRecordOffset(int index) { return ENTRY_BASE_OFFSET + (index * entrySize); } - + /** * Shift all records by one starting with index to the end. * @param index the smaller key index (0 <= index1) @@ -113,49 +104,41 @@ class FixedRecNode extends LongKeyRecordNode { * one record. */ private void shiftRecords(int index, boolean rightShift) { - + // No movement needed for appended record if (index == keyCount) return; - + // Determine block to be moved int start = getRecordOffset(index); int end = getRecordOffset(keyCount); int len = end - start; - + // Move record data int offset = start + (rightShift ? entrySize : -entrySize); buffer.move(start, offset, len); } - - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#remove(int) - */ - @Override - void remove(int index) { -if (index < 0 || index >= keyCount) -throw new AssertException(); + @Override + public void remove(int index) { + + if (index < 0 || index >= keyCount) + throw new AssertException(); shiftRecords(index + 1, false); - setKeyCount(keyCount - 1); + setKeyCount(keyCount - 1); } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#insertRecord(int, ghidra.framework.store.db.Record) - */ @Override - boolean insertRecord(int index, Record record) throws IOException { - - // Check for use of indirect chained record node(s) -// int len = record.length(); + boolean insertRecord(int index, Record record) throws IOException { - if (keyCount == ((buffer.length() - HEADER_SIZE) / entrySize)) + if (keyCount == ((buffer.length() - HEADER_SIZE) / entrySize)) { return false; // insufficient space for record storage + } // Make room for new record shiftRecords(index, true); - + // Store new record int offset = getRecordOffset(index); buffer.putLong(offset, record.getKey()); @@ -165,21 +148,15 @@ throw new AssertException(); return true; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#updateRecord(int, ghidra.framework.store.db.Record) - */ @Override - LongKeyNode updateRecord(int index, Record record) throws IOException { + LongKeyNode updateRecord(int index, Record record) throws IOException { int offset = getRecordOffset(index) + KEY_SIZE; record.write(buffer, offset); return getRoot(); } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#getRecord(long, ghidra.framework.store.db.Schema) - */ @Override - Record getRecord(long key, Schema schema) throws IOException { + Record getRecord(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) return null; @@ -188,50 +165,39 @@ throw new AssertException(); return record; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#getRecord(ghidra.framework.store.db.Schema, int) - */ @Override - Record getRecord(Schema schema, int index) throws IOException { + public Record getRecord(Schema schema, int index) throws IOException { long key = getKey(index); Record record = schema.createRecord(key); record.read(buffer, getRecordOffset(index) + KEY_SIZE); - return record; + return record; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#splitData(ghidra.framework.store.db.LongKeyRecordNode) - */ @Override - void splitData(LongKeyRecordNode newRightLeaf) { - + void splitData(LongKeyRecordNode newRightLeaf) { + FixedRecNode rightNode = (FixedRecNode) newRightLeaf; - + int splitIndex = keyCount / 2; int count = keyCount - splitIndex; int start = getRecordOffset(splitIndex); // start of block to be moved int end = getRecordOffset(keyCount); // end of block to be moved int splitLen = end - start; // length of block to be moved - + // Copy data to new leaf node - rightNode.buffer.copy(ENTRY_BASE_OFFSET, buffer, start, splitLen); - + rightNode.buffer.copy(ENTRY_BASE_OFFSET, buffer, start, splitLen); + // Adjust key counts setKeyCount(keyCount - count); rightNode.setKeyCount(count); } - /* - * @see ghidra.framework.store.db.LongKeyNode#delete() - */ @Override - public void delete() throws IOException { + public void delete() throws IOException { nodeMgr.deleteNode(this); } - /* - * @see ghidra.framework.store.db.BTreeNode#getBufferReferences() - */ + @Override public int[] getBufferReferences() { return EMPTY_ID_LIST; } diff --git a/Ghidra/Framework/DB/src/main/java/db/IllegalFieldAccessException.java b/Ghidra/Framework/DB/src/main/java/db/IllegalFieldAccessException.java index d22f0504de..e606016e25 100644 --- a/Ghidra/Framework/DB/src/main/java/db/IllegalFieldAccessException.java +++ b/Ghidra/Framework/DB/src/main/java/db/IllegalFieldAccessException.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,4 +26,13 @@ public class IllegalFieldAccessException extends RuntimeException { IllegalFieldAccessException() { super("Illegal field access"); } + + /** + * Construct an illegal field access exception + * with a specific message + */ + IllegalFieldAccessException(String msg) { + super(msg); + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/IndexBuffer.java b/Ghidra/Framework/DB/src/main/java/db/IndexBuffer.java deleted file mode 100644 index e221617ade..0000000000 --- a/Ghidra/Framework/DB/src/main/java/db/IndexBuffer.java +++ /dev/null @@ -1,237 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 db; - - -import java.io.IOException; - -import db.buffers.DataBuffer; - -/** - * IndexBuffer stores index data for a common index key - * within a data buffer. The index data has the following layout (field size in - * bytes): - *
- *   | FieldType(1) | KeyCount(4) | PrimeKey1(8) | ... | PrimeKeyN(8) |
- * 
- * This type of index buffer is used to store primary keys associated with a - * single secondary key. The association to a specific secondary key - * is handled by the IndexTable. The primary keys are maintained - * within the buffer in an asscending sorted order. - */ -class IndexBuffer { - - private static final int FIELD_TYPE_SIZE = 1; - private static final int KEY_COUNT_SIZE = 4; - - private static final int FIELD_TYPE_OFFSET = 0; - private static final int KEY_COUNT_OFFSET = FIELD_TYPE_OFFSET + FIELD_TYPE_SIZE; - - static final int INDEX_HEADER_SIZE = FIELD_TYPE_SIZE + KEY_COUNT_SIZE; - - static final int PRIMARY_KEY_SIZE = 8; - - Field indexKey; - int keyCount; - IndexDataBuffer dataBuffer; - - /** - * Construct a new index buffer. - * @param indexKey associated index key - * @param data existing index buffer data from storage or null for an - * empty index buffer. - * @throws IOException thrown if IO error occurs - */ - IndexBuffer(Field indexKey, byte[] data) throws IOException { - this.indexKey = indexKey; - if (data == null) { - dataBuffer = new IndexDataBuffer(INDEX_HEADER_SIZE); - dataBuffer.putByte(FIELD_TYPE_OFFSET, indexKey.getFieldType()); - dataBuffer.putInt(KEY_COUNT_OFFSET, 0); - } - else { - if (data[FIELD_TYPE_OFFSET] != indexKey.getFieldType()) - throw new IOException("Invalid index data"); - dataBuffer = new IndexDataBuffer(data); - } - keyCount = dataBuffer.getInt(KEY_COUNT_OFFSET); - } - - /** - * Get the associated index key - * @return index key - */ - Field getIndexKey() { - return indexKey; - } - - /** - * Set the stored primary key count - * @param cnt primary key count - */ - private void setKeyCount(int cnt) { - keyCount = cnt; - dataBuffer.putInt(KEY_COUNT_OFFSET, keyCount); - } - - /** - * Provides data buffer manipulation for the index data - */ - class IndexDataBuffer extends DataBuffer { - - /** - * Construct an index data buffer. - * @see db.buffers.DataBuffer#DataBuffer(byte[]) - */ - IndexDataBuffer(byte[] data) { - super(data); - } - - /** - * Construct an index data buffer. - * @see db.buffers.DataBuffer#DataBuffer(int) - */ - IndexDataBuffer(int size) { - super(size); - } - - /** - * Get the storage array associated with this buffer. - * @return byte storage array. - */ - @Override - protected byte[] getData() { - return data; - } - - /** - * Get the storage array associated with this buffer. - * @return byte storage array. - */ - @Override - protected void setData(byte[] data) { - this.data = data; - } - } - - /** - * Get the index buffer data. - * @return index data or null if index data is empty. - */ - byte[] getData() { - byte[] data = dataBuffer.getData(); - if (data.length <= INDEX_HEADER_SIZE) - return null; - return data; - } - - /** - * Get the primary key associated with the specified entry index. - * This method does not perform any bounds checking on the index value. - * @param index index entry index. - * @return primary key associated with entry. - */ - long getPrimaryKey(int index) { - return dataBuffer.getLong(INDEX_HEADER_SIZE + (index * PRIMARY_KEY_SIZE)); - } - - /** - * Get the secondary key index within the buffer. - * @param primaryKey primary key - * @return key index if found, else -(key index + 1) indicates insertion - * point. - */ - int getIndex(long primaryKey) { - return getKeyIndex(primaryKey); - } - - /** - * Perform a binary search to locate the specified primary key. - * @param primaryKey primary key - * @return key index if found, else -(key index + 1) indicates insertion - * point. - */ - private int getKeyIndex(long primaryKey) { - - int min = 0; - int max = keyCount - 1; - - while (min <= max) { - int i = (min + max)/2; - long k = getPrimaryKey(i); - if (k == primaryKey) { - return i; - } - else if (k < primaryKey) { - min = i + 1; - } - else { - max = i - 1; - } - } - return -(min+1); - } - - /** - * Add a new primary key to this index buffer. - * @param primaryKey primary key - */ - void addEntry(long primaryKey) { - int index = getKeyIndex(primaryKey); - if (index < 0) { - index = -index-1; - IndexDataBuffer newDataBuffer = new IndexDataBuffer(dataBuffer.length() + PRIMARY_KEY_SIZE); - int len = INDEX_HEADER_SIZE + (index * PRIMARY_KEY_SIZE); - newDataBuffer.copy(0, dataBuffer, 0, len); - newDataBuffer.copy(len + PRIMARY_KEY_SIZE, dataBuffer, len, dataBuffer.length() - len); - newDataBuffer.putLong(len, primaryKey); - dataBuffer = newDataBuffer; - setKeyCount(keyCount + 1); - } - } - - /** - * Delete the specified index entry from this index buffer. - * @param primaryKey primary key - */ - void deleteEntry(long primaryKey) { - int index = getKeyIndex(primaryKey); - if (index >= 0) { - IndexDataBuffer newDataBuffer = new IndexDataBuffer(dataBuffer.length() - PRIMARY_KEY_SIZE); - int len = INDEX_HEADER_SIZE + (index * PRIMARY_KEY_SIZE); - newDataBuffer.copy(0, dataBuffer, 0, len); - newDataBuffer.copy(len, dataBuffer, len + PRIMARY_KEY_SIZE, dataBuffer.length() - len - PRIMARY_KEY_SIZE); - dataBuffer = newDataBuffer; - setKeyCount(keyCount - 1); - } - } - - /** - * Get the list of primary keys contained within this index buffer. - * @return long[] list of primary keys - * @throws IOException thrown if IO error occurs - */ - long[] getPrimaryKeys() { - long[] keys = new long[keyCount]; - for (int i = 0; i < keyCount; i++) { - keys[i] = getPrimaryKey(i); - } - return keys; - } - - -} diff --git a/Ghidra/Framework/DB/src/main/java/db/IndexField.java b/Ghidra/Framework/DB/src/main/java/db/IndexField.java index 5c3d7597cd..ed8eed711f 100644 --- a/Ghidra/Framework/DB/src/main/java/db/IndexField.java +++ b/Ghidra/Framework/DB/src/main/java/db/IndexField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,486 +15,276 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; -abstract class IndexField extends Field { +import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; - private static final int MAX_INDEX_FIELD_LENGTH = 64; +/** + * IndexField provides a index table primary key {@link Field} + * implementation which wraps both the index field value (fixed or varaible length) + * and its' corresponding primary key (fixed or variable length). + */ +class IndexField extends Field { - private long primaryKey; - private Field nonTruncatedIndexField; - private Field indexField; + static final int MAX_INDEX_FIELD_LENGTH = 64; + + private Field primaryKey; + private Field nonTruncatedIndexedField; + private Field indexedField; private boolean isTruncated = false; /** - * Construct a new index field with an initial value of null. + * Construct an index field with an initial value. + * @param indexedField indexed field value + * @param primaryKey primary key value */ - IndexField(Field newIndexField) { - indexField = newIndexField; - nonTruncatedIndexField = newIndexField; + IndexField(Field indexedField, Field primaryKey) { + if (primaryKey.isVariableLength()) { + throw new IllegalArgumentException("variable length primaryKey not supported"); + } + this.primaryKey = primaryKey.copyField(); + this.nonTruncatedIndexedField = indexedField; + this.indexedField = indexedField; + if (indexedField.isVariableLength() && indexedField.length() >= MAX_INDEX_FIELD_LENGTH) { + // Ensure that we do not exceed the maximum allowed index key length + // and conserves space when indexing very long values + this.indexedField = indexedField.copyField(); + this.indexedField.truncate(MAX_INDEX_FIELD_LENGTH); + isTruncated = true; + } } /** - * Construct an index field with an initial value. + * Get the indexed field value. If the original value exceeded + * {@link #MAX_INDEX_FIELD_LENGTH} in length the returned value will + * be truncated. + * @return indexed field value */ - IndexField(Field value, long primaryKey) { - this.nonTruncatedIndexField = value; - indexField = value.newField(value); - if (indexField.isVariableLength() && indexField.length() >= MAX_INDEX_FIELD_LENGTH) { - // Ensure that we do not exceed the maximum allowed index key length - // and conserves space when indexing very long values - indexField.truncate(MAX_INDEX_FIELD_LENGTH); - isTruncated = true; - } - this.primaryKey = primaryKey; - } - - Field getIndexField() { - return indexField; + Field getIndexedField() { + return indexedField; } + /** + * Get the non-truncated index field value. + * @return non-truncated index field value. + * @deprecated this method serves no real purpose since the non-truncated + * indexed field value is not retained within the index table. + */ + @Deprecated Field getNonTruncatedIndexField() { - return nonTruncatedIndexField; + return nonTruncatedIndexedField; } + /** + * Determine if the index field value has been truncated from its' original + * value. + * @return true if truncated else false + * @deprecated this method serves no real purpose since the truncation + * status is not retained within the index table. + */ + @Deprecated boolean usesTruncatedFieldValue() { return isTruncated; } - long getPrimaryKey() { + Field getPrimaryKey() { return primaryKey; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { - return indexField.length() + 8; + return indexedField.length() + primaryKey.length(); } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { - offset = indexField.write(buf, offset); - return buf.putLong(offset, primaryKey); + offset = indexedField.write(buf, offset); + return primaryKey.write(buf, offset); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { - offset = indexField.read(buf, offset); - primaryKey = buf.getLong(offset); - return offset + 8; + offset = indexedField.read(buf, offset); + return primaryKey.read(buf, offset); } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { - return indexField.readLength(buf, offset) + 8; + return indexedField.readLength(buf, offset) + primaryKey.length(); } - /* - * @see ghidra.framework.store.db.Field#isVariableLength() - */ @Override public boolean isVariableLength() { - return true; + return indexedField.isVariableLength(); } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected abstract byte getFieldType(); + public IndexField copyField() { + return new IndexField(indexedField.copyField(), primaryKey.copyField()); + } - abstract String getFieldTypeString(); + @Override + public IndexField newField() { + return new IndexField(indexedField.newField(), primaryKey.newField()); + } - /* - * @see java.lang.Object#toString() + /** + * Construct a new {@link IndexField} instance for the given indexValue and + * associated primary key. These fields are verified against this instance to + * ensure that they are of the correct type. + * @param indexValue column field value to be indexed + * @param key primary key associated with indexValue + * @return new IndexField instance */ + IndexField newIndexField(Field indexValue, Field key) { + if (!indexValue.isSameType(indexedField) || !primaryKey.isSameType(getPrimaryKey())) { + throw new IllegalArgumentException("incorrect index value or key type"); + + } + return new IndexField(indexValue, key); + } + + @Override + final IndexField getMinValue() { + throw new UnsupportedOperationException(); + } + + @Override + final IndexField getMaxValue() { + throw new UnsupportedOperationException(); + } + + @Override + byte getFieldType() { + return getIndexFieldType(indexedField, primaryKey); + } + @Override public String toString() { - return getFieldTypeString() + ": " + indexField; + return indexedField + "/" + primaryKey; } @Override public String getValueAsString() { - return indexField.getValueAsString() + " + " + Long.toHexString(primaryKey); + return indexedField.getValueAsString() + " / " + primaryKey.getValueAsString(); } - boolean hasSameIndex(IndexField field) { + boolean hasSameIndexValue(IndexField field) { if (field == null) { return false; } - if (indexField == null) { - return field.indexField == null; + if (indexedField == null) { + return field.indexedField == null; } - return indexField.equals(field.indexField); + return indexedField.equals(field.indexedField); } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { - byte[] indexBytes = indexField.getBinaryData(); - int len = indexBytes.length; - byte[] bytes = new byte[len + 8]; - System.arraycopy(indexBytes, 0, bytes, 0, len); - - bytes[len] = (byte) (primaryKey >> 56); - bytes[++len] = (byte) (primaryKey >> 48); - bytes[++len] = (byte) (primaryKey >> 40); - bytes[++len] = (byte) (primaryKey >> 32); - bytes[++len] = (byte) (primaryKey >> 24); - bytes[++len] = (byte) (primaryKey >> 16); - bytes[++len] = (byte) (primaryKey >> 8); - bytes[++len] = (byte) primaryKey; - + byte[] indexBytes = indexedField.getBinaryData(); + byte[] primaryKeyBytes = primaryKey.getBinaryData(); + int len = indexBytes.length + primaryKeyBytes.length; + byte[] bytes = new byte[len]; + System.arraycopy(indexBytes, 0, bytes, 0, indexBytes.length); + System.arraycopy(primaryKeyBytes, 0, bytes, indexBytes.length, primaryKeyBytes.length); return bytes; } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ + @Override + public void setBinaryData(byte[] bytes) { + if (isVariableLength()) { + throw new IllegalFieldAccessException("Unsupported for variable length IndexField"); + } + if (bytes.length != length()) { + throw new IllegalFieldAccessException(); + } + BinaryDataBuffer buffer = new BinaryDataBuffer(bytes); + try { + read(buffer, 0); + } + catch (IOException e) { + throw new IllegalFieldAccessException(); + } + } + @Override public int compareTo(Field o) { IndexField f = (IndexField) o; - int result = indexField.compareTo(f.indexField); + int result = indexedField.compareTo(f.indexedField); if (result != 0) { return result; } - if (primaryKey == f.primaryKey) { - return 0; - } - else if (primaryKey < f.primaryKey) { - return -1; - } - return 1; + return primaryKey.compareTo(f.primaryKey); + } + + @Override + int compareTo(DataBuffer buffer, int offset) { + int result = indexedField.compareTo(buffer, offset); + if (result != 0) { + return result; + } + try { + int indexedFieldLen = indexedField.readLength(buffer, offset); + return primaryKey.compareTo(buffer, offset + indexedFieldLen); + } + catch (IOException e) { + throw new AssertException(e); // DataBuffer does not throw IOException + } + } + + @Override + public boolean isSameType(Field field) { + if (!(field instanceof IndexField)) { + return false; + } + IndexField otherField = (IndexField) field; + return indexedField.isSameType(otherField.indexedField) && + primaryKey.isSameType(otherField.primaryKey); } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { - if (!getClass().isInstance(obj)) + if (obj == null || obj.getClass() != getClass()) { return false; + } IndexField f = (IndexField) obj; - return primaryKey == f.primaryKey && indexField.equals(f.indexField); + return primaryKey.equals(f.primaryKey) && indexedField.equals(f.indexedField); } - /* - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { - return (int) primaryKey; + return (indexedField.hashCode() * 31) + primaryKey.hashCode(); + } + + static byte getIndexFieldType(Field indexedFieldType, Field primaryKeyFieldType) { + if (primaryKeyFieldType instanceof IndexField) { + throw new IllegalArgumentException(); + } + if (indexedFieldType instanceof IndexField) { + throw new IllegalArgumentException(); + } + return (byte) ((primaryKeyFieldType.getFieldType() << INDEX_FIELD_TYPE_SHIFT) | + indexedFieldType.getFieldType()); + } /** - * Get the field associated with the specified type value. - * @param fieldType - * @return Field + * Get the index field associated with the specified encoded field type. + * @param fieldType field type + * @return IndexField + * @throws UnsupportedFieldException if unsupported fieldType specified */ - static IndexField getIndexField(byte fieldType) { - switch (fieldType & BASE_TYPE_MASK) { - case LONG_TYPE: - return new LongIndexField(); - case INT_TYPE: - return new IntIndexField(); - case STRING_TYPE: - return new StringIndexField(); - case SHORT_TYPE: - return new ShortIndexField(); - case BYTE_TYPE: - return new ByteIndexField(); - case BOOLEAN_TYPE: - return new BooleanIndexField(); - case BINARY_OBJ_TYPE: - return new BinaryIndexField(); + static IndexField getIndexField(byte fieldType) throws UnsupportedFieldException { + Field indexedField = Field.getField((byte) (fieldType & FIELD_TYPE_MASK)); + + byte primaryKeyFeldType = (byte) (fieldType >> INDEX_FIELD_TYPE_SHIFT & FIELD_TYPE_MASK); + if (primaryKeyFeldType == LEGACY_INDEX_LONG_TYPE) { + return new LegacyIndexField(indexedField); } - throw new AssertException(); + + Field primaryKeyType = Field.getField(primaryKeyFeldType); + return new IndexField(indexedField, primaryKeyType); } - static IndexField getIndexField(Field indexedField, long primaryKey) { - switch (indexedField.getFieldType()) { - case LONG_TYPE: - return new LongIndexField((LongField) indexedField, primaryKey); - case INT_TYPE: - return new IntIndexField((IntField) indexedField, primaryKey); - case STRING_TYPE: - return new StringIndexField((StringField) indexedField, primaryKey); - case SHORT_TYPE: - return new ShortIndexField((ShortField) indexedField, primaryKey); - case BYTE_TYPE: - return new ByteIndexField((ByteField) indexedField, primaryKey); - case BOOLEAN_TYPE: - return new BooleanIndexField((BooleanField) indexedField, primaryKey); - case BINARY_OBJ_TYPE: - return new BinaryIndexField((BinaryField) indexedField, primaryKey); - } - throw new AssertException(); - } - - private static class LongIndexField extends IndexField { - - LongIndexField() { - super(new LongField()); - } - - LongIndexField(LongField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | LONG_TYPE; - } - - @Override - String getFieldTypeString() { - return "LongIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof LongIndexField)) { - throw new AssertException(); - } - LongIndexField f = (LongIndexField) fieldValue; - return new LongIndexField((LongField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new LongIndexField(); - } - - } - - private static class IntIndexField extends IndexField { - - IntIndexField() { - super(new IntField()); - } - - IntIndexField(IntField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | INT_TYPE; - } - - @Override - String getFieldTypeString() { - return "IntIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof IntIndexField)) { - throw new AssertException(); - } - IntIndexField f = (IntIndexField) fieldValue; - return new IntIndexField((IntField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new IntIndexField(); - } - - } - - private static class StringIndexField extends IndexField { - - StringIndexField() { - super(new StringField()); - } - - StringIndexField(StringField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | STRING_TYPE; - } - - @Override - String getFieldTypeString() { - return "StringIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof StringIndexField)) { - throw new AssertException(); - } - StringIndexField f = (StringIndexField) fieldValue; - return new StringIndexField((StringField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new StringIndexField(); - } - - } - - private static class ShortIndexField extends IndexField { - - ShortIndexField() { - super(new ShortField()); - } - - ShortIndexField(ShortField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | SHORT_TYPE; - } - - @Override - String getFieldTypeString() { - return "ShortIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof ShortIndexField)) { - throw new AssertException(); - } - ShortIndexField f = (ShortIndexField) fieldValue; - return new ShortIndexField((ShortField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new ShortIndexField(); - } - - } - - private static class ByteIndexField extends IndexField { - - ByteIndexField() { - super(new ByteField()); - } - - ByteIndexField(ByteField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | BYTE_TYPE; - } - - @Override - String getFieldTypeString() { - return "ByteIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof ByteIndexField)) { - throw new AssertException(); - } - ByteIndexField f = (ByteIndexField) fieldValue; - return new ByteIndexField((ByteField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new ByteIndexField(); - } - - } - - private static class BooleanIndexField extends IndexField { - - BooleanIndexField() { - super(new BooleanField()); - } - - BooleanIndexField(BooleanField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | BOOLEAN_TYPE; - } - - @Override - String getFieldTypeString() { - return "BooleanIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof BooleanIndexField)) { - throw new AssertException(); - } - BooleanIndexField f = (BooleanIndexField) fieldValue; - return new BooleanIndexField((BooleanField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new BooleanIndexField(); - } - - } - - private static class BinaryIndexField extends IndexField { - - BinaryIndexField() { - super(new BinaryField()); - } - - BinaryIndexField(BinaryField indexedField, long primaryKey) { - super(indexedField, primaryKey); - } - - @Override - protected byte getFieldType() { - return INDEX_TYPE_FLAG | BINARY_OBJ_TYPE; - } - - @Override - String getFieldTypeString() { - return "BinaryIndexField"; - } - - @Override - public Field newField(Field fieldValue) { - if (!(fieldValue instanceof BinaryIndexField)) { - throw new AssertException(); - } - BinaryIndexField f = (BinaryIndexField) fieldValue; - return new BinaryIndexField((BinaryField) f.getIndexField(), f.getPrimaryKey()); - } - - @Override - public Field newField() { - return new BinaryIndexField(); - } - - } } diff --git a/Ghidra/Framework/DB/src/main/java/db/IndexTable.java b/Ghidra/Framework/DB/src/main/java/db/IndexTable.java index 49904dcf60..109f65917e 100644 --- a/Ghidra/Framework/DB/src/main/java/db/IndexTable.java +++ b/Ghidra/Framework/DB/src/main/java/db/IndexTable.java @@ -16,7 +16,6 @@ package db; import java.io.IOException; -import java.util.NoSuchElementException; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; @@ -29,7 +28,7 @@ import ghidra.util.task.TaskMonitor; */ abstract class IndexTable { - protected static final long[] emptyKeyArray = new long[0]; + protected static final Field[] emptyKeyArray = Field.EMPTY_ARRAY; /** * Database Handle @@ -51,11 +50,6 @@ abstract class IndexTable { */ protected Table indexTable; - /** - * Field type associated with indexed column. - */ - protected final Field fieldType; - /** * Indexed column within primary table schema. */ @@ -69,15 +63,14 @@ abstract class IndexTable { * @throws IOException thrown if IO error occurs */ IndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException { - if (!primaryTable.useLongKeys()) { - throw new AssertException("Only long-key tables may be indexed"); + if (!primaryTable.useLongKeys() && !primaryTable.useFixedKeys()) { + throw new AssertException("Only fixed-length key tables may be indexed"); } this.db = primaryTable.getDBHandle(); this.primaryTable = primaryTable; this.indexTableRecord = indexTableRecord; this.indexTable = new Table(primaryTable.getDBHandle(), indexTableRecord); this.colIndex = indexTableRecord.getIndexedColumn(); - fieldType = primaryTable.getSchema().getField(indexTableRecord.getIndexedColumn()); primaryTable.addIndex(this); } @@ -95,14 +88,12 @@ abstract class IndexTable { throw new AssertException("Table not found: " + name); } - if (indexTableRecord.getSchema().getKeyFieldType() instanceof IndexField) { + Field keyFieldType = indexTableRecord.getSchema().getKeyFieldType(); + if (keyFieldType instanceof IndexField) { return new FieldIndexTable(primaryTable, indexTableRecord); } - Field fieldType = primaryTable.getSchema().getField(indexTableRecord.getIndexedColumn()); - if (fieldType.isVariableLength()) { - return new VarIndexTable(primaryTable, indexTableRecord); - } - return new FixedIndexTable(primaryTable, indexTableRecord); + throw new AssertException( + "Unexpected index field type: " + keyFieldType.getClass().getName()); } /** @@ -121,14 +112,23 @@ abstract class IndexTable { /** * Check the consistency of this index table. + * @param monitor task monitor * @return true if consistency check passed, else false - * @throws IOException - * @throws CancelledException + * @throws IOException if IO error occurs + * @throws CancelledException if task cancelled */ boolean isConsistent(TaskMonitor monitor) throws IOException, CancelledException { return indexTable.isConsistent(primaryTable.getSchema().getFieldNames()[colIndex], monitor); } + /** + * Get the primary table key type + * @return primary table key type + */ + Field getPrimaryTableKeyType() { + return primaryTable.getSchema().getKeyFieldType(); + } + /** * Get the table number associated with the underlying index table. * @return table number @@ -160,6 +160,7 @@ abstract class IndexTable { * Determine if there is an occurance of the specified index key value. * @param field index key value * @return true if an index key value equal to field exists. + * @throws IOException if IO error occurs */ boolean hasRecord(Field field) throws IOException { return indexTable.hasRecord(field); @@ -168,16 +169,18 @@ abstract class IndexTable { /** * Find all primary keys which correspond to the specified indexed field * value. - * @param field the field value to search for. + * @param indexValue the field value to search for. * @return list of primary keys + * @throws IOException if IO error occurs */ - abstract long[] findPrimaryKeys(Field indexValue) throws IOException; + abstract Field[] findPrimaryKeys(Field indexValue) throws IOException; /** * Get the number of primary keys which correspond to the specified indexed field * value. - * @param field the field value to search for. + * @param indexValue the field value to search for. * @return key count + * @throws IOException if IO error occurs */ abstract int getKeyCount(Field indexValue) throws IOException; @@ -185,19 +188,20 @@ abstract class IndexTable { * Add an entry to this index. Caller is responsible for ensuring that this * is not a duplicate entry. * @param record new record - * @throws IOException + * @throws IOException if IO error occurs */ abstract void addEntry(Record record) throws IOException; /** * Delete an entry from this index. * @param record deleted record - * @throws IOException + * @throws IOException if IO error occurs */ abstract void deleteEntry(Record record) throws IOException; /** * Delete all records within this index table. + * @throws IOException if IO error occurs */ void deleteAll() throws IOException { indexTable.deleteAll(); @@ -218,7 +222,7 @@ abstract class IndexTable { * @param before if true initial position is before minField, else position * is after endField * @return index field iterator. - * @throws IOException + * @throws IOException if IO error occurs */ abstract DBFieldIterator indexIterator(Field minField, Field maxField, boolean before) throws IOException; @@ -233,7 +237,7 @@ abstract class IndexTable { * @param before if true initial position is before startField value, else position * is after startField value * @return index field iterator. - * @throws IOException + * @throws IOException if IO error occurs */ abstract DBFieldIterator indexIterator(Field minField, Field maxField, Field startField, boolean before) throws IOException; @@ -243,9 +247,7 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIterator() throws IOException { - return new PrimaryKeyIterator(); - } + abstract DBFieldIterator keyIterator() throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -255,9 +257,7 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIteratorBefore(Field startField) throws IOException { - return new PrimaryKeyIterator(startField, false); - } + abstract DBFieldIterator keyIteratorBefore(Field startField) throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -268,9 +268,7 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIteratorAfter(Field startField) throws IOException { - return new PrimaryKeyIterator(startField, true); - } + abstract DBFieldIterator keyIteratorAfter(Field startField) throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -282,9 +280,8 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIteratorBefore(Field startField, long primaryKey) throws IOException { - return new PrimaryKeyIterator(null, null, startField, primaryKey, false); - } + abstract DBFieldIterator keyIteratorBefore(Field startField, Field primaryKey) + throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -296,9 +293,8 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIteratorAfter(Field startField, long primaryKey) throws IOException { - return new PrimaryKeyIterator(null, null, startField, primaryKey, true); - } + abstract DBFieldIterator keyIteratorAfter(Field startField, Field primaryKey) + throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -314,17 +310,8 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIterator(Field minField, Field maxField, boolean before) throws IOException { - - Field startField = before ? minField : maxField; - - if (startField == null && !before) { - - } - - return new PrimaryKeyIterator(minField, maxField, before ? minField : maxField, - before ? Long.MIN_VALUE : Long.MAX_VALUE, !before); - } + abstract DBFieldIterator keyIterator(Field minField, Field maxField, boolean before) + throws IOException; /** * Iterate over all primary keys sorted based upon the associated index key. @@ -337,295 +324,7 @@ abstract class IndexTable { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - DBLongIterator keyIterator(Field minField, Field maxField, Field startField, boolean before) - throws IOException { - return new PrimaryKeyIterator(minField, maxField, startField, - before ? Long.MIN_VALUE : Long.MAX_VALUE, !before); - } - - /** - * Iterates over primary keys for a range of index field values. - */ - private class PrimaryKeyIterator implements DBLongIterator { - - private RecordIterator indexIterator; - private int expectedModCount; - - private int index; - private long indexPrimaryKey; - private IndexBuffer indexBuffer; - private boolean forward = true; - private boolean reverse = true; - private Field lastKey; - - private boolean hasPrev = false; - private boolean hasNext = false; - - /** - * Construct a key iterator starting with the minimum secondary key. - */ - PrimaryKeyIterator() throws IOException { - expectedModCount = indexTable.modCount; - indexIterator = indexTable.iterator(); - } - - /** - * Construct a key iterator. The iterator is positioned immediately before - * the key associated with the first occurance of the startValue. - * @param startValue indexed field value. - * @param after if true the iterator is positioned immediately after - * the last occurance of the specified startValue position. - */ - PrimaryKeyIterator(Field startValue, boolean after) throws IOException { - this(null, null, startValue, after ? Long.MAX_VALUE : Long.MIN_VALUE, after); - } - - /** - * Construct a key iterator. The iterator is positioned immediately before - * or after the key associated with the specified startValue/primaryKey. - * @param minValue minimum index value or null if no minimum - * @param maxValue maximum index value or null if no maximum - * @param startValue starting index value. - * @param primaryKey starting primary key value. - * @param after if true iterator is positioned immediately after - * the startValue/primaryKey, - * otherwise immediately before. - * @throws IOException - */ - PrimaryKeyIterator(Field minValue, Field maxValue, Field startValue, long primaryKey, - boolean after) throws IOException { - expectedModCount = indexTable.modCount; - indexIterator = indexTable.iterator(minValue, maxValue, startValue); - - if (hasNext()) { - if (startValue.equals(indexBuffer.getIndexKey())) { - index = indexBuffer.getIndex(primaryKey); - if (index < 0) { - index = -index - 1; - } - else if (after) { - index++; - } - if (index == indexBuffer.keyCount) { - --index; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = false; - hasPrev = true; - } - } - } - } - - @Override - public boolean hasNext() throws IOException { - if (hasNext) { - return true; - } - synchronized (db) { - - // Handle concurrent modification if necessary - // This is a slightly lazy approach which could miss keys added to the end of an index buffer - if (indexBuffer != null && index < (indexBuffer.keyCount - 1) && - indexTable.modCount != expectedModCount) { - - // refetch index buffer which may have changed - Field indexKey = indexBuffer.getIndexKey(); - Record indexRecord; - if (indexKey.isVariableLength()) { - indexRecord = indexTable.getRecord(indexKey); - } - else { - indexRecord = indexTable.getRecord(indexKey.getLongValue()); - } - if (indexRecord != null) { - - // recover position within index buffer - indexBuffer = new IndexBuffer(indexKey, indexRecord.getBinaryData(0)); - index = indexBuffer.getIndex(indexPrimaryKey + 1); - if (index < 0) { - index = -index - 1; - if (index == indexBuffer.keyCount) { - // next must be found in next index buffer below - indexBuffer = null; - } - else { - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = true; - } - } - else { - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = true; - } - } - else { - // index buffer no longer exists - will need to get next buffer below - indexBuffer = null; - } - hasPrev = false; - } - - if (!hasNext) { - // Goto next index buffer - if ((indexBuffer == null || index >= (indexBuffer.keyCount) - 1)) { - // get next index buffer - Record indexRecord = indexIterator.next(); - if (indexRecord != null) { - if (!forward) { - indexRecord = indexIterator.next(); - forward = true; - } - reverse = false; - if (indexRecord != null) { - indexBuffer = - new IndexBuffer(fieldType.newField(indexRecord.getKeyField()), - indexRecord.getBinaryData(0)); - index = 0; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = true; - hasPrev = false; - } - } - } - - // Step within current index buffer - else { - ++index; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = true; - hasPrev = false; - } - } - expectedModCount = indexTable.modCount; - return hasNext; - } - } - - @Override - public boolean hasPrevious() throws IOException { - if (hasPrev) { - return true; - } - synchronized (db) { - - // Handle concurrent modification if necessary - // This is a slightly lazy approach which could miss keys added to the beginning of an index buffer - if (indexBuffer != null && index > 0 && indexTable.modCount != expectedModCount) { - - // refetch index buffer which may have changed - Field indexKey = indexBuffer.getIndexKey(); - Record indexRecord; // refetch index buffer which may have changed - if (indexKey.isVariableLength()) { - indexRecord = indexTable.getRecord(indexKey); - } - else { - indexRecord = indexTable.getRecord(indexKey.getLongValue()); - } - if (indexRecord != null) { - - // recover position within index buffer - indexBuffer = new IndexBuffer(indexKey, indexRecord.getBinaryData(0)); - index = indexBuffer.getIndex(indexPrimaryKey - 1); - if (index < 0) { - index = -index - 1; - if (index == 0) { - // previous must be found in previous index buffer below - indexBuffer = null; - } - else { - --index; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasPrev = true; - } - } - else { - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasPrev = true; - } - } - else { - indexBuffer = null; - } - hasNext = false; - } - - if (!hasPrev) { - // Goto previous index buffer - if ((indexBuffer == null || index == 0)) { - // get previous index buffer - Record indexRecord = indexIterator.previous(); - if (indexRecord != null) { - if (!reverse) { - indexRecord = indexIterator.previous(); - reverse = true; - } - forward = false; - if (indexRecord != null) { - indexBuffer = - new IndexBuffer(fieldType.newField(indexRecord.getKeyField()), - indexRecord.getBinaryData(0)); - index = indexBuffer.keyCount - 1; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = false; - hasPrev = true; - } - } - } - - // Step within current index buffer - else { - --index; - indexPrimaryKey = indexBuffer.getPrimaryKey(index); - hasNext = false; - hasPrev = true; - } - } - expectedModCount = indexTable.modCount; - return hasPrev; - } - } - - @Override - public long next() throws IOException { - if (hasNext || hasNext()) { - long key = indexBuffer.getPrimaryKey(index); - lastKey = new LongField(key); - hasNext = false; - hasPrev = true; - return key; - } - throw new NoSuchElementException(); - } - - @Override - public long previous() throws IOException { - if (hasPrev || hasPrevious()) { - long key = indexBuffer.getPrimaryKey(index); - lastKey = new LongField(key); - hasNext = true; - hasPrev = false; - return key; - } - throw new NoSuchElementException(); - } - - /** - * WARNING: This could be slow since the index buffer must be read - * after each record deletion. - * @see db.DBLongIterator#delete() - */ - @Override - public boolean delete() throws IOException { - if (lastKey == null) { - return false; - } - synchronized (db) { - long key = lastKey.getLongValue(); - primaryTable.deleteRecord(key); - lastKey = null; - return true; - } - } - } + abstract DBFieldIterator keyIterator(Field minField, Field maxField, Field startField, + boolean before) throws IOException; } diff --git a/Ghidra/Framework/DB/src/main/java/db/IntField.java b/Ghidra/Framework/DB/src/main/java/db/IntField.java index d18adf95dd..00a64b2b57 100644 --- a/Ghidra/Framework/DB/src/main/java/db/IntField.java +++ b/Ghidra/Framework/DB/src/main/java/db/IntField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,30 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import db.buffers.DataBuffer; + /** * IntField provides a wrapper for 4-byte signed integer data * which is read or written to a Record. */ -public class IntField extends Field { +public final class IntField extends Field { + + /** + * Minimum integer field value + */ + public static final IntField MIN_VALUE = new IntField(Integer.MIN_VALUE, true); + + /** + * Maximum integer field value + */ + public static final IntField MAX_VALUE = new IntField(Integer.MAX_VALUE, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final IntField INSTANCE = MIN_VALUE; private int value; @@ -39,69 +53,57 @@ public class IntField extends Field { * @param i initial value */ public IntField(int i) { - value = i; + this(i, false); } /** - * @see db.Field#getIntValue() + * Construct an integer field with an initial value of i. + * @param i initial value + * @param immutable true if field value is immutable */ + IntField(int i, boolean immutable) { + super(immutable); + value = i; + } + @Override public int getIntValue() { return value; } - /** - * @see db.Field#setIntValue(int) - */ @Override public void setIntValue(int value) { + checkImmutable(); this.value = value; } - /** - * @see db.Field#length() - */ @Override int length() { return 4; } - /** - * @see db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { return buf.putInt(offset, value); } - /** - * @see db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); value = buf.getInt(offset); return offset + 4; } - /** - * @see db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { return 4; } - /** - * @see db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return INT_TYPE; } - /** - * @see java.lang.Object#toString() - */ @Override public String toString() { return "IntField: " + Integer.toString(value); @@ -109,12 +111,9 @@ public class IntField extends Field { @Override public String getValueAsString() { - return Integer.toHexString(value); + return "0x" + Integer.toHexString(value); } - /** - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof IntField)) @@ -122,9 +121,6 @@ public class IntField extends Field { return ((IntField) obj).value == value; } - /** - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { IntField f = (IntField) o; @@ -135,54 +131,64 @@ public class IntField extends Field { return 1; } - /** - * @see db.Field#newField(docking.widgets.fieldpanel.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue.isVariableLength()) - throw new AssertException(); - return new IntField((int) fieldValue.getLongValue()); + int compareTo(DataBuffer buffer, int offset) { + int otherValue = buffer.getInt(offset); + if (value == otherValue) + return 0; + else if (value < otherValue) + return -1; + return 1; } - /** - * @see db.Field#newField() - */ @Override - public Field newField() { + public IntField copyField() { + return new IntField((int) getLongValue()); + } + + @Override + public IntField newField() { return new IntField(); } - /** - * @see db.Field#getLongValue() - */ @Override public long getLongValue() { return value; } - /** - * @see db.Field#setLongValue(long) - */ @Override public void setLongValue(long value) { - this.value = (int) value; + setIntValue((int) value); } - /** - * @see db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return new byte[] { (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value }; } - /** - * @see java.lang.Object#hashCode() - */ + @Override + public void setBinaryData(byte[] bytes) { + checkImmutable(); + if (bytes.length != 4) { + throw new IllegalFieldAccessException(); + } + value = ((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) | ((bytes[2] & 0xff) << 8) | + (bytes[3] & 0xff); + } + @Override public int hashCode() { return value; } + + @Override + IntField getMinValue() { + return MIN_VALUE; + } + + @Override + IntField getMaxValue() { + return MAX_VALUE; + } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAppliedMarkupTableDBAdapterV0.java b/Ghidra/Framework/DB/src/main/java/db/InteriorNode.java similarity index 76% rename from Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAppliedMarkupTableDBAdapterV0.java rename to Ghidra/Framework/DB/src/main/java/db/InteriorNode.java index 30487db7ad..139886b7b6 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/db/VTAppliedMarkupTableDBAdapterV0.java +++ b/Ghidra/Framework/DB/src/main/java/db/InteriorNode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.feature.vt.api.db; - -public class VTAppliedMarkupTableDBAdapterV0 { +package db; +/** + * Marker interface for {@link Table} interior nodes within the BTree structure. + */ +public interface InteriorNode extends BTreeNode { + // marker interface only } diff --git a/Ghidra/Framework/DB/src/main/java/db/KeyToRecordIterator.java b/Ghidra/Framework/DB/src/main/java/db/KeyToRecordIterator.java index d5d2eb97f2..e69a8d753f 100644 --- a/Ghidra/Framework/DB/src/main/java/db/KeyToRecordIterator.java +++ b/Ghidra/Framework/DB/src/main/java/db/KeyToRecordIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,25 +23,26 @@ import java.util.NoSuchElementException; */ public class KeyToRecordIterator implements RecordIterator { - private DBLongIterator keyIter; + private DBFieldIterator keyIter; private Table table; private DBHandle db; - + /** * Construct a record iterator from a secondary index key iterator. * @param keyIter key iterator. */ - public KeyToRecordIterator(Table table, DBLongIterator keyIter) { + public KeyToRecordIterator(Table table, DBFieldIterator keyIter) { this.table = table; this.db = table.getDBHandle(); this.keyIter = keyIter; } - + /** * @see db.RecordIterator#hasNext() */ + @Override public boolean hasNext() throws IOException { - synchronized(db) { + synchronized (db) { return keyIter.hasNext(); } } @@ -50,8 +50,9 @@ public class KeyToRecordIterator implements RecordIterator { /** * @see db.RecordIterator#hasPrevious() */ + @Override public boolean hasPrevious() throws IOException { - synchronized(db) { + synchronized (db) { return keyIter.hasPrevious(); } } @@ -59,12 +60,14 @@ public class KeyToRecordIterator implements RecordIterator { /** * @see db.RecordIterator#next() */ + @Override public Record next() throws IOException { - synchronized(db) { + synchronized (db) { try { return table.getRecord(keyIter.next()); - } catch (NoSuchElementException e) { - return null; + } + catch (NoSuchElementException e) { + return null; } } } @@ -72,12 +75,14 @@ public class KeyToRecordIterator implements RecordIterator { /** * @see db.RecordIterator#previous() */ + @Override public Record previous() throws IOException { - synchronized(db) { + synchronized (db) { try { return table.getRecord(keyIter.previous()); - } catch (NoSuchElementException e) { - return null; + } + catch (NoSuchElementException e) { + return null; } } } @@ -85,6 +90,7 @@ public class KeyToRecordIterator implements RecordIterator { /** * @see db.RecordIterator#delete() */ + @Override public boolean delete() throws IOException { return keyIter.delete(); } diff --git a/Ghidra/Framework/DB/src/main/java/db/LegacyIndexField.java b/Ghidra/Framework/DB/src/main/java/db/LegacyIndexField.java new file mode 100644 index 0000000000..e7732d5dc8 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/LegacyIndexField.java @@ -0,0 +1,61 @@ +/* ### + * 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 db; + +/** + * LegacyIndexField supports legacy index tables where the indexed + * field was a {@link LongField} and improperly employed a variable-length + * index storage scheme when the primary key was a LongField. + */ +class LegacyIndexField extends IndexField { + + /** + * Constructor + * @param indexField primary table field type being indexed + */ + LegacyIndexField(Field indexField) { + super(indexField, new LongField()); + } + + private LegacyIndexField(Field indexField, LongField primaryKey) { + super(indexField, primaryKey); + } + + @Override + public boolean isVariableLength() { + // NOTE: while fixed-length IndexFields are possible this past + // oversight failed to override this method for fixed-length cases + // (e.g., indexing fixed-length field with long primary key). + // To preserve backward compatibility this can not be changed for + // long primary keys. + return true; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof LegacyIndexField) && super.equals(obj); + } + + @Override + LegacyIndexField newIndexField(Field indexValue, Field primaryKey) { + if (!indexValue.isSameType(getIndexedField()) || !(primaryKey instanceof LongField)) { + throw new IllegalArgumentException("incorrect index value or key type"); + + } + return new LegacyIndexField(indexValue, (LongField) primaryKey); + } + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/LongField.java b/Ghidra/Framework/DB/src/main/java/db/LongField.java index efe2b988b8..cd1604dbec 100644 --- a/Ghidra/Framework/DB/src/main/java/db/LongField.java +++ b/Ghidra/Framework/DB/src/main/java/db/LongField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,30 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import db.buffers.DataBuffer; + /** * LongField provides a wrapper for 8-byte signed long data * which is read or written to a Record. */ -public class LongField extends Field { +public final class LongField extends Field { + + /** + * Minimum long field value + */ + public static final LongField MIN_VALUE = new LongField(Long.MIN_VALUE, true); + + /** + * Maximum long field value + */ + public static final LongField MAX_VALUE = new LongField(Long.MAX_VALUE, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final LongField INSTANCE = MIN_VALUE; private long value; @@ -39,69 +53,57 @@ public class LongField extends Field { * @param l initial value */ public LongField(long l) { + this(l, false); + } + + /** + * Construct a long field with an initial value of l. + * @param l initial value + * @param immutable true if field value is immutable + */ + LongField(long l, boolean immutable) { + super(immutable); value = l; } - /* - * @see ghidra.framework.store.db.Field#getLongValue() - */ @Override public long getLongValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#setLongValue(long) - */ @Override public void setLongValue(long value) { + checkImmutable(); this.value = value; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return 8; } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { return buf.putLong(offset, value); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); value = buf.getLong(offset); return offset + 8; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { return 8; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return LONG_TYPE; } - /* - * @see java.lang.Object#toString() - */ @Override public String toString() { return "LongField: " + Long.toString(value); @@ -109,12 +111,9 @@ public class LongField extends Field { @Override public String getValueAsString() { - return Long.toHexString(value); + return "0x" + Long.toHexString(value); } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof LongField)) @@ -122,11 +121,11 @@ public class LongField extends Field { return ((LongField) obj).value == value; } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { + if (!(o instanceof LongField)) { + throw new UnsupportedOperationException("may only compare similar Field types"); + } LongField f = (LongField) o; if (value == f.value) return 0; @@ -135,27 +134,26 @@ public class LongField extends Field { return 1; } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue.isVariableLength()) - throw new AssertException(); - return new LongField(fieldValue.getLongValue()); + int compareTo(DataBuffer buffer, int offset) { + long otherValue = buffer.getLong(offset); + if (value == otherValue) + return 0; + else if (value < otherValue) + return -1; + return 1; } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public LongField copyField() { + return new LongField(getLongValue()); + } + + @Override + public LongField newField() { return new LongField(); } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return new byte[] { (byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), @@ -163,12 +161,31 @@ public class LongField extends Field { (byte) value }; } - /* - * @see java.lang.Object#hashCode() - */ + @Override + public void setBinaryData(byte[] bytes) { + checkImmutable(); + if (bytes.length != 8) { + throw new IllegalFieldAccessException(); + } + value = (((long) bytes[0] & 0xff) << 56) | (((long) bytes[1] & 0xff) << 48) | + (((long) bytes[2] & 0xff) << 40) | (((long) bytes[3] & 0xff) << 32) | + (((long) bytes[4] & 0xff) << 24) | (((long) bytes[5] & 0xff) << 16) | + (((long) bytes[6] & 0xff) << 8) | ((long) bytes[7] & 0xff); + } + @Override public int hashCode() { return (int) (value ^ (value >>> 32)); } + @Override + LongField getMinValue() { + return MIN_VALUE; + } + + @Override + LongField getMaxValue() { + return MAX_VALUE; + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/LongKeyInteriorNode.java b/Ghidra/Framework/DB/src/main/java/db/LongKeyInteriorNode.java index 5adfadb361..2e8b954eaa 100644 --- a/Ghidra/Framework/DB/src/main/java/db/LongKeyInteriorNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/LongKeyInteriorNode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package db; +import java.io.IOException; + +import db.buffers.DataBuffer; import ghidra.util.Msg; import ghidra.util.exception.AssertException; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.buffers.DataBuffer; - /** * LongKeyInteriorNode stores a BTree node for use as an interior * node when searching for Table records within the database. This type of node @@ -33,7 +31,7 @@ import db.buffers.DataBuffer; * | NodeType(1) | KeyCount(4) | Key0(8) | ID0(4) | ... | KeyN(8) | IDN(4) | * */ -class LongKeyInteriorNode extends LongKeyNode { +class LongKeyInteriorNode extends LongKeyNode implements InteriorNode { private static final int BASE = LONGKEY_NODE_HEADER_SIZE; @@ -63,7 +61,8 @@ class LongKeyInteriorNode extends LongKeyNode { * @param id2 right child node buffer ID * @throws IOException thrown if IO error occurs */ - LongKeyInteriorNode(NodeMgr nodeMgr, long key1, int id1, long key2, int id2) throws IOException { + LongKeyInteriorNode(NodeMgr nodeMgr, long key1, int id1, long key2, int id2) + throws IOException { super(nodeMgr, NodeMgr.LONGKEY_INTERIOR_NODE); maxKeyCount = (buffer.length() - BASE) / ENTRY_SIZE; setKeyCount(2); @@ -73,6 +72,11 @@ class LongKeyInteriorNode extends LongKeyNode { putEntry(1, key2, id2); } + @Override + public LongKeyInteriorNode getParent() { + return parent; + } + /** * Construct a new empty long-key interior node. * Node must be initialized with a minimum of two keys. @@ -86,16 +90,16 @@ class LongKeyInteriorNode extends LongKeyNode { void logConsistencyError(String tableName, String msg, Throwable t) { Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); - Msg.debug(this, " parent.key[0]=" + Long.toHexString(getKey(0)) + " bufferID=" + - getBufferId()); + Msg.debug(this, + " parent.key[0]=" + Long.toHexString(getKey(0)) + " bufferID=" + getBufferId()); if (t != null) { Msg.error(this, "Consistency Error (" + tableName + ")", t); } } @Override - public boolean isConsistent(String tableName, TaskMonitor monitor) throws IOException, - CancelledException { + public boolean isConsistent(String tableName, TaskMonitor monitor) + throws IOException, CancelledException { boolean consistent = true; long lastMinKey = 0; long lastMaxKey = 0; @@ -106,23 +110,21 @@ class LongKeyInteriorNode extends LongKeyNode { if (i != 0) { if (key <= lastMinKey) { consistent = false; - logConsistencyError(tableName, "child[" + i + "].minKey <= child[" + (i - 1) + - "].minKey", null); + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].minKey", null); Msg.debug(this, " child[" + i + "].minKey = 0x" + Long.toHexString(key) + " bufferID=" + getBufferId(i)); - Msg.debug(this, - " child[" + (i - 1) + "].minKey = 0x" + Long.toHexString(lastMinKey) + - " bufferID=" + getBufferId(i - 1)); + Msg.debug(this, " child[" + (i - 1) + "].minKey = 0x" + + Long.toHexString(lastMinKey) + " bufferID=" + getBufferId(i - 1)); } else if (key <= lastMaxKey) { consistent = false; - logConsistencyError(tableName, "child[" + i + "].minKey <= child[" + (i - 1) + - "].maxKey", null); + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].maxKey", null); Msg.debug(this, " child[" + i + "].minKey = 0x" + Long.toHexString(key) + " bufferID=" + getBufferId(i)); - Msg.debug(this, - " child[" + (i - 1) + "].maxKey = 0x" + Long.toHexString(lastMaxKey) + - " bufferID=" + getBufferId(i - 1)); + Msg.debug(this, " child[" + (i - 1) + "].maxKey = 0x" + + Long.toHexString(lastMaxKey) + " bufferID=" + getBufferId(i - 1)); } } @@ -155,8 +157,8 @@ class LongKeyInteriorNode extends LongKeyNode { long childKey0 = node.getKey(0); if (key != childKey0) { consistent = false; - logConsistencyError(tableName, "parent key entry mismatch with child[" + i + - "].minKey", null); + logConsistencyError(tableName, + "parent key entry mismatch with child[" + i + "].minKey", null); Msg.debug(this, " child[" + i + "].minKey = 0x" + Long.toHexString(childKey0) + " bufferID=" + getBufferId(i - 1)); Msg.debug(this, " parent key entry = 0x" + Long.toHexString(key)); @@ -178,9 +180,15 @@ class LongKeyInteriorNode extends LongKeyNode { /** * Perform a binary search to locate the specified key and derive an index - * into the Buffer ID storage. - * @param key - * @return int buffer ID index. + * into the Buffer ID storage. This method is intended to locate the child + * node which contains the specified key. The returned index corresponds + * to a child's stored buffer/node ID and may correspond to another interior + * node or a leaf record node. Each stored key within this interior node + * effectively identifies the maximum key contained within the corresponding + * child node. + * @param key key to search for + * @return int buffer ID index of child node. An existing positive index + * value will always be returned. */ int getIdIndex(long key) { @@ -203,10 +211,18 @@ class LongKeyInteriorNode extends LongKeyNode { return max; } + @Override + public int getKeyIndex(Field key) throws IOException { + return getKeyIndex(key.getLongValue()); + } + /** * Perform a binary search to locate the specified key and derive an index - * into the Buffer ID storage. - * @param key + * into the Buffer ID storage. This method is intended to find the insertion + * index or exact match for a child key. A negative value will be returned + * when an exact match is not found and may be transformed into an + * insertion index (insetIndex = -returnedIndex-1). + * @param key key to search for * @return int buffer ID index. */ private int getKeyIndex(long key) { @@ -230,9 +246,6 @@ class LongKeyInteriorNode extends LongKeyNode { return -(min + 1); } - /* - * @see ghidra.framework.store.db.LongKeyNode#getKey(int) - */ @Override long getKey(int index) { return buffer.getLong(BASE + (index * ENTRY_SIZE)); @@ -329,10 +342,11 @@ class LongKeyInteriorNode extends LongKeyNode { if (index < 0) { throw new AssertException(); } + // Update key + putKey(index, newKey); if (index == 0 && parent != null) { parent.keyChanged(oldKey, newKey); } - putKey(index, newKey); } /** @@ -395,9 +409,6 @@ class LongKeyInteriorNode extends LongKeyNode { newNode.getBufferId()); } - /* - * @see ghidra.framework.store.db.LongKeyNode#getLeafNode(long) - */ @Override LongKeyRecordNode getLeafNode(long key) throws IOException { LongKeyNode node = nodeMgr.getLongKeyNode(getBufferId(getIdIndex(key))); @@ -544,9 +555,6 @@ class LongKeyInteriorNode extends LongKeyNode { } } - /* - * @see ghidra.framework.store.db.LongKeyNode#delete() - */ @Override public void delete() throws IOException { @@ -559,9 +567,7 @@ class LongKeyInteriorNode extends LongKeyNode { nodeMgr.deleteNode(this); } - /* - * @see ghidra.framework.store.db.BTreeNode#getBufferReferences() - */ + @Override public int[] getBufferReferences() { int[] ids = new int[keyCount]; for (int i = 0; i < keyCount; i++) { diff --git a/Ghidra/Framework/DB/src/main/java/db/LongKeyNode.java b/Ghidra/Framework/DB/src/main/java/db/LongKeyNode.java index 1bf69e8404..c3c5a0c961 100644 --- a/Ghidra/Framework/DB/src/main/java/db/LongKeyNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/LongKeyNode.java @@ -101,6 +101,11 @@ abstract class LongKeyNode implements BTreeNode { */ abstract long getKey(int index); + @Override + public final Field getKeyField(int index) throws IOException { + return new LongField(getKey(index)); + } + /** * Get the leaf node which contains the specified key. * @param key key value diff --git a/Ghidra/Framework/DB/src/main/java/db/LongKeyRecordNode.java b/Ghidra/Framework/DB/src/main/java/db/LongKeyRecordNode.java index e340f6353d..dd1396d6db 100644 --- a/Ghidra/Framework/DB/src/main/java/db/LongKeyRecordNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/LongKeyRecordNode.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +15,32 @@ */ package db; +import java.io.IOException; +import db.buffers.DataBuffer; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.buffers.DataBuffer; - /** * LongKeyRecordNode is an abstract implementation of a BTree leaf node * which utilizes long key values and stores records. + *

+ * This type of node has the following partial layout within a single DataBuffer + * (field size in bytes): + *

+ *   | NodeType(1) | KeyCount(4) | PrevLeafId(4) | NextLeafId(4) | ...
+ * 
*/ -abstract class LongKeyRecordNode extends LongKeyNode { +abstract class LongKeyRecordNode extends LongKeyNode implements RecordNode { private static final int ID_SIZE = 4; - + private static final int PREV_LEAF_ID_OFFSET = LONGKEY_NODE_HEADER_SIZE; private static final int NEXT_LEAF_ID_OFFSET = PREV_LEAF_ID_OFFSET + ID_SIZE; - - static final int RECORD_LEAF_HEADER_SIZE = LONGKEY_NODE_HEADER_SIZE + 2*ID_SIZE; - + + static final int RECORD_LEAF_HEADER_SIZE = LONGKEY_NODE_HEADER_SIZE + 2 * ID_SIZE; + /** * Construct an existing long-key record leaf node. * @param nodeMgr table node manager instance @@ -46,7 +49,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyRecordNode(NodeMgr nodeMgr, DataBuffer buf) { super(nodeMgr, buf); } - + /** * Construct a new long-key record leaf node. * @param nodeMgr table node manager instance @@ -55,14 +58,20 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @param nextLeafId node buffer id for next leaf - right sibling ( < 0 : no leaf) * @throws IOException thrown if an IO error occurs */ - LongKeyRecordNode(NodeMgr nodeMgr, byte nodeType, int prevLeafId, int nextLeafId) throws IOException { + LongKeyRecordNode(NodeMgr nodeMgr, byte nodeType, int prevLeafId, int nextLeafId) + throws IOException { super(nodeMgr, nodeType); - + // Initialize header buffer.putInt(PREV_LEAF_ID_OFFSET, prevLeafId); buffer.putInt(NEXT_LEAF_ID_OFFSET, nextLeafId); } - + + @Override + public LongKeyInteriorNode getParent() { + return parent; + } + void logConsistencyError(String tableName, String msg, Throwable t) { Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); Msg.debug(this, " bufferID=" + getBufferId() + " key[0]=0x" + Long.toHexString(getKey(0))); @@ -70,9 +79,10 @@ abstract class LongKeyRecordNode extends LongKeyNode { Msg.error(this, "Consistency Error (" + tableName + ")", t); } } - + @Override - public boolean isConsistent(String tableName, TaskMonitor monitor) throws IOException, CancelledException { + public boolean isConsistent(String tableName, TaskMonitor monitor) + throws IOException, CancelledException { boolean consistent = true; long prevKey = 0; for (int i = 0; i < keyCount; i++) { @@ -81,14 +91,15 @@ abstract class LongKeyRecordNode extends LongKeyNode { if (i != 0) { if (key <= prevKey) { consistent = false; - logConsistencyError(tableName, "key[" + i + "] <= key[" + (i-1) + "]", null); + logConsistencyError(tableName, "key[" + i + "] <= key[" + (i - 1) + "]", null); Msg.debug(this, " key[" + i + "].minKey = 0x" + Long.toHexString(key)); - Msg.debug(this, " key[" + (i-1) + "].minKey = 0x" + Long.toHexString(prevKey)); + Msg.debug(this, + " key[" + (i - 1) + "].minKey = 0x" + Long.toHexString(prevKey)); } } prevKey = key; } - + if ((parent == null || parent.isLeftmostKey(getKey(0))) && getPreviousLeaf() != null) { consistent = false; logConsistencyError(tableName, "previous-leaf should not exist", null); @@ -112,18 +123,15 @@ abstract class LongKeyRecordNode extends LongKeyNode { consistent = false; logConsistencyError(tableName, "this leaf is not linked to next-leaf", null); } - + return consistent; } - - /* - * @see ghidra.framework.store.db.LongKeyNode#getLeafNode(long) - */ + @Override - LongKeyRecordNode getLeafNode(long key) throws IOException { + LongKeyRecordNode getLeafNode(long key) throws IOException { return this; } - + /** * Get this leaf node's right sibling * @return this leaf node's right sibling or null if right sibling does not exist. @@ -135,9 +143,9 @@ abstract class LongKeyRecordNode extends LongKeyNode { if (nextLeafId >= 0) { leaf = (LongKeyRecordNode) nodeMgr.getLongKeyNode(nextLeafId); } - return leaf; + return leaf; } - + /** * Get this leaf node's left sibling * @return this leaf node's left sibling or null if left sibling does not exist. @@ -149,9 +157,9 @@ abstract class LongKeyRecordNode extends LongKeyNode { if (nextLeafId >= 0) { leaf = (LongKeyRecordNode) nodeMgr.getLongKeyNode(nextLeafId); } - return leaf; + return leaf; } - + /** * Perform a binary search to locate the specified key. * @param key key value @@ -159,12 +167,12 @@ abstract class LongKeyRecordNode extends LongKeyNode { * point. */ int getKeyIndex(long key) { - + int min = 0; int max = keyCount - 1; - + while (min <= max) { - int i = (min + max)/2; + int i = (min + max) / 2; long k = getKey(i); if (k == key) { return i; @@ -176,9 +184,14 @@ abstract class LongKeyRecordNode extends LongKeyNode { max = i - 1; } } - return -(min+1); + return -(min + 1); } - + + @Override + public int getKeyIndex(Field key) throws IOException { + return getKeyIndex(key.getLongValue()); + } + /** * Split this leaf node in half and update tree. * When a split is performed, the next operation must be performed @@ -187,7 +200,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @throws IOException thrown if an IO error occurs */ LongKeyNode split() throws IOException { - + // Create new leaf int oldSiblingId = buffer.getInt(NEXT_LEAF_ID_OFFSET); LongKeyRecordNode newLeaf = createNewLeaf(buffer.getId(), oldSiblingId); @@ -199,59 +212,61 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyRecordNode leaf = (LongKeyRecordNode) nodeMgr.getLongKeyNode(oldSiblingId); leaf.buffer.putInt(PREV_LEAF_ID_OFFSET, newBufId); } - + // Split node creating two balanced leaves splitData(newLeaf); - + if (parent != null) { // Ask parent to insert new node and return root return parent.insert(newBufId, newLeaf.getKey(0)); } - + // New parent node becomes root - return new LongKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), newLeaf.getKey(0), newBufId); + return new LongKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), newLeaf.getKey(0), + newBufId); } - + /** * Append a leaf which contains one or more keys and update tree. Leaf is inserted * as the new right sibling of this leaf. - * @param newLeaf new right sibling leaf (must be same node type as this leaf) + * @param leaf new right sibling leaf (must be same node type as this leaf) * @return root node which may have changed. * @throws IOException thrown if an IO error occurs */ LongKeyNode appendLeaf(LongKeyRecordNode leaf) throws IOException { - + // Create new leaf and link leaf.buffer.putInt(PREV_LEAF_ID_OFFSET, buffer.getId()); int rightLeafBufId = buffer.getInt(NEXT_LEAF_ID_OFFSET); leaf.buffer.putInt(NEXT_LEAF_ID_OFFSET, rightLeafBufId); - + // Adjust this node int newBufId = leaf.buffer.getId(); buffer.putInt(NEXT_LEAF_ID_OFFSET, newBufId); - + // Adjust old right node if present if (rightLeafBufId >= 0) { LongKeyNode rightLeaf = nodeMgr.getLongKeyNode(rightLeafBufId); rightLeaf.buffer.putInt(PREV_LEAF_ID_OFFSET, newBufId); } - + if (parent != null) { // Ask parent to insert new node and return root - leaf parent is unknown return parent.insert(newBufId, leaf.getKey(0)); } - + // New parent node becomes root - return new LongKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), leaf.getKey(0), newBufId); + return new LongKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), leaf.getKey(0), + newBufId); } - + /** * Remove this leaf from the tree. * @return root node which may have changed. * @throws IOException thrown if IO error occurs */ LongKeyNode removeLeaf() throws IOException { - + long key = getKey(0); int prevBufferId = buffer.getInt(PREV_LEAF_ID_OFFSET); int nextBufferId = buffer.getInt(NEXT_LEAF_ID_OFFSET); @@ -263,11 +278,11 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyRecordNode nextNode = (LongKeyRecordNode) nodeMgr.getLongKeyNode(nextBufferId); nextNode.getBuffer().putInt(PREV_LEAF_ID_OFFSET, prevBufferId); } - + nodeMgr.deleteNode(this); if (parent == null) { return null; - } + } return parent.deleteChild(key); } @@ -277,12 +292,12 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @param newRightLeaf empty right sibling leaf */ abstract void splitData(LongKeyRecordNode newRightLeaf); - + /** * Create a new leaf and add to the node manager. * The new leaf's parent is unknown. - * @param prevLeafId node buffer id for previous leaf - left sibling ( < 0: no leaf) - * @param nextLeafId node buffer id for next leaf - right sibling ( < 0 : no leaf) + * @param prevNodeId node buffer id for previous leaf - left sibling ( < 0: no leaf) + * @param nextNodeId node buffer id for next leaf - right sibling ( < 0 : no leaf) * @return new leaf node. * @throws IOException thrown if IO error occurs */ @@ -296,10 +311,10 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @throws IOException thrown if IO error occurs */ LongKeyNode putRecord(Record record, Table table) throws IOException { - + long key = record.getKey(); int index = getKeyIndex(key); - + // Handle record update case if (index >= 0) { if (table != null) { @@ -308,19 +323,19 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyNode newRoot = updateRecord(index, record); return newRoot; } - + // Handle new record - see if we have room in this leaf - index = -index-1; + index = -index - 1; if (insertRecord(index, record)) { if (index == 0 && parent != null) { - parent.keyChanged(getKey(1), key); + parent.keyChanged(getKey(1), key); } if (table != null) { table.insertedRecord(record); } return getRoot(); } - + // Special Case - append new leaf to right if (index == keyCount) { LongKeyNode newRoot = appendNewLeaf(record); @@ -334,19 +349,19 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyRecordNode leaf = split().getLeafNode(key); return leaf.putRecord(record, table); } - + /** * Append a new leaf and insert the specified record. * @param record data record with long key * @return root node which may have changed. * @throws IOException thrown if IO error occurs - */ + */ LongKeyNode appendNewLeaf(Record record) throws IOException { LongKeyRecordNode newLeaf = createNewLeaf(-1, -1); newLeaf.insertRecord(0, record); return appendLeaf(newLeaf); } - + /** * Delete the record identified by the specified key. * @param key record key @@ -361,7 +376,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { if (index < 0) { return getRoot(); } - + if (table != null) { table.deletedRecord(getRecord(table.getSchema(), index)); } @@ -374,15 +389,15 @@ abstract class LongKeyRecordNode extends LongKeyNode { // Remove record within this node remove(index); - + // Notify parent of leftmost key change if (index == 0 && parent != null) { - parent.keyChanged(key, getKey(0)); + parent.keyChanged(key, getKey(0)); } return getRoot(); } - + /** * Remove the record identified by index. * This will never be the last record within the node. @@ -391,7 +406,6 @@ abstract class LongKeyRecordNode extends LongKeyNode { */ abstract void remove(int index) throws IOException; - /** * Inserts the record at the given index if there is sufficient space in * the buffer. @@ -401,7 +415,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @throws IOException thrown if IO error occurs */ abstract boolean insertRecord(int index, Record record) throws IOException; - + /** * Updates the record at the given index. * @param index record index @@ -419,16 +433,16 @@ abstract class LongKeyRecordNode extends LongKeyNode { * @throws IOException thrown if IO error occurs */ abstract Record getRecord(long key, Schema schema) throws IOException; - + /** * Get the record located at the specified index. * @param schema record data schema - * @param keyIndex key index + * @param index key index * @return Record * @throws IOException thrown if IO error occurs */ abstract Record getRecord(Schema schema, int index) throws IOException; - + /** * Get the first record whoose key is less than the specified key. * @param key record key @@ -439,7 +453,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { Record getRecordBefore(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { - index = -index-2; + index = -index - 2; } else { --index; @@ -448,9 +462,9 @@ abstract class LongKeyRecordNode extends LongKeyNode { LongKeyRecordNode nextLeaf = getPreviousLeaf(); return nextLeaf != null ? nextLeaf.getRecord(schema, nextLeaf.keyCount - 1) : null; } - return getRecord(schema, index); + return getRecord(schema, index); } - + /** * Get the first record whoose key is greater than the specified key. * @param key record key @@ -461,7 +475,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { Record getRecordAfter(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { - index = -(index+1); + index = -(index + 1); } else { ++index; @@ -472,7 +486,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { } return getRecord(schema, index); } - + /** * Get the first record whoose key is less than or equal to the specified * key. @@ -484,15 +498,15 @@ abstract class LongKeyRecordNode extends LongKeyNode { Record getRecordAtOrBefore(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { - index = -index-2; + index = -index - 2; } if (index < 0) { LongKeyRecordNode nextLeaf = getPreviousLeaf(); return nextLeaf != null ? nextLeaf.getRecord(schema, nextLeaf.keyCount - 1) : null; } - return getRecord(schema, index); + return getRecord(schema, index); } - + /** * Get the first record whoose key is greater than or equal to the specified * key. @@ -504,19 +518,19 @@ abstract class LongKeyRecordNode extends LongKeyNode { Record getRecordAtOrAfter(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { - index = -(index+1); + index = -(index + 1); } if (index == keyCount) { LongKeyRecordNode nextLeaf = getNextLeaf(); return nextLeaf != null ? nextLeaf.getRecord(schema, 0) : null; } - return getRecord(schema, index); + return getRecord(schema, index); } - + /** * Create a new record node with no siblings attached. * @param nodeMgr table node manager instance - * @param fixedRecordLength length of fixed-length record, 0 = variable length + * @param schema record schema * @return new record leaf node * @throws IOException thrown if IO error occurs */ @@ -528,7 +542,7 @@ abstract class LongKeyRecordNode extends LongKeyNode { else { node = new FixedRecNode(nodeMgr, schema.getFixedLength(), -1, -1); } - return node; + return node; } } diff --git a/Ghidra/Framework/DB/src/main/java/db/MasterTable.java b/Ghidra/Framework/DB/src/main/java/db/MasterTable.java index 675f9d420d..ec2db855bc 100644 --- a/Ghidra/Framework/DB/src/main/java/db/MasterTable.java +++ b/Ghidra/Framework/DB/src/main/java/db/MasterTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +15,6 @@ */ package db; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -28,45 +26,48 @@ import java.util.Arrays; * object associated with the database. */ class MasterTable { - + private TableRecord masterRecord; - + + private DBHandle dbh; private DBParms dbParms; private Table table; // List of table records sorted by tablenum TableRecord[] tableRecords; - + private long nextTableNum = 0; - + /** * Construct an existing master table. - * @param db database handle + * @param dbh database handle + * @throws IOException database IO error */ - MasterTable(DBHandle db) throws IOException { - this.dbParms = db.getDBParms(); - - masterRecord = new TableRecord(0, "MASTER", - TableRecord.getTableRecordSchema(), -1); + MasterTable(DBHandle dbh) throws IOException { + this.dbh = dbh; + this.dbParms = dbh.getDBParms(); + + masterRecord = new TableRecord(0, "MASTER", TableRecord.getTableRecordSchema(), -1); try { masterRecord.setRootBufferId(dbParms.get(DBParms.MASTER_TABLE_ROOT_BUFFER_ID_PARM)); - } catch (ArrayIndexOutOfBoundsException e) { - throw new IOException("Corrupt database parameters"); } - - table = new Table(db, masterRecord); + catch (ArrayIndexOutOfBoundsException e) { + throw new IOException("Corrupt database parameters", e); + } + + table = new Table(dbh, masterRecord); ArrayList trList = new ArrayList(); RecordIterator it = table.iterator(); - while(it.hasNext()) { - trList.add(new TableRecord(it.next())); + while (it.hasNext()) { + trList.add(new TableRecord(dbh, it.next())); } tableRecords = new TableRecord[trList.size()]; trList.toArray(tableRecords); if (tableRecords.length > 0) { - nextTableNum = tableRecords[tableRecords.length-1].getTableNum() + 1; + nextTableNum = tableRecords[tableRecords.length - 1].getTableNum() + 1; } } - + /** * Create a new table record and add to master table. * If this is an index table the name corresponds to the table which is @@ -78,34 +79,37 @@ class MasterTable { * @param tableSchema table schema * @param indexedColumn primary table index key column, or -1 for primary table * @return new table record + * @throws IOException database IO error */ - TableRecord createTableRecord(String name, Schema tableSchema, int indexedColumn) throws IOException { - + TableRecord createTableRecord(String name, Schema tableSchema, int indexedColumn) + throws IOException { + // Create new table record TableRecord tableRecord = new TableRecord(nextTableNum++, name, tableSchema, indexedColumn); table.putRecord(tableRecord.getRecord()); // Update master root which may have changed dbParms.set(DBParms.MASTER_TABLE_ROOT_BUFFER_ID_PARM, masterRecord.getRootBufferId()); - + // Update tableRecord list - TableRecord[] newList = new TableRecord[tableRecords.length+1]; + TableRecord[] newList = new TableRecord[tableRecords.length + 1]; System.arraycopy(tableRecords, 0, newList, 0, tableRecords.length); newList[tableRecords.length] = tableRecord; Arrays.sort(newList); tableRecords = newList; - + return tableRecord; } - + /** * Remove the master table record associated with the specified table name. * This method may only be invoked while a database transaction * is in progress. - * @param id table name + * @param tableNum table number (key within master table) + * @throws IOException database IO error */ void deleteTableRecord(long tableNum) throws IOException { - + // Locate tableRecord to be deleted for (int i = 0; i < tableRecords.length; i++) { if (tableRecords[i].getTableNum() == tableNum) { @@ -113,21 +117,22 @@ class MasterTable { throw new IOException("Can not delete non-empty table"); table.deleteRecord(tableNum); tableRecords[i].invalidate(); - + // Update master root which may have changed - dbParms.set(DBParms.MASTER_TABLE_ROOT_BUFFER_ID_PARM, masterRecord.getRootBufferId()); - + dbParms.set(DBParms.MASTER_TABLE_ROOT_BUFFER_ID_PARM, + masterRecord.getRootBufferId()); + // Update tableRecord list - TableRecord[] newList = new TableRecord[tableRecords.length-1]; + TableRecord[] newList = new TableRecord[tableRecords.length - 1]; System.arraycopy(tableRecords, 0, newList, 0, i); - System.arraycopy(tableRecords, i+1, newList, i, tableRecords.length-i-1); + System.arraycopy(tableRecords, i + 1, newList, i, tableRecords.length - i - 1); tableRecords = newList; return; } } throw new IOException("Table not found"); } - + /** * Get a list of all tables defined within this master table. * Records are returned in the list ordered by their table number key. @@ -136,59 +141,61 @@ class MasterTable { TableRecord[] getTableRecords() { return tableRecords; } - + /** * Refresh table data from the master table. * Records are returned in the list ordered by their table number key. * @return the update list of master table records. + * @throws IOException database IO error */ TableRecord[] refreshTableRecords() throws IOException { - + try { int masterRootId = dbParms.get(DBParms.MASTER_TABLE_ROOT_BUFFER_ID_PARM); if (masterRecord.getRootBufferId() != masterRootId) { masterRecord.setRootBufferId(masterRootId); table.tableRecordChanged(); } - } catch (ArrayIndexOutOfBoundsException e) { - throw new IOException("Corrupt database parameters"); + } + catch (ArrayIndexOutOfBoundsException e) { + throw new IOException("Corrupt database parameters", e); } ArrayList trList = new ArrayList(); - + int ix = 0; int oldTableCnt = tableRecords.length; RecordIterator it = table.iterator(); while (it.hasNext()) { Record rec = it.next(); long tablenum = rec.getKey(); - + while (ix < tableRecords.length && tablenum > tableRecords[ix].getTableNum()) { tableRecords[ix++].invalidate(); // table no longer exists } - + if (ix == oldTableCnt || tablenum < tableRecords[ix].getTableNum()) { - trList.add(new TableRecord(rec)); // new table - } + trList.add(new TableRecord(dbh, rec)); // new table + } else if (tablenum == tableRecords[ix].getTableNum()) { - tableRecords[ix].setRecord(rec); + tableRecords[ix].setRecord(dbh, rec); trList.add(tableRecords[ix++]); // update existing table - } + } } - + while (ix < tableRecords.length) { tableRecords[ix++].invalidate(); // table no longer exists } - + tableRecords = trList.toArray(new TableRecord[trList.size()]); return tableRecords; } - + /** * Flush all unsaved table changes to the underlying buffer mgr. * This method may only be invoked while a database transaction * is in progress. - * @throws IOException + * @throws IOException database IO error */ void flush() throws IOException { for (int i = 0; i < tableRecords.length; i++) { @@ -201,8 +208,8 @@ class MasterTable { /** * Change the name of a table and its associated indexes. - * @param oldName - * @param newName + * @param oldName old table name + * @param newName new tablename */ void changeTableName(String oldName, String newName) { for (int i = 0; i < tableRecords.length; i++) { @@ -211,6 +218,5 @@ class MasterTable { } } } - -} +} diff --git a/Ghidra/Framework/DB/src/main/java/db/NodeMgr.java b/Ghidra/Framework/DB/src/main/java/db/NodeMgr.java index 84cbe772b2..552f81ac36 100644 --- a/Ghidra/Framework/DB/src/main/java/db/NodeMgr.java +++ b/Ghidra/Framework/DB/src/main/java/db/NodeMgr.java @@ -15,13 +15,12 @@ */ package db; -import ghidra.util.datastruct.IntObjectHashtable; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import java.util.HashMap; import db.buffers.BufferMgr; import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; /** * The NodeMgr manages all database nodes associated with @@ -30,6 +29,25 @@ import db.buffers.DataBuffer; * buffer allocations, retrievals and releases as required. The NodeMgr * also performs hard caching of all buffers until the releaseNodes * method is invoked. + * + * Legacy Issues (prior to Ghidra 9.2): + *
    + *
  • Legacy {@link Table} implementation incorrectly employed {@link VarKeyNode} + * storage with primitive fixed-length primary keys other than {@link LongField} + * (e.g., {@link ByteField}). With improved support for fixed-length keys + * legacy data poses a backward capatibility issue. This has been + * addressed through the use of a hack whereby a {@link Schema} is forced to + * treat the primary key as variable length + * (see {@link Schema#forceUseOfVariableLengthKeyNodes()}. The detection + * for this rare condition is provided by {@link TableRecord} during + * schema instantiation.
  • + * + *
  • Legacy {@link Table} implementation incorrectly employed variable + * length storage when both primary key and indexed fields were + * LongField types. This issue has been addressed by treating the + * {@link Field#LEGACY_INDEX_LONG_TYPE} (0x8) as variable-length (see + * implementation {@link LegacyIndexField}).
  • + *
*/ class NodeMgr { @@ -70,6 +88,24 @@ class NodeMgr { */ static final byte VARKEY_REC_NODE = 4; + /** + * Node type for fixed-length key interior tree nodes + * @see db.FixedKeyInteriorNode + */ + static final byte FIXEDKEY_INTERIOR_NODE = 5; + + /** + * Node type for fixed-length key variable-length record leaf nodes + * @see db.FixedKeyVarRecNode + */ + static final byte FIXEDKEY_VAR_REC_NODE = 6; + + /** + * Node type for fixed-length key fixed-length record leaf nodes + * @see db.FixedKeyFixedRecNode + */ + static final byte FIXEDKEY_FIXED_REC_NODE = 7; + /** * Node type for chained buffer index nodes * @see db.DBBuffer @@ -84,21 +120,21 @@ class NodeMgr { private BufferMgr bufferMgr; private Schema schema; + private String tableName; private int leafRecordCnt = 0; - private IntObjectHashtable nodeTable = new IntObjectHashtable(10); - -// private ArrayList nodeList = new ArrayList(10); + private HashMap nodeTable = new HashMap<>(); /** * Construct a node manager for a specific table. + * @param table associated table * @param bufferMgr buffer manager. - * @param schema table schema (required for Table use) */ - NodeMgr(BufferMgr bufferMgr, Schema schema) { + NodeMgr(Table table, BufferMgr bufferMgr) { this.bufferMgr = bufferMgr; - this.schema = schema; + this.schema = table.getSchema(); + this.tableName = table.getName(); } /** @@ -109,21 +145,36 @@ class NodeMgr { return bufferMgr; } + /** + * Get the table schema associated with this node manager + * @return table schema + */ + Schema getTableSchema() { + return schema; + } + + /** + * Get the table name associated with this node manager + * @return table name + */ + String getTableName() { + return tableName; + } + /** * Release all nodes held by this node manager. * This method must be invoked before a database transaction can be committed. * @return the change in record count (+/-) + * @throws IOException if IO error occurs on database */ int releaseNodes() throws IOException { - int[] bufferIds = nodeTable.getKeys(); - for (int bufferId : bufferIds) { - BTreeNode node = nodeTable.get(bufferId); - if (node instanceof LongKeyRecordNode || node instanceof VarKeyRecordNode) { + for (BTreeNode node : nodeTable.values()) { + if (node instanceof RecordNode) { leafRecordCnt -= node.getKeyCount(); } bufferMgr.releaseBuffer(node.getBuffer()); } - nodeTable.removeAll(); + nodeTable = new HashMap<>(); int result = -leafRecordCnt; leafRecordCnt = 0; return result; @@ -133,8 +184,8 @@ class NodeMgr { * Release a specific read-only buffer node. * WARNING! This method may only be used to release read-only buffers, * if a release buffer has been modified an IOException will be thrown. - * @param bufferId - * @throws IOException + * @param bufferId buffer ID + * @throws IOException if IO error occurs on database */ void releaseReadOnlyNode(int bufferId) throws IOException { BTreeNode node = nodeTable.get(bufferId); @@ -142,7 +193,7 @@ class NodeMgr { // There is a possible leafRecordCount error if buffer is released multiple times throw new IOException("Releasing modified buffer node as read-only"); } - if (node instanceof LongKeyRecordNode || node instanceof VarKeyRecordNode) { + if (node instanceof RecordNode) { leafRecordCnt -= node.getKeyCount(); } bufferMgr.releaseBuffer(node.getBuffer()); @@ -170,11 +221,32 @@ class NodeMgr { bufferMgr.deleteBuffer(bufferId); } + /** + * Perform a test of the specified buffer to determine if it is + * a VarKeyNode type. It is important that the specified buffer + * not be in use. + * @param bufferMgr buffer manager + * @param bufferId buffer ID + * @return true if node found and is a VarKeyNode type + * @throws IOException thrown if an IO error occurs + */ + static boolean isVarKeyNode(BufferMgr bufferMgr, int bufferId) throws IOException { + DataBuffer buf = bufferMgr.getBuffer(bufferId); + try { + int nodeType = getNodeType(buf); + return nodeType == VARKEY_REC_NODE || nodeType == VARKEY_INTERIOR_NODE; + } + finally { + bufferMgr.releaseBuffer(buf); + } + } + /** * Get a LongKeyNode object for a specified buffer * @param bufferId buffer ID * @return LongKeyNode instance * @throws ClassCastException if node type is incorrect. + * @throws IOException if IO error occurs on database */ LongKeyNode getLongKeyNode(int bufferId) throws IOException { LongKeyNode node = (LongKeyNode) nodeTable.get(bufferId); @@ -197,8 +269,44 @@ class NodeMgr { node = new LongKeyInteriorNode(this, buf); break; default: - throw new AssertException("Unexpected Node Type (" + nodeType + - ") found, expecting LongKeyNode"); + bufferMgr.releaseBuffer(buf); + throw new AssertException( + "Unexpected Node Type (" + nodeType + ") found, expecting LongKeyNode"); + } + return node; + } + + /** + * Get a FixedKeyNode object for a specified buffer + * @param bufferId buffer ID + * @return LongKeyNode instance + * @throws ClassCastException if node type is incorrect. + * @throws IOException if IO error occurs on database + */ + FixedKeyNode getFixedKeyNode(int bufferId) throws IOException { + FixedKeyNode node = (FixedKeyNode) nodeTable.get(bufferId); + if (node != null) { + return node; + } + + DataBuffer buf = bufferMgr.getBuffer(bufferId); + int nodeType = getNodeType(buf); + switch (nodeType) { + case FIXEDKEY_VAR_REC_NODE: + node = new FixedKeyVarRecNode(this, buf); + leafRecordCnt += node.keyCount; + break; + case FIXEDKEY_FIXED_REC_NODE: + node = new FixedKeyFixedRecNode(this, buf); + leafRecordCnt += node.keyCount; + break; + case FIXEDKEY_INTERIOR_NODE: + node = new FixedKeyInteriorNode(this, buf); + break; + default: + bufferMgr.releaseBuffer(buf); + throw new IOException( + "Unexpected Node Type (" + nodeType + ") found, expecting FixedKeyNode"); } return node; } @@ -208,6 +316,7 @@ class NodeMgr { * @param bufferId buffer ID * @return VarKeyNode instance * @throws ClassCastException if node type is incorrect. + * @throws IOException if IO error occurs on database */ VarKeyNode getVarKeyNode(int bufferId) throws IOException { VarKeyNode node = (VarKeyNode) nodeTable.get(bufferId); @@ -226,8 +335,9 @@ class NodeMgr { node = new VarKeyInteriorNode(this, buf); break; default: - throw new AssertException("Unexpected Node Type (" + nodeType + - ") found, expecting VarKeyNode"); + bufferMgr.releaseBuffer(buf); + throw new AssertException( + "Unexpected Node Type (" + nodeType + ") found, expecting VarKeyNode"); } return node; } diff --git a/Ghidra/Framework/DB/src/main/java/db/ObjectStorageAdapterDB.java b/Ghidra/Framework/DB/src/main/java/db/ObjectStorageAdapterDB.java index 7e2e80ec0a..3d106f11b6 100644 --- a/Ghidra/Framework/DB/src/main/java/db/ObjectStorageAdapterDB.java +++ b/Ghidra/Framework/DB/src/main/java/db/ObjectStorageAdapterDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +15,10 @@ */ package db; -import ghidra.util.ObjectStorage; - import java.util.ArrayList; +import ghidra.util.ObjectStorage; + /** * ObjectStorageAdapterDB provides an ObjectStorage * implementation for use by Saveable objects. This allows Saveable objects @@ -29,17 +28,17 @@ import java.util.ArrayList; * using a suitable schema. */ public class ObjectStorageAdapterDB implements ObjectStorage { - + private ArrayList fieldList = new ArrayList(); private int col = 0; private boolean readOnly = false; - + /** * Construct an empty writable storage adapter. */ public ObjectStorageAdapterDB() { } - + /** * Construct a read-only storage adapter from an * existing record. @@ -53,81 +52,63 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#putInt(int) - */ + @Override public void putInt(int value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new IntField(value)); } - /* - * @see ghidra.util.ObjectStorage#putByte(byte) - */ + @Override public void putByte(byte value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new ByteField(value)); } - /* - * @see ghidra.util.ObjectStorage#putShort(short) - */ + @Override public void putShort(short value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new ShortField(value)); } - /* - * @see ghidra.util.ObjectStorage#putLong(long) - */ + @Override public void putLong(long value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new LongField(value)); } - /* - * @see ghidra.util.ObjectStorage#putString(java.lang.String) - */ + @Override public void putString(String value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new StringField(value)); } - /* - * @see ghidra.util.ObjectStorage#putBoolean(boolean) - */ + @Override public void putBoolean(boolean value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BooleanField(value)); } - /* - * @see ghidra.util.ObjectStorage#putFloat(float) - */ + @Override public void putFloat(float value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putDouble(double) - */ + @Override public void putDouble(double value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#getInt() - */ + @Override public int getInt() { try { return fieldList.get(col++).getIntValue(); @@ -137,9 +118,7 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getByte() - */ + @Override public byte getByte() { try { return fieldList.get(col++).getByteValue(); @@ -149,9 +128,7 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getShort() - */ + @Override public short getShort() { try { return fieldList.get(col++).getShortValue(); @@ -161,9 +138,7 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getLong() - */ + @Override public long getLong() { try { return fieldList.get(col++).getLongValue(); @@ -173,9 +148,7 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getBoolean() - */ + @Override public boolean getBoolean() { try { return fieldList.get(col++).getBooleanValue(); @@ -185,9 +158,7 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getString() - */ + @Override public String getString() { try { return fieldList.get(col++).getString(); @@ -197,12 +168,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getFloat() - */ + @Override public float getFloat() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getFloatValue(); } catch (IndexOutOfBoundsException e) { @@ -210,12 +179,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getDouble() - */ + @Override public double getDouble() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getDoubleValue(); } catch (IndexOutOfBoundsException e) { @@ -223,75 +190,59 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#putInts(int[]) - */ + @Override public void putInts(int[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putBytes(byte[]) - */ + @Override public void putBytes(byte[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putShorts(short[]) - */ + @Override public void putShorts(short[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putLongs(long[]) - */ + @Override public void putLongs(long[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putFloats(float[]) - */ + @Override public void putFloats(float[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putDoubles(double[]) - */ + @Override public void putDoubles(double[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#putStrings(java.lang.String[]) - */ + @Override public void putStrings(String[] value) { if (readOnly) throw new IllegalStateException(); fieldList.add(new BinaryCodedField(value)); } - /* - * @see ghidra.util.ObjectStorage#getInts() - */ + @Override public int[] getInts() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getIntArray(); } catch (IndexOutOfBoundsException e) { @@ -299,12 +250,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getBytes() - */ + @Override public byte[] getBytes() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getByteArray(); } catch (IndexOutOfBoundsException e) { @@ -312,12 +261,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getShorts() - */ + @Override public short[] getShorts() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getShortArray(); } catch (IndexOutOfBoundsException e) { @@ -325,12 +272,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getLongs() - */ + @Override public long[] getLongs() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getLongArray(); } catch (IndexOutOfBoundsException e) { @@ -338,12 +283,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getFloats() - */ + @Override public float[] getFloats() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getFloatArray(); } catch (IndexOutOfBoundsException e) { @@ -351,12 +294,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getDoubles() - */ + @Override public double[] getDoubles() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getDoubleArray(); } catch (IndexOutOfBoundsException e) { @@ -364,12 +305,10 @@ public class ObjectStorageAdapterDB implements ObjectStorage { } } - /* - * @see ghidra.util.ObjectStorage#getStrings() - */ + @Override public String[] getStrings() { try { - BinaryCodedField codedField = new BinaryCodedField((BinaryField)fieldList.get(col++)); + BinaryCodedField codedField = new BinaryCodedField((BinaryField) fieldList.get(col++)); return codedField.getStringArray(); } catch (IndexOutOfBoundsException e) { @@ -383,13 +322,13 @@ public class ObjectStorageAdapterDB implements ObjectStorage { * @return Schema */ public Schema getSchema(int version) { - Class[] fieldClasses = new Class[fieldList.size()]; - String[] fieldNames = new String[fieldClasses.length]; - for (int i = 0; i < fieldClasses.length; i++) { - fieldClasses[i] = fieldList.get(i).getClass(); + Field[] fields = new Field[fieldList.size()]; + String[] fieldNames = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + fields[i] = fieldList.get(i).newField(); fieldNames[i] = Integer.toString(i); } - return new Schema(version, "key", fieldClasses, fieldNames); + return new Schema(version, "key", fields, fieldNames); } /** diff --git a/Ghidra/Framework/DB/src/main/java/db/Record.java b/Ghidra/Framework/DB/src/main/java/db/Record.java index f757ac5e0f..e60a8639d3 100644 --- a/Ghidra/Framework/DB/src/main/java/db/Record.java +++ b/Ghidra/Framework/DB/src/main/java/db/Record.java @@ -29,15 +29,15 @@ import ghidra.util.exception.AssertException; * */ public class Record implements Comparable { - + private Field key; private Field[] fieldValues; private boolean dirty = false; - + private int length = -1; private boolean isVariableLength; - + /** * Construct a new record. * The schema is derived from the field values supplied. @@ -48,7 +48,7 @@ public class Record implements Comparable { this.key = key; this.fieldValues = fieldValues; } - + /** * Set the primary key associated with this record. * @param key primary key @@ -56,9 +56,9 @@ public class Record implements Comparable { public void setKey(long key) { if (!(this.key instanceof LongField)) throw new AssertException(); - this.key = new LongField(key); + this.key = new LongField(key); } - + /** * Set the primary key associated with this record. * @param key primary key @@ -66,9 +66,9 @@ public class Record implements Comparable { public void setKey(Field key) { if (!this.key.getClass().equals(key.getClass())) throw new AssertException(); - this.key = key; + this.key = key; } - + /** * Get the record primary key. * @return primary key as long value. @@ -76,7 +76,7 @@ public class Record implements Comparable { public long getKey() { return key.getLongValue(); } - + /** * Get the record primary key as a Field object. * @return primary key as a field object. @@ -84,7 +84,7 @@ public class Record implements Comparable { public Field getKeyField() { return key; } - + /** * Determine if this record's schema is the same as another record's * schema. This check factors column count and column field types only. @@ -107,17 +107,19 @@ public class Record implements Comparable { /** * Determine if this record's schema is compatible with the specified schema. * This check factors column count and column field types only. - * @param schema + * @param schema other schema * @return true if records schemas are the same */ public boolean hasSameSchema(Schema schema) { - if (fieldValues.length != schema.getFieldCount()) { return false; } - Class[] schemaFieldClasses = schema.getFieldClasses(); + if (!key.isSameType(schema.getKeyFieldType())) { + return false; + } + Field[] otherFields = schema.getFields(); for (int i = 0; i < fieldValues.length; i++) { - if (!fieldValues[i].getClass().equals(schemaFieldClasses[i])) { + if (!fieldValues[i].isSameType(otherFields[i])) { return false; } } @@ -131,7 +133,7 @@ public class Record implements Comparable { public int getColumnCount() { return fieldValues.length; } - + /** * Get a copy of the specified field value. * @param columnIndex @@ -139,9 +141,9 @@ public class Record implements Comparable { */ public Field getFieldValue(int columnIndex) { Field f = fieldValues[columnIndex]; - return f.newField(f); + return f.copyField(); } - + /** * Set the field value for the specified field. * @param colIndex field index @@ -153,7 +155,7 @@ public class Record implements Comparable { } fieldValues[colIndex] = value; } - + /** * Get the specified field. The object returned must not be * modified. @@ -163,7 +165,7 @@ public class Record implements Comparable { Field getField(int columnIndex) { return fieldValues[columnIndex]; } - + /** * Get all fields. The objects returned must not be * modified. @@ -172,7 +174,7 @@ public class Record implements Comparable { Field[] getFields() { return fieldValues; } - + /** * Determine if the specified field equals the field associated with the * specified columnIndex. @@ -183,7 +185,7 @@ public class Record implements Comparable { public boolean fieldEquals(int columnIndex, Field field) { return fieldValues[columnIndex].equals(field); } - + /** * Compare two field values. * @param columnIndex the field index within this record @@ -195,22 +197,22 @@ public class Record implements Comparable { public int compareFieldTo(int columnIndex, Field value) { return fieldValues[columnIndex].compareTo(value); } - + /** * Obtain a copy of this record object. * @return Record */ public Record copy() { - - Field newKey = key.newField(key); + + Field newKey = key.copyField(); Field[] fields = new Field[fieldValues.length]; for (int i = 0; i < fields.length; i++) { Field f = fieldValues[i]; - fields[i] = f.newField(f); + fields[i] = f.copyField(); } return new Record(newKey, fields); } - + /** * Get the stored record length. * This method is used to determine the space required to store the data @@ -228,7 +230,7 @@ public class Record implements Comparable { } return length; } - + /** * Get the long value for the specified field. * @param colIndex field index @@ -238,7 +240,7 @@ public class Record implements Comparable { public long getLongValue(int colIndex) { return fieldValues[colIndex].getLongValue(); } - + /** * Set the long value for the specified field. * @param colIndex field index @@ -249,7 +251,7 @@ public class Record implements Comparable { dirty = true; fieldValues[colIndex].setLongValue(value); } - + /** * Get the integer value for the specified field. * @param colIndex field index @@ -270,7 +272,7 @@ public class Record implements Comparable { dirty = true; fieldValues[colIndex].setIntValue(value); } - + /** * Get the short value for the specified field. * @param colIndex field index @@ -280,7 +282,7 @@ public class Record implements Comparable { public short getShortValue(int colIndex) { return fieldValues[colIndex].getShortValue(); } - + /** * Set the short value for the specified field. * @param colIndex field index @@ -291,7 +293,7 @@ public class Record implements Comparable { dirty = true; fieldValues[colIndex].setShortValue(value); } - + /** * Get the byte value for the specified field. * @param colIndex field index @@ -301,7 +303,7 @@ public class Record implements Comparable { public byte getByteValue(int colIndex) { return fieldValues[colIndex].getByteValue(); } - + /** * Set the byte value for the specified field. * @param colIndex field index @@ -322,7 +324,7 @@ public class Record implements Comparable { public boolean getBooleanValue(int colIndex) { return fieldValues[colIndex].getBooleanValue(); } - + /** * Set the boolean value for the specified field. * @param colIndex field index @@ -333,7 +335,7 @@ public class Record implements Comparable { dirty = true; fieldValues[colIndex].setBooleanValue(value); } - + /** * Get the binary data array for the specified field. * @param colIndex field index @@ -343,19 +345,20 @@ public class Record implements Comparable { public byte[] getBinaryData(int colIndex) { return fieldValues[colIndex].getBinaryData(); } - + /** * Set the binary data array for the specified field. * @param colIndex field index * @param bytes field value * @throws IllegalFieldAccessException if field does support binary data access + * or incorrect number of bytes provided */ public void setBinaryData(int colIndex, byte[] bytes) { dirty = true; length = -1; fieldValues[colIndex].setBinaryData(bytes); } - + /** * Get the string value for the specified field. * @param colIndex field index @@ -365,7 +368,7 @@ public class Record implements Comparable { public String getString(int colIndex) { return fieldValues[colIndex].getString(); } - + /** * Set the string value for the specified field. * @param colIndex field index @@ -390,7 +393,7 @@ public class Record implements Comparable { } dirty = false; } - + /** * Read the record field data from the specified buffer and offset * @param buf data buffer @@ -403,7 +406,7 @@ public class Record implements Comparable { } dirty = false; } - + /** * Determine if data fields have been modified since the last write * occurred. @@ -412,23 +415,24 @@ public class Record implements Comparable { public boolean isDirty() { return dirty; } - + @Override public int hashCode() { return key.hashCode(); } + /** * Compare the content of two Records for equality. * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(Object obj) { + public boolean equals(Object obj) { if (!(obj instanceof Record)) return false; Record rec = (Record) obj; return key.equals(rec.key) && Arrays.equals(fieldValues, rec.fieldValues); } - + /** * Compares the key associated with this record with the * key of another record (obj). @@ -438,5 +442,10 @@ public class Record implements Comparable { public int compareTo(Record otherRec) { return key.compareTo(otherRec.key); } - + + @Override + public String toString() { + return "{key:" + key + "}"; + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/RecordNode.java b/Ghidra/Framework/DB/src/main/java/db/RecordNode.java new file mode 100644 index 0000000000..3aa56233e6 --- /dev/null +++ b/Ghidra/Framework/DB/src/main/java/db/RecordNode.java @@ -0,0 +1,42 @@ +/* ### + * 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 db; + +import java.io.IOException; + +/** + * {@link Table} record leaf nodes within the BTree structure. + */ +public interface RecordNode extends BTreeNode { + + /** + * Get the record offset within the node's data buffer + * @param index key/record index + * @return positive record offset within buffer, or a negative bufferID for + * indirect record storage in a dedicated buffer + * @throws IOException if IO error occurs + */ + int getRecordOffset(int index) throws IOException; + + /** + * Get the key offset within the node's data buffer + * @param index key/record index + * @return positive record offset within buffer + * @throws IOException if IO error occurs + */ + int getKeyOffset(int index) throws IOException; + +} diff --git a/Ghidra/Framework/DB/src/main/java/db/Schema.java b/Ghidra/Framework/DB/src/main/java/db/Schema.java index 6465512cab..6e9502c221 100644 --- a/Ghidra/Framework/DB/src/main/java/db/Schema.java +++ b/Ghidra/Framework/DB/src/main/java/db/Schema.java @@ -33,42 +33,42 @@ public class Schema { private Field keyType; private String keyName; - private Class[] fieldClasses; + private Field[] fields; private String[] fieldNames; private boolean isVariableLength; private int fixedLength; + private boolean forceUseVariableLengthKeyNodes; + /** * Construct a new Schema. - * @param version - * @param keyFieldClass Field class associated with primary key. If the - * class is LongField, the long key methods on Table must be used. Specifying any - * other Field class requires the use of the Field key methods on Table. - * @param keyName - * @param fieldClasses - * @param fieldNames + * @param version schema version + * @param keyField field associated with primary key (representative instance) + * @param keyName primary key name + * @param fields array of column fields (representative instances) + * @param fieldNames array of column field names + * @throws IllegalArgumentException invalid parameters */ - public Schema(int version, Class keyFieldClass, String keyName, - Class[] fieldClasses, String[] fieldNames) { + public Schema(int version, Field keyField, String keyName, Field[] fields, + String[] fieldNames) { this.version = version; - this.keyType = getField(keyFieldClass); + this.keyType = keyField; this.keyName = keyName; - this.fieldClasses = new Class[fieldClasses.length]; + this.fields = fields; this.fieldNames = fieldNames; - if (fieldClasses.length != fieldNames.length) - throw new IllegalArgumentException(); + if (fields.length != fieldNames.length) + throw new IllegalArgumentException("fieldNames and fields lengths differ"); isVariableLength = false; fixedLength = 0; - for (int i = 0; i < fieldClasses.length; i++) { - this.fieldClasses[i] = fieldClasses[i]; - Field field = getField(fieldClasses[i]); + for (int colIndex = 0; colIndex < fields.length; colIndex++) { + Field field = fields[colIndex]; if (field.isVariableLength()) { isVariableLength = true; } fixedLength += field.length(); - if (fieldNames[i].indexOf(NAME_SEPARATOR) >= 0) - throw new IllegalArgumentException(); + if (fieldNames[colIndex].indexOf(NAME_SEPARATOR) >= 0) + throw new IllegalArgumentException("field names may not contain ';'"); } if (isVariableLength) { fixedLength = 0; @@ -76,46 +76,96 @@ public class Schema { } /** - * Construct a new Schema which uses a long key. The Field key methods on Table - * should not be used. - * @param version - * @param keyName - * @param fieldClasses - * @param fieldNames + * Construct a new Schema which uses a long key. + * @param version schema version + * @param keyName primary key name + * @param fields array of column fields (representative instances) + * @param fieldNames array of column field names + * @throws IllegalArgumentException invalid parameters + */ + public Schema(int version, String keyName, Field[] fields, String[] fieldNames) { + this(version, LongField.INSTANCE, keyName, fields, fieldNames); + } + + /** + * Construct a new Schema. + * @param version schema version + * @param keyClass field class associated with primary key + * @param keyName primary key name + * @param fieldClasses array of column field classes + * @param fieldNames array of column field names + * @throws IllegalArgumentException invalid parameters + */ + public Schema(int version, Class keyClass, String keyName, Class[] fieldClasses, + String[] fieldNames) { + this(version, getField(keyClass), keyName, getFields(fieldClasses), fieldNames); + } + + /** + * Construct a new Schema which uses a long key. + * @param version schema version + * @param keyName primary key name + * @param fieldClasses array of column field classes + * @param fieldNames array of column field names + * @throws IllegalArgumentException invalid parameters */ public Schema(int version, String keyName, Class[] fieldClasses, String[] fieldNames) { - this(version, LongField.class, keyName, fieldClasses, fieldNames); + this(version, LongField.INSTANCE, keyName, getFields(fieldClasses), fieldNames); } /** * Construct a new Schema with the given number of columns - * @param version - * @param fieldTypes + * @param version schema version + * @param encodedKeyFieldType key field type + * @param encodedFieldTypes encoded field types array. * @param packedFieldNames packed list of field names separated by ';'. * The first field name corresponds to the key name. * @throws UnsupportedFieldException if unsupported fieldType specified */ - Schema(int version, byte keyFieldType, byte[] fieldTypes, String packedFieldNames) + Schema(int version, byte encodedKeyFieldType, byte[] encodedFieldTypes, String packedFieldNames) throws UnsupportedFieldException { this.version = version; - this.keyType = Field.getField(keyFieldType); + this.keyType = Field.getField(encodedKeyFieldType); parseNames(packedFieldNames); - if (fieldTypes.length != fieldNames.length) - throw new IllegalArgumentException(); - this.fieldClasses = new Class[fieldTypes.length]; isVariableLength = false; fixedLength = 0; - for (int i = 0; i < fieldTypes.length; i++) { - Field field = Field.getField(fieldTypes[i]); - fieldClasses[i] = field.getClass(); - if (field.isVariableLength()) { + fields = new Field[encodedFieldTypes.length]; + for (int i = 0; i < encodedFieldTypes.length; i++) { + byte b = encodedFieldTypes[i]; + Field f = Field.getField(b); + fields[i] = f; + if (f.isVariableLength()) { isVariableLength = true; } - fixedLength += field.length(); + fixedLength += f.length(); } if (isVariableLength) { fixedLength = 0; } + if (fieldNames.length != encodedFieldTypes.length) { + throw new IllegalArgumentException("fieldNames and column types differ in length"); + } + } + + private static Field getField(Class fieldClass) { + if (!Field.class.isAssignableFrom(fieldClass) || fieldClass == Field.class || + IndexField.class.isAssignableFrom(fieldClass)) { + throw new IllegalArgumentException("Invalid Field class: " + fieldClass.getName()); + } + try { + return (Field) fieldClass.getConstructor().newInstance(); + } + catch (Exception e) { + throw new RuntimeException("Failed to construct: " + fieldClass.getName(), e); + } + } + + private static Field[] getFields(Class[] fieldClasses) { + Field[] fields = new Field[fieldClasses.length]; + for (int i = 0; i < fieldClasses.length; i++) { + fields[i] = getField(fieldClasses[i]); + } + return fields; } /** @@ -123,22 +173,43 @@ public class Schema { * @return true if LongKeyNode's can be used to store records produced with this schema. */ boolean useLongKeyNodes() { - return keyType instanceof LongField; + return !forceUseVariableLengthKeyNodes && keyType instanceof LongField; } /** - * Get the key Field class - * @return key Field classes + * Determine if this schema uses VarKeyNode's within a table. + * @return true if VarKeyNode's are be used to store records produced with this schema. */ - public Class getKeyFieldClass() { - return keyType.getClass(); + boolean useVariableKeyNodes() { + return forceUseVariableLengthKeyNodes || keyType.isVariableLength(); + } + + /** + * Determine if this schema can use FixedKeyNode's within a table. + * @return true if FixedKeyNode's can be used to store records produced with this schema. + */ + boolean useFixedKeyNodes() { + return !useVariableKeyNodes() && !useLongKeyNodes(); + } + + /** + * Force use of variable-length key nodes. + *
+ * This method provides a work-around for legacy schemas which + * employ primitive fixed-length keys other than LongField + * and improperly employ a variable-length-key storage schema. + * Although rare, this may be neccessary to ensure backward compatibility + * with legacy DB storage (example ByteField key employed by old table). + */ + void forceUseOfVariableLengthKeyNodes() { + forceUseVariableLengthKeyNodes = true; } /** * Get the Field type for the key. * @return key Field type */ - Field getKeyFieldType() { + public Field getKeyFieldType() { return keyType; } @@ -155,8 +226,8 @@ public class Schema { * The returned list is ordered consistent with the schema definition. * @return data Field classes */ - public Class[] getFieldClasses() { - return fieldClasses; + public Field[] getFields() { + return fields; } /** @@ -173,7 +244,7 @@ public class Schema { * @return data Field count */ public int getFieldCount() { - return fieldClasses.length; + return fields.length; } /** @@ -207,16 +278,21 @@ public class Schema { return buf.toString(); } + byte getEncodedKeyFieldType() { + return keyType.getFieldType(); + } + /** - * Get the schema field types as a byte array. - * @return byte[] field type list + * Get the schema field types as an encoded byte array. + * @return byte[] field type list as an encoded byte array. */ - byte[] getFieldTypes() { - byte[] fieldTypes = new byte[fieldClasses.length]; - for (int i = 0; i < fieldClasses.length; i++) { - fieldTypes[i] = getField(fieldClasses[i]).getFieldType(); + byte[] getEncodedFieldTypes() { + + byte[] encodedFieldTypes = new byte[fields.length]; + for (int colIndex = 0; colIndex < fields.length; colIndex++) { + encodedFieldTypes[colIndex] = fields[colIndex].getFieldType(); } - return fieldTypes; + return encodedFieldTypes; } /** @@ -245,8 +321,8 @@ public class Schema { /** * Create an empty record for the specified key. - * @param key - * @return Record + * @param key long key + * @return new record */ public Record createRecord(long key) { return createRecord(new LongField(key)); @@ -254,21 +330,20 @@ public class Schema { /** * Create an empty record for the specified key. - * @param key + * @param key record key field * @return new record */ public Record createRecord(Field key) { - if (!getKeyFieldClass().equals(key.getClass())) { - throw new IllegalArgumentException( - "expected key field type of " + keyType.getClass().getSimpleName()); + if (!keyType.isSameType(key)) { + throw new IllegalArgumentException("key differs from schema key type"); } - Field[] fieldValues = new Field[fieldClasses.length]; - for (int i = 0; i < fieldClasses.length; i++) { + Field[] fieldValues = new Field[fields.length]; + for (int colIndex = 0; colIndex < fields.length; colIndex++) { try { - fieldValues[i] = (Field) fieldClasses[i].newInstance(); + fieldValues[colIndex] = fields[colIndex].newField(); } catch (Exception e) { - throw new AssertException(); + throw new AssertException(e); } } return new Record(key, fieldValues); @@ -281,48 +356,13 @@ public class Schema { */ Field getField(int colIndex) { try { - return (Field) fieldClasses[colIndex].newInstance(); + return fields[colIndex].newField(); } catch (Exception e) { throw new AssertException(e.getMessage()); } } - /** - * Get a new instance of a data Field object for the specified Field class. - * @param fieldClass Field implementation class - * @return new Field object suitable for data reading/writing. - */ - private Field getField(Class fieldClass) { - try { - return (Field) fieldClass.newInstance(); - } - catch (Exception e) { - throw new AssertException(e.getMessage()); - } - } - - /** - * Compare two schemas for equality. - * Field names are ignored in this comparison. - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Schema)) - return false; - Schema otherSchema = (Schema) obj; - if (version != otherSchema.version || - !keyType.getClass().equals(otherSchema.keyType.getClass()) || - fieldClasses.length != otherSchema.fieldClasses.length) - return false; - for (int i = 0; i < fieldClasses.length; i++) { - if (!fieldClasses[i].getClass().equals(otherSchema.fieldClasses[i].getClass())) - return false; - } - return true; - } - @Override public String toString() { StringBuilder buf = new StringBuilder(); @@ -334,7 +374,7 @@ public class Schema { buf.append("\n"); buf.append(fieldNames[i]); buf.append("("); - buf.append(fieldClasses[i].getSimpleName()); + buf.append(fields[i].getClass().getSimpleName()); buf.append(")"); } return buf.toString(); diff --git a/Ghidra/Framework/DB/src/main/java/db/ShortField.java b/Ghidra/Framework/DB/src/main/java/db/ShortField.java index ddcc4eb2f5..33697bdf3e 100644 --- a/Ghidra/Framework/DB/src/main/java/db/ShortField.java +++ b/Ghidra/Framework/DB/src/main/java/db/ShortField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,30 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; +import db.buffers.DataBuffer; + /** * ShortField provides a wrapper for 2-byte signed short data * which is read or written to a Record. */ -public class ShortField extends Field { +public final class ShortField extends Field { + + /** + * Minimum short field value + */ + public static final ShortField MIN_VALUE = new ShortField(Short.MIN_VALUE, true); + + /** + * Maximum short field value + */ + public static final ShortField MAX_VALUE = new ShortField(Short.MAX_VALUE, true); + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final ShortField INSTANCE = MIN_VALUE; private short value; @@ -39,69 +53,57 @@ public class ShortField extends Field { * @param s initial value */ public ShortField(short s) { + this(s, false); + } + + /** + * Construct a short field with an initial value of s. + * @param s initial value + * @param immutable true if field value is immutable + */ + ShortField(short s, boolean immutable) { + super(immutable); value = s; } - /* - * @see ghidra.framework.store.db.Field#getShortValue() - */ @Override public short getShortValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#setShortValue(short) - */ @Override public void setShortValue(short value) { + checkImmutable(); this.value = value; } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return 2; } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { return buf.putShort(offset, value); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); value = buf.getShort(offset); return offset + 2; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { return 2; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return SHORT_TYPE; } - /* - * @see java.lang.Object#toString() - */ @Override public String toString() { return "ShortField: " + Short.toString(value); @@ -109,12 +111,9 @@ public class ShortField extends Field { @Override public String getValueAsString() { - return Integer.toHexString(value); + return "0x" + Integer.toHexString(value); } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof ShortField)) @@ -122,9 +121,6 @@ public class ShortField extends Field { return ((ShortField) obj).value == value; } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { ShortField f = (ShortField) o; @@ -135,54 +131,63 @@ public class ShortField extends Field { return 1; } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue.isVariableLength()) - throw new AssertException(); - return new ShortField((short) fieldValue.getLongValue()); + int compareTo(DataBuffer buffer, int offset) { + short otherValue = buffer.getShort(offset); + if (value == otherValue) + return 0; + else if (value < otherValue) + return -1; + return 1; } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public ShortField copyField() { + return new ShortField((short) getLongValue()); + } + + @Override + public ShortField newField() { return new ShortField(); } - /* - * @see ghidra.framework.store.db.Field#getLongValue() - */ @Override public long getLongValue() { return value; } - /* - * @see ghidra.framework.store.db.Field#setLongValue(long) - */ @Override public void setLongValue(long value) { - this.value = (short) value; + setShortValue((short) value); } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return new byte[] { (byte) (value >> 8), (byte) value }; } - /* - * @see java.lang.Object#hashCode() - */ + @Override + public void setBinaryData(byte[] bytes) { + checkImmutable(); + if (bytes.length != 2) { + throw new IllegalFieldAccessException(); + } + value = (short) (((bytes[0] & 0xff) << 8) | (bytes[1] & 0xff)); + } + @Override public int hashCode() { return value; } + @Override + ShortField getMinValue() { + return MIN_VALUE; + } + + @Override + ShortField getMaxValue() { + return MAX_VALUE; + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/StringField.java b/Ghidra/Framework/DB/src/main/java/db/StringField.java index 9e7699628c..d8b2cb2882 100644 --- a/Ghidra/Framework/DB/src/main/java/db/StringField.java +++ b/Ghidra/Framework/DB/src/main/java/db/StringField.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,22 @@ */ package db; -import ghidra.util.exception.AssertException; - import java.io.IOException; import java.io.UnsupportedEncodingException; +import db.buffers.DataBuffer; +import ghidra.util.exception.AssertException; + /** * StringField provides a wrapper for variable length String data which is read or * written to a Record. Strings are always encoded as UTF-8. */ -public class StringField extends Field { +public final class StringField extends Field { + + /** + * Instance intended for defining a {@link Table} {@link Schema} + */ + public static final StringField INSTANCE = new StringField(null, true); private static String ENCODING = "UTF-8"; @@ -40,45 +45,48 @@ public class StringField extends Field { /** * Construct a String field with an initial value of s. - * @param s initial value + * @param str initial string value or null */ - public StringField(String s) { - setString(s); + public StringField(String str) { + this(str, false); } - /* - * @see ghidra.framework.store.db.Field#getString() + /** + * Construct a String field with an initial value of s. + * @param str initial string value or null + * @param immutable true if field value is immutable */ + StringField(String str, boolean immutable) { + super(immutable); + doSetString(str); + } + @Override public String getString() { return str; } - /* - * @see ghidra.framework.store.db.Field#setString(java.lang.String) - */ @Override public void setString(String str) { + checkImmutable(); + doSetString(str); + } + + private void doSetString(String str) { this.str = str; try { bytes = (str != null ? str.getBytes(ENCODING) : null); } catch (UnsupportedEncodingException e) { - throw new AssertException(); + throw new AssertException(e); } } - /* - * @see ghidra.framework.store.db.Field#length() - */ @Override int length() { return (bytes == null) ? 4 : (bytes.length + 4); } - /* - * @see ghidra.framework.store.db.Field#write(ghidra.framework.store.Buffer, int) - */ @Override int write(Buffer buf, int offset) throws IOException { if (bytes == null) { @@ -88,11 +96,9 @@ public class StringField extends Field { return buf.put(offset, bytes); } - /* - * @see ghidra.framework.store.db.Field#read(ghidra.framework.store.Buffer, int) - */ @Override int read(Buffer buf, int offset) throws IOException { + checkImmutable(); int len = buf.getInt(offset); offset += 4; if (len < 0) { @@ -107,34 +113,22 @@ public class StringField extends Field { return offset; } - /* - * @see ghidra.framework.store.db.Field#readLength(ghidra.framework.store.Buffer, int) - */ @Override int readLength(Buffer buf, int offset) throws IOException { int len = buf.getInt(offset); return (len < 0 ? 0 : len) + 4; } - /* - * @see ghidra.framework.store.db.Field#isVariableLength() - */ @Override public boolean isVariableLength() { return true; } - /* - * @see ghidra.framework.store.db.Field#getFieldType() - */ @Override - protected byte getFieldType() { + byte getFieldType() { return STRING_TYPE; } - /* - * @see java.lang.Object#toString() - */ @Override public String toString() { return "StringField: " + str; @@ -168,9 +162,6 @@ public class StringField extends Field { // return value; // } - /* - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof StringField)) @@ -182,19 +173,14 @@ public class StringField extends Field { return str.equals(f.str); } - /* - * @see ghidra.framework.store.db.Field#getBinaryData() - */ @Override public byte[] getBinaryData() { return bytes; } - /* - * @see ghidra.framework.store.db.Field#setBinaryData(byte[]) - */ @Override public void setBinaryData(byte[] bytes) { + checkImmutable(); if (bytes == null) { str = null; } @@ -204,14 +190,11 @@ public class StringField extends Field { str = new String(bytes, ENCODING); } catch (UnsupportedEncodingException e) { - throw new AssertException(); + throw new AssertException(e); } } } - /* - * @see ghidra.framework.store.db.Field#truncate(int) - */ @Override void truncate(int length) { int maxLen = length - 4; @@ -220,9 +203,6 @@ public class StringField extends Field { } } - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ @Override public int compareTo(Field o) { StringField f = (StringField) o; @@ -237,36 +217,41 @@ public class StringField extends Field { return str.compareTo(f.str); } - /* - * @see ghidra.framework.store.db.Field#newField(ghidra.framework.store.db.Field) - */ @Override - public Field newField(Field fieldValue) { - if (fieldValue instanceof StringField) { - return new StringField(fieldValue.getString()); - } + int compareTo(DataBuffer buffer, int offset) { + StringField f = new StringField(); try { - return new StringField(new String(fieldValue.getBinaryData(), ENCODING)); + f.read(buffer, offset); } - catch (UnsupportedEncodingException e) { + catch (IOException e) { + throw new AssertException(e); // DataBuffer does not throw IOException } - throw new AssertException(); + return compareTo(f); } - /* - * @see ghidra.framework.store.db.Field#newField() - */ @Override - public Field newField() { + public StringField copyField() { + return new StringField(str); + } + + @Override + public StringField newField() { return new StringField(); } - /* - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { return str.hashCode(); } + @Override + StringField getMinValue() { + throw new UnsupportedOperationException(); + } + + @Override + StringField getMaxValue() { + throw new UnsupportedOperationException(); + } + } diff --git a/Ghidra/Framework/DB/src/main/java/db/Table.java b/Ghidra/Framework/DB/src/main/java/db/Table.java index 2c125cd2f0..151480a7e0 100644 --- a/Ghidra/Framework/DB/src/main/java/db/Table.java +++ b/Ghidra/Framework/DB/src/main/java/db/Table.java @@ -36,7 +36,6 @@ public class Table { private TableRecord tableRecord; private Schema schema; - private boolean useLongKeyNodes; private NodeMgr nodeMgr; @@ -63,19 +62,18 @@ public class Table { this.db = db; this.tableRecord = tableRecord; + schema = tableRecord.getSchema(); tableRecord.setTable(this); - schema = tableRecord.getSchema(); - useLongKeyNodes = schema.useLongKeyNodes(); rootBufferId = tableRecord.getRootBufferId(); recordCount = tableRecord.getRecordCount(); maximumKey = tableRecord.getMaxKey(); - nodeMgr = new NodeMgr(db.getBufferMgr(), schema); + nodeMgr = new NodeMgr(this, db.getBufferMgr()); } /** - * Return the database handle used by this table. + * @return the database handle used by this table. */ DBHandle getDBHandle() { return db; @@ -83,11 +81,18 @@ public class Table { /** * Determine if this table uses long keys. - * @return true if this table utilizes long keys. If false, the table uses - * a Field type key. + * @return true if this table utilizes long keys. */ public boolean useLongKeys() { - return useLongKeyNodes; + return schema.useLongKeyNodes(); + } + + /** + * Determine if this table uses FixedField keys. + * @return true if this table utilizes FixedField keys. + */ + public boolean useFixedKeys() { + return schema.useFixedKeyNodes(); } /** @@ -129,6 +134,7 @@ public class Table { /** * Get table statistics. * @return list of diagnostic statistics data for this table and related index tables. + * @throws IOException database IO error */ public TableStatistics[] getAllStatistics() throws IOException { @@ -143,6 +149,23 @@ public class Table { return statList; } + private BTreeNode getBTreeNode(int bufferId) throws IOException { + if (schema.useLongKeyNodes()) { + return nodeMgr.getLongKeyNode(bufferId); + } + if (schema.useFixedKeyNodes()) { + return nodeMgr.getFixedKeyNode(bufferId); + } + return nodeMgr.getVarKeyNode(bufferId); + } + + private FieldKeyNode getFieldKeyNode(int bufferId) throws IOException { + if (schema.useFixedKeyNodes()) { + return nodeMgr.getFixedKeyNode(bufferId); + } + return nodeMgr.getVarKeyNode(bufferId); + } + /** * Accumulate node statistics * @param stats statistics collection object @@ -152,19 +175,12 @@ public class Table { private void accumulateNodeStatistics(TableStatistics stats, int bufferId) throws IOException { if (bufferId < 0) return; - BTreeNode node; - if (useLongKeyNodes) { - node = nodeMgr.getLongKeyNode(bufferId); - } - else { - node = nodeMgr.getVarKeyNode(bufferId); - } - + BTreeNode node = getBTreeNode(bufferId); ++stats.bufferCount; int[] ids = node.getBufferReferences(); - if (node instanceof LongKeyInteriorNode || node instanceof VarKeyInteriorNode) { + if (node instanceof InteriorNode) { ++stats.interiorNodeCnt; for (int id : ids) { accumulateNodeStatistics(stats, id); @@ -219,8 +235,8 @@ public class Table { * Callback method for when a new record is added. * Used for maintaining indexes only. May be called before * the old record is actually inserted. - * @param record - * @throws IOException + * @param record new record which has been added + * @throws IOException thrown if IO error occurs */ void insertedRecord(Record record) throws IOException { // Add secondary index entries for new record @@ -234,9 +250,9 @@ public class Table { * Callback method for when an existing record is modified. * Used for maintaining indexes only. May be called before * the old record is actually updated. - * @param oldRecord - * @param newRecord - * @throws IOException + * @param oldRecord old record + * @param newRecord new record + * @throws IOException thrown if IO error occurs */ void updatedRecord(Record oldRecord, Record newRecord) throws IOException { // Update secondary indexes which have been affected @@ -255,8 +271,8 @@ public class Table { * Callback method for when existing records are deleted. * Used for maintaining indexes only. May be called before * the old record is actually deleted. - * @param oldRecord - * @throws IOException + * @param oldRecord record which has been deleted + * @throws IOException thrown if IO error occurs */ void deletedRecord(Record oldRecord) throws IOException { // Delete secondary index entries @@ -268,9 +284,9 @@ public class Table { /** * Rebuild table and associated indexes to ensure consistent state. - * @param monitor + * @param monitor task monitor * @throws IOException if unable to rebuild - * @throws CancelledException + * @throws CancelledException if task was cancelled */ public void rebuild(TaskMonitor monitor) throws IOException, CancelledException { synchronized (db) { @@ -281,28 +297,21 @@ public class Table { return; try { - BTreeNode rootNode = null; - try { - if (useLongKeyNodes) { - rootNode = nodeMgr.getLongKeyNode(rootBufferId); - } - else { - rootNode = nodeMgr.getVarKeyNode(rootBufferId); - } - } - catch (IOException t) { - throw new IOException("Low level tree consistency error (" + getName() + - "): failed to fetch root buffer: " + t.getMessage()); - } + BTreeNode rootNode = getBTreeNode(rootBufferId); if (!rootNode.isConsistent(getName(), monitor)) { throw new IOException("Low level tree consistency error (" + getName() + "): Unable to rebuild database"); } } + catch (IOException t) { + throw new IOException("Low level tree consistency error (" + getName() + + "): failed to fetch root buffer: " + t.getMessage()); + } finally { nodeMgr.releaseNodes(); } + // Rebuild table indexes try { // Remove all index records for (int indexedColumn : indexedColumns) { @@ -351,9 +360,10 @@ public class Table { /** * Check the consistency of this table and its associated index tables. + * @param monitor task monitor * @return true if consistency check passed, else false - * @throws IOException - * @throws CancelledException + * @throws IOException thrown if IO error occurs + * @throws CancelledException is task was cancelled */ public boolean isConsistent(TaskMonitor monitor) throws IOException, CancelledException { return isConsistent(null, monitor); @@ -370,22 +380,14 @@ public class Table { boolean consistent; try { - BTreeNode rootNode = null; - try { - if (useLongKeyNodes) { - rootNode = nodeMgr.getLongKeyNode(rootBufferId); - } - else { - rootNode = nodeMgr.getVarKeyNode(rootBufferId); - } - } - catch (IOException t) { - Msg.debug(this, "Consistency Error (" + getName() + - "): failed to fetch root buffer: " + t.getMessage()); - return false; - } + BTreeNode rootNode = getBTreeNode(rootBufferId); consistent = rootNode.isConsistent(getName(), monitor); } + catch (IOException t) { + Msg.debug(this, "Consistency Error (" + getName() + + "): failed to fetch root buffer: " + t.getMessage()); + return false; + } finally { nodeMgr.releaseNodes(); } @@ -409,9 +411,10 @@ public class Table { for (int indexedColumn : indexedColumns) { IndexTable indexTable = secondaryIndexes.get(indexedColumn); boolean found = false; - for (long key : indexTable.findPrimaryKeys( - rec.getField(indexTable.getColumnIndex()))) { - if (key == rec.getKey()) { + Field[] keys = + indexTable.findPrimaryKeys(rec.getField(indexTable.getColumnIndex())); + for (Field key : keys) { + if (key.equals(rec.getKeyField())) { found = true; break; } @@ -427,7 +430,8 @@ public class Table { } logIndexConsistencyError( schema.getFieldNames()[indexTable.getColumnIndex()], - "Index table does not reference record key: " + rec.getKey()); + "Index table does not reference record key: " + + rec.getKeyField().getValueAsString()); } } @@ -451,18 +455,18 @@ public class Table { monitor.setMessage("Check Index " + getName() + "." + schema.getFieldNames()[indexTable.getColumnIndex()]); - HashSet keySet = new HashSet<>(); + HashSet keySet = new HashSet<>(); int extra = 0; - DBLongIterator keyIterator = indexTable.keyIterator(); + DBFieldIterator keyIterator = indexTable.keyIterator(); while (keyIterator.hasNext()) { - long key = keyIterator.next(); + Field key = keyIterator.next(); if (getRecord(key) == null) { ++extra; } if (!keySet.add(key)) { logIndexConsistencyError( schema.getFieldNames()[indexTable.getColumnIndex()], - "Index table references duplicate key: " + key); + "Index table references duplicate key: " + key.getValueAsString()); } } if (extra != 0) { @@ -483,6 +487,7 @@ public class Table { /** * Delete all records within this table. + * @throws IOException if IO error occurs */ public void deleteAll() throws IOException { synchronized (db) { @@ -490,15 +495,10 @@ public class Table { if (rootBufferId < 0) return; try { - + BTreeNode rootNode = getBTreeNode(rootBufferId); try { // Delete all records - if (useLongKeyNodes) { - nodeMgr.getLongKeyNode(rootBufferId).delete(); - } - else { - nodeMgr.getVarKeyNode(rootBufferId).delete(); - } + rootNode.delete(); // Delete all index entries for (int indexedColumn : indexedColumns) { @@ -529,8 +529,9 @@ public class Table { /** * Remove the index associated with the specified column. - * @param columnIndex - * @throws IOException + * @param columnIndex column corresponding to the column index which + * should be deleted. + * @throws IOException thrown if IO error occurs */ void removeIndex(int columnIndex) throws IOException { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -560,8 +561,9 @@ public class Table { /** * Change the name of this table - * @param name - * @throws DuplicateNameException + * @param name new table name + * @return true if rename successful + * @throws DuplicateNameException if new table name already exists */ public boolean setName(String name) throws DuplicateNameException { return db.setTableName(getName(), name); @@ -629,7 +631,7 @@ public class Table { * Determine if this table contains a record with the specified key. * @param key record key. * @return true if record exists with key, else false. - * @throws IOException + * @throws IOException thrown if IO error occurs */ public boolean hasRecord(long key) throws IOException { synchronized (db) { @@ -655,14 +657,14 @@ public class Table { */ public boolean hasRecord(Field key) throws IOException { synchronized (db) { - if (useLongKeyNodes) { + if (schema.useLongKeyNodes()) { return hasRecord(key.getLongValue()); } if (rootBufferId < 0) return false; boolean result = false; try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + FieldKeyRecordNode leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); result = leaf.getKeyIndex(key) >= 0; } finally { @@ -704,10 +706,16 @@ public class Table { synchronized (db) { if (rootBufferId < 0) return null; - if (key instanceof LongField) + if (key instanceof LongField) { return getRecord(key.getLongValue()); + } + FieldKeyRecordNode leaf; try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + if (key instanceof FixedField) { + leaf = nodeMgr.getFixedKeyNode(rootBufferId).getLeafNode(key); + return leaf.getRecord(key, schema); + } + leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); return leaf.getRecord(key, schema); } finally { @@ -717,8 +725,8 @@ public class Table { } /** - * Get the first record which has a key value less than the - * specified key. + * Get the record with the maximum key value which is less than + * the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value less than the * specified key, or null if no record was found. @@ -739,8 +747,8 @@ public class Table { } /** - * Get the first record which has a key value less than the - * specified key. + * Get the record with the maximum key value which is less than + * the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value less than the * specified key, or null if no record was found. @@ -753,7 +761,7 @@ public class Table { if (key instanceof LongField) return getRecordBefore(key.getLongValue()); try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + FieldKeyRecordNode leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); return leaf.getRecordBefore(key, schema); } finally { @@ -763,8 +771,8 @@ public class Table { } /** - * Get the first record which has a key value greater than the - * specified key. + * Get the record with the minimum key value which is greater than + * the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value greater than the * specified key, or null if no record was found. @@ -785,8 +793,8 @@ public class Table { } /** - * Get the first record which has a key value greater than the - * specified key. + * Get the record with the minimum key value which is greater than + * the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value greater than the * specified key, or null if no record was found. @@ -799,7 +807,7 @@ public class Table { if (key instanceof LongField) return getRecordAfter(key.getLongValue()); try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + FieldKeyRecordNode leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); return leaf.getRecordAfter(key, schema); } finally { @@ -809,8 +817,8 @@ public class Table { } /** - * Get the first record which has a key value less than or equal to the - * specified key. + * Get the record with the maximum key value which is less than or equal + * to the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value less than or equal to the * specified key, or null if no record was found. @@ -831,8 +839,8 @@ public class Table { } /** - * Get the first record which has a key value less than or equal to the - * specified key. + * Get the record with the maximum key value which is less than or equal + * to the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value less than or equal to the * specified key, or null if no record was found. @@ -845,7 +853,7 @@ public class Table { if (key instanceof LongField) return getRecordAtOrBefore(key.getLongValue()); try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + FieldKeyRecordNode leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); return leaf.getRecordAtOrBefore(key, schema); } finally { @@ -855,8 +863,8 @@ public class Table { } /** - * Get the first record which has a key value greater than or equal to the - * specified key. + * Get the record with the minimum key value which is greater than or equal + * to the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value greater than or equal to the * specified key, or null if no record was found. @@ -877,8 +885,8 @@ public class Table { } /** - * Get the first record which has a key value greater than or equal to the - * specified key. + * Get the record with the minimum key value which is greater than or equal + * to the specified key. * @param key unique key which may or may not exist within the table. * @return the first record which has a key value greater than or equal to the * specified key, or null if no record was found. @@ -891,7 +899,7 @@ public class Table { if (key instanceof LongField) return getRecordAtOrAfter(key.getLongValue()); try { - VarKeyRecordNode leaf = nodeMgr.getVarKeyNode(rootBufferId).getLeafNode(key); + FieldKeyRecordNode leaf = getFieldKeyNode(rootBufferId).getLeafNode(key); return leaf.getRecordAtOrAfter(key, schema); } finally { @@ -908,11 +916,11 @@ public class Table { public void putRecord(Record record) throws IOException { synchronized (db) { db.checkTransaction(); - if (useLongKeyNodes) { + if (schema.useLongKeyNodes()) { putLongKeyRecord(record); } else { - putVarKeyRecord(record); + putFieldKeyRecord(record); } } @@ -920,7 +928,7 @@ public class Table { /** * Store a record which uses a long key - * @param record + * @param record recore to be inserted or updated * @throws IOException throw if an IO Error occurs */ private void putLongKeyRecord(Record record) throws IOException { @@ -969,10 +977,10 @@ public class Table { /** * Store a record which uses a Field key - * @param record + * @param record record to be inserted or updated * @throws IOException throw if an IO Error occurs */ - private void putVarKeyRecord(Record record) throws IOException { + private void putFieldKeyRecord(Record record) throws IOException { // boolean inserted = false; try { @@ -981,17 +989,18 @@ public class Table { // Establish root node ++modCount; - VarKeyNode rootNode = null; + FieldKeyNode rootNode = null; if (rootBufferId < 0) { - rootNode = new VarKeyRecordNode(nodeMgr, schema.getKeyFieldType()); + rootNode = schema.useFixedKeyNodes() ? FixedKeyRecordNode.createRecordNode(nodeMgr) + : new VarKeyRecordNode(nodeMgr, schema.getKeyFieldType()); } else { - rootNode = nodeMgr.getVarKeyNode(rootBufferId); + rootNode = getFieldKeyNode(rootBufferId); } // Put record and update root buffer ID Field recKey = record.getKeyField(); - VarKeyRecordNode leaf = rootNode.getLeafNode(recKey); + FieldKeyRecordNode leaf = rootNode.getLeafNode(recKey); rootNode = leaf.putRecord(record, isIndexed ? this : null); int id = rootNode.getBufferId(); if (rootBufferId != id) { @@ -1026,7 +1035,7 @@ public class Table { if (rootBufferId < 0) return false; - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new IllegalArgumentException("Field key required"); try { ++modCount; @@ -1073,12 +1082,13 @@ public class Table { if (rootBufferId < 0) return false; - if (useLongKeyNodes) - throw new IllegalArgumentException("long key required"); + if (key instanceof LongField) { + return deleteRecord(key.getLongValue()); + } try { ++modCount; - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); - VarKeyRecordNode leaf = rootNode.getLeafNode(key); + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); + FieldKeyRecordNode leaf = rootNode.getLeafNode(key); rootNode = leaf.deleteRecord(key, isIndexed ? this : null); if (rootNode != null) { @@ -1119,7 +1129,7 @@ public class Table { db.checkTransaction(); if (startKey > endKey) throw new IllegalArgumentException(); - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new IllegalArgumentException("Long key required"); boolean result = false; @@ -1243,7 +1253,7 @@ public class Table { db.checkTransaction(); if (startKey.compareTo(endKey) > 0) throw new IllegalArgumentException(); - if (useLongKeyNodes) + if (schema.useLongKeyNodes()) throw new IllegalArgumentException("Field key required"); boolean result = false; @@ -1252,8 +1262,8 @@ public class Table { try { ++modCount; - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); - VarKeyRecordNode leaf = rootNode.getLeafNode(startKey); + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); + FieldKeyRecordNode leaf = rootNode.getLeafNode(startKey); try { // Handle partial first leaf where leftmost key is not deleted @@ -1275,35 +1285,36 @@ public class Table { leaf.remove(index); } result = true; - if (index < leaf.keyCount) { + if (index < leaf.getKeyCount()) { return result; } - VarKeyRecordNode nextLeaf = leaf.getNextLeaf(); + RecordNode nextLeaf = leaf.getNextLeaf(); if (nextLeaf == null) { return result; } - lastKey = nextLeaf.getKey(nextLeaf.keyCount - 1); + lastKey = nextLeaf.getKeyField(nextLeaf.getKeyCount() - 1); leaf = rootNode.getLeafNode(lastKey); index = 0; } else { - lastKey = leaf.getKey(leaf.keyCount - 1); + lastKey = leaf.getKeyField(leaf.getKeyCount() - 1); } // Handle additional whole leaves while (lastKey.compareTo(endKey) <= 0) { if (isIndexed) { - for (int n = 0; n < leaf.keyCount; n++) { + int count = leaf.getKeyCount(); + for (int n = 0; n < count; n++) { deletedRecord(leaf.getRecord(schema, n)); } } - VarKeyRecordNode nextLeaf = leaf.getNextLeaf(); + RecordNode nextLeaf = leaf.getNextLeaf(); rootNode = leaf.removeLeaf(); result = true; if (nextLeaf == null) { return result; } - lastKey = nextLeaf.getKey(nextLeaf.keyCount - 1); + lastKey = nextLeaf.getKeyField(nextLeaf.getKeyCount() - 1); leaf = rootNode.getLeafNode(lastKey); } @@ -1313,7 +1324,7 @@ public class Table { if (lastIndex < 0) { lastIndex = -lastIndex - 2; } - Field key = leaf.getKey(0); + Field key = leaf.getKeyField(0); while (index <= lastIndex--) { if (isIndexed) { deletedRecord(leaf.getRecord(schema, index)); @@ -1321,8 +1332,8 @@ public class Table { leaf.remove(index); result = true; } - if (index == 0 && leaf.parent != null) { - leaf.parent.keyChanged(key, leaf.getKey(0), leaf); + if (index == 0 && leaf.getParent() != null) { + leaf.getParent().keyChanged(key, leaf.getKeyField(0), leaf); } } finally { @@ -1358,14 +1369,15 @@ public class Table { /** * Find the primary keys corresponding to those records which contain the * specified field value in the specified record column. The table must - * have been created with a secondary index on the specified column index. + * have been created with long keys and a secondary index on the specified + * column index. * @param field the field value * @param columnIndex the record schema column which should be searched. * @return list of primary keys * @throws IOException if a secondary index does not exist for the specified * column, or the wrong field type was specified, or an I/O error occurs. */ - public long[] findRecords(Field field, int columnIndex) throws IOException { + public Field[] findRecords(Field field, int columnIndex) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); if (indexTable == null) @@ -1400,7 +1412,7 @@ public class Table { * @param field the field value * @param columnIndex the record schema column which should be searched. * @return true if one or more records exis with the specified value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ public boolean hasRecord(Field field, int columnIndex) throws IOException { synchronized (db) { @@ -1417,7 +1429,7 @@ public class Table { * set to the minimum index value. * @param columnIndex identifies an indexed column. * @return index field iterator. - * @throws IOException + * @throws IOException thrown if IO error occurs */ public DBFieldIterator indexFieldIterator(int columnIndex) throws IOException { synchronized (db) { @@ -1437,7 +1449,7 @@ public class Table { * is after maxField * @param columnIndex identifies an indexed column. * @return index field iterator. - * @throws IOException + * @throws IOException thrown if IO error occurs */ public DBFieldIterator indexFieldIterator(Field minField, Field maxField, boolean before, int columnIndex) throws IOException { @@ -1460,7 +1472,8 @@ public class Table { * is after startField value * @param columnIndex identifies an indexed column. * @return index field iterator. - * @throws IOException + * @throws IOException if a secondary index does not exist for the specified + * column or an I/O error occurs. */ public DBFieldIterator indexFieldIterator(Field minField, Field maxField, Field startField, boolean before, int columnIndex) throws IOException { @@ -1573,7 +1586,7 @@ public class Table { * @throws IOException if a secondary index does not exist for the specified * column, or the wrong field type was specified, or an I/O error occurs. */ - public RecordIterator indexIteratorAfter(int columnIndex, Field startValue, long primaryKey) + public RecordIterator indexIteratorAfter(int columnIndex, Field startValue, Field primaryKey) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -1599,7 +1612,7 @@ public class Table { * @throws IOException if a secondary index does not exist for the specified * column, or the wrong field type was specified, or an I/O error occurs. */ - public RecordIterator indexIteratorBefore(int columnIndex, Field startValue, long primaryKey) + public RecordIterator indexIteratorBefore(int columnIndex, Field startValue, Field primaryKey) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -1616,7 +1629,7 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIterator(int columnIndex) throws IOException { + public DBFieldIterator indexKeyIterator(int columnIndex) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); if (indexTable == null) @@ -1634,7 +1647,7 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIteratorBefore(int columnIndex, Field startField) + public DBFieldIterator indexKeyIteratorBefore(int columnIndex, Field startField) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -1654,7 +1667,7 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIteratorAfter(int columnIndex, Field startField) + public DBFieldIterator indexKeyIteratorAfter(int columnIndex, Field startField) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -1675,8 +1688,8 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIteratorBefore(int columnIndex, Field startField, long primaryKey) - throws IOException { + public DBFieldIterator indexKeyIteratorBefore(int columnIndex, Field startField, + Field primaryKey) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); if (indexTable == null) @@ -1696,8 +1709,8 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIteratorAfter(int columnIndex, Field startField, long primaryKey) - throws IOException { + public DBFieldIterator indexKeyIteratorAfter(int columnIndex, Field startField, + Field primaryKey) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); if (indexTable == null) @@ -1721,7 +1734,7 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIterator(int columnIndex, Field minField, Field maxField, + public DBFieldIterator indexKeyIterator(int columnIndex, Field minField, Field maxField, boolean atMin) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); @@ -1743,8 +1756,8 @@ public class Table { * @return primary key iterator * @throws IOException thrown if IO error occurs */ - public DBLongIterator indexKeyIterator(int columnIndex, LongField minField, LongField maxField, - LongField startField, boolean before) throws IOException { + public DBFieldIterator indexKeyIterator(int columnIndex, Field minField, Field maxField, + Field startField, boolean before) throws IOException { synchronized (db) { IndexTable indexTable = secondaryIndexes.get(columnIndex); if (indexTable == null) @@ -1760,10 +1773,10 @@ public class Table { */ public RecordIterator iterator() throws IOException { synchronized (db) { - if (useLongKeyNodes) { + if (schema.useLongKeyNodes()) { return new LongKeyRecordIterator(); } - return new VarKeyRecordIterator(null, null, null); + return new FieldKeyRecordIterator(null, null, null); } } @@ -1776,7 +1789,7 @@ public class Table { */ public RecordIterator iterator(long startKey) throws IOException { synchronized (db) { - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new IllegalArgumentException("Field key required"); return new LongKeyRecordIterator(Long.MIN_VALUE, Long.MAX_VALUE, startKey); } @@ -1795,7 +1808,7 @@ public class Table { */ public RecordIterator iterator(long minKey, long maxKey, long startKey) throws IOException { synchronized (db) { - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new IllegalArgumentException("Field key required"); return new LongKeyRecordIterator(minKey, maxKey, startKey); } @@ -1810,11 +1823,11 @@ public class Table { */ public RecordIterator iterator(Field startKey) throws IOException { synchronized (db) { - if (useLongKeyNodes) { + if (schema.useLongKeyNodes()) { return new LongKeyRecordIterator(Long.MIN_VALUE, Long.MAX_VALUE, startKey.getLongValue()); } - return new VarKeyRecordIterator(null, null, startKey); + return new FieldKeyRecordIterator(null, null, startKey); } } @@ -1829,13 +1842,13 @@ public class Table { */ public RecordIterator iterator(Field minKey, Field maxKey, Field startKey) throws IOException { synchronized (db) { - if (useLongKeyNodes) { + if (schema.useLongKeyNodes()) { long min = minKey != null ? minKey.getLongValue() : Long.MIN_VALUE; long max = maxKey != null ? maxKey.getLongValue() : Long.MAX_VALUE; long start = startKey != null ? startKey.getLongValue() : min; return new LongKeyRecordIterator(min, max, start); } - return new VarKeyRecordIterator(minKey, maxKey, startKey); + return new FieldKeyRecordIterator(minKey, maxKey, startKey); } } @@ -1846,7 +1859,7 @@ public class Table { */ public DBLongIterator longKeyIterator() throws IOException { synchronized (db) { - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new AssertException(); return new LongKeyIterator(); } @@ -1861,7 +1874,7 @@ public class Table { */ public DBLongIterator longKeyIterator(long startKey) throws IOException { synchronized (db) { - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new AssertException(); return new LongKeyIterator(Long.MIN_VALUE, Long.MAX_VALUE, startKey); } @@ -1879,7 +1892,7 @@ public class Table { public DBLongIterator longKeyIterator(long minKey, long maxKey, long startKey) throws IOException { synchronized (db) { - if (!useLongKeyNodes) + if (!schema.useLongKeyNodes()) throw new AssertException(); return new LongKeyIterator(minKey, maxKey, startKey); } @@ -1892,9 +1905,9 @@ public class Table { */ public DBFieldIterator fieldKeyIterator() throws IOException { synchronized (db) { - if (useLongKeyNodes) + if (schema.useLongKeyNodes()) throw new AssertException(); - return new VarKeyIterator(null, null, null); + return new FieldKeyIterator(null, null, null); } } @@ -1907,9 +1920,9 @@ public class Table { */ public DBFieldIterator fieldKeyIterator(Field startKey) throws IOException { synchronized (db) { - if (useLongKeyNodes) + if (schema.useLongKeyNodes()) throw new AssertException(); - return new VarKeyIterator(null, null, startKey); + return new FieldKeyIterator(null, null, startKey); } } @@ -1926,9 +1939,9 @@ public class Table { public DBFieldIterator fieldKeyIterator(Field minKey, Field maxKey, Field startKey) throws IOException { synchronized (db) { - if (useLongKeyNodes) + if (schema.useLongKeyNodes()) throw new AssertException(); - return new VarKeyIterator(minKey, maxKey, startKey); + return new FieldKeyIterator(minKey, maxKey, startKey); } } @@ -1945,9 +1958,9 @@ public class Table { public DBFieldIterator fieldKeyIterator(Field minKey, Field maxKey, boolean before) throws IOException { synchronized (db) { - if (useLongKeyNodes) + if (schema.useLongKeyNodes()) throw new AssertException(); - return new VarKeyIterator(minKey, maxKey, before); + return new FieldKeyIterator(minKey, maxKey, before); } } @@ -1974,9 +1987,8 @@ public class Table { private int expectedModCount; /** - * Construct a record iterator. - * @param startKey the first primary key value. - * @throws IOException + * Construct a record iterator over all records. + * @throws IOException thrown if IO error occurs */ LongKeyRecordIterator() throws IOException { this(Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE); @@ -1988,7 +2000,7 @@ public class Table { * @param minKey minimum allowed primary key. * @param maxKey maximum allowed primary key. * @param startKey the first primary key value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ LongKeyRecordIterator(long minKey, long maxKey, long startKey) throws IOException { @@ -2065,11 +2077,11 @@ public class Table { * the current position will be set to the previous record and isPrev set to true; * else if false and the current record no longer exists, the current position * will be set to the next record and isNext set to true. - * @return VarKeyRecordNode the leaf node containing the current record position + * @return LongKeyRecordNode the leaf node containing the current record position * identified by bufferId and recordIndex. If null, the current record was not found * or the position could not be set to a next/previous record position based upon the * recoverPrev value specified. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private LongKeyRecordNode getRecordLeaf(boolean recoverPrev) throws IOException { @@ -2121,9 +2133,6 @@ public class Table { return leaf; } - /** - * @see db.RecordIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -2169,9 +2178,6 @@ public class Table { } } - /** - * @see db.RecordIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -2217,9 +2223,6 @@ public class Table { } } - /** - * @see db.RecordIterator#next() - */ @Override public Record next() throws IOException { if (hasNext || hasNext()) { @@ -2231,9 +2234,6 @@ public class Table { return null; } - /** - * @see db.RecordIterator#previous() - */ @Override public Record previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -2245,9 +2245,6 @@ public class Table { return null; } - /** - * @see db.RecordIterator#delete() - */ @Override public boolean delete() throws IOException { if (lastRecord == null) @@ -2262,7 +2259,7 @@ public class Table { /** * A RecordIterator class for use with table data contained within LeafNode's. */ - private class VarKeyRecordIterator implements RecordIterator { + private class FieldKeyRecordIterator implements RecordIterator { private int bufferId = -1; // current record buffer ID private int recordIndex; // current record index @@ -2286,9 +2283,9 @@ public class Table { * @param minKey minimum allowed primary key. * @param maxKey maximum allowed primary key. * @param startKey the first primary key value. If null, minKey will be used. - * @throws IOException + * @throws IOException thrown if IO error occurs */ - VarKeyRecordIterator(Field minKey, Field maxKey, Field startKey) throws IOException { + FieldKeyRecordIterator(Field minKey, Field maxKey, Field startKey) throws IOException { expectedModCount = modCount; @@ -2312,11 +2309,11 @@ public class Table { } try { - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); // If startKey not specified, start with leftmost record if (startKey == null) { - VarKeyRecordNode leaf = rootNode.getLeftmostLeafNode(); + FieldKeyRecordNode leaf = rootNode.getLeftmostLeafNode(); bufferId = leaf.getBufferId(); recordIndex = 0; record = leaf.getRecord(schema, 0); @@ -2327,7 +2324,7 @@ public class Table { // else, start with specified startKey else { - VarKeyRecordNode leaf = rootNode.getLeafNode(startKey); + FieldKeyRecordNode leaf = rootNode.getLeafNode(startKey); recordIndex = leaf.getKeyIndex(startKey); // Start key was found @@ -2339,35 +2336,35 @@ public class Table { // Start key was not found else { recordIndex = -(recordIndex + 1); - if (recordIndex == leaf.keyCount) { + if (recordIndex == leaf.getKeyCount()) { --recordIndex; hasPrev = minKey == null ? true - : (leaf.getKey(recordIndex).compareTo(minKey) >= 0); + : (leaf.getKeyField(recordIndex).compareTo(minKey) >= 0); if (!hasPrev) { leaf = leaf.getNextLeaf(); if (leaf == null) return; recordIndex = 0; hasNext = maxKey == null ? true - : (leaf.getKey(recordIndex).compareTo(maxKey) <= 0); + : (leaf.getKeyField(recordIndex).compareTo(maxKey) <= 0); } } else { hasNext = maxKey == null ? true - : (leaf.getKey(recordIndex).compareTo(maxKey) <= 0); + : (leaf.getKeyField(recordIndex).compareTo(maxKey) <= 0); if (!hasNext) { // position to previous record if (recordIndex == 0) { leaf = leaf.getPreviousLeaf(); if (leaf == null) return; - recordIndex = leaf.keyCount - 1; + recordIndex = leaf.getKeyCount() - 1; } else { --recordIndex; } hasPrev = minKey == null ? true - : (leaf.getKey(recordIndex).compareTo(minKey) >= 0); + : (leaf.getKeyField(recordIndex).compareTo(minKey) >= 0); } } } @@ -2393,25 +2390,26 @@ public class Table { * the current position will be set to the previous record and isPrev set to true; * else if false and the current record no longer exists, the current position * will be set to the next record and isNext set to true. - * @return VarKeyRecordNode the leaf node containing the current record position + * @return FieldKeyRecordNode the leaf node containing the current record position * identified by bufferId and recordIndex. If null, the current record was not found * or the position could not be set to a next/previous record position based upon the * recoverPrev value specified. - * @throws IOException + * @throws IOException thrown if IO error occurs */ - private VarKeyRecordNode getRecordLeaf(boolean recoverPrev) throws IOException { + private FieldKeyRecordNode getRecordLeaf(boolean recoverPrev) throws IOException { if (rootBufferId < 0 || record == null) return null; Field key = record.getKeyField(); - VarKeyRecordNode leaf = null; + FieldKeyRecordNode leaf = null; isNext = false; isPrev = false; if (expectedModCount == modCount) { - leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(bufferId); - if (recordIndex >= leaf.keyCount || !leaf.getKey(recordIndex).equals(key)) { + leaf = (FieldKeyRecordNode) getFieldKeyNode(bufferId); + if (recordIndex >= leaf.getKeyCount() || + !leaf.getKeyField(recordIndex).equals(key)) { leaf = null; // something changed - key search required } } @@ -2419,7 +2417,7 @@ public class Table { if (leaf == null) { // Something changed - try to relocate record using key - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); leaf = rootNode.getLeafNode(key); int index = leaf.getKeyIndex(key); if (index < 0) { @@ -2429,12 +2427,12 @@ public class Table { --index; if (index < 0) { leaf = leaf.getPreviousLeaf(); - index = leaf != null ? (leaf.keyCount - 1) : 0; + index = leaf != null ? (leaf.getKeyCount() - 1) : 0; } isPrev = true; } else { - if (index == leaf.keyCount) { + if (index == leaf.getKeyCount()) { leaf = leaf.getNextLeaf(); index = 0; } @@ -2450,9 +2448,6 @@ public class Table { return leaf; } - /** - * @see db.RecordIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -2461,7 +2456,7 @@ public class Table { try { // Check for modification to storage of previous record - VarKeyRecordNode leaf = getRecordLeaf(false); + FieldKeyRecordNode leaf = getRecordLeaf(false); if (leaf == null) return false; @@ -2471,7 +2466,7 @@ public class Table { ++nextIndex; } int nextBufferId = bufferId; - if (nextIndex == leaf.keyCount) { + if (nextIndex == leaf.getKeyCount()) { leaf = leaf.getNextLeaf(); if (leaf == null) return false; @@ -2499,9 +2494,6 @@ public class Table { } } - /** - * @see db.RecordIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -2510,7 +2502,7 @@ public class Table { try { // Check for modification to storage of next record - VarKeyRecordNode leaf = getRecordLeaf(true); + FieldKeyRecordNode leaf = getRecordLeaf(true); if (leaf == null) return false; @@ -2525,7 +2517,7 @@ public class Table { if (leaf == null) return false; prevBufferId = leaf.getBufferId(); - prevIndex = leaf.keyCount - 1; + prevIndex = leaf.getKeyCount() - 1; } // Load previous record @@ -2548,9 +2540,6 @@ public class Table { } } - /** - * @see db.RecordIterator#next() - */ @Override public Record next() throws IOException { if (hasNext || hasNext()) { @@ -2562,9 +2551,6 @@ public class Table { return null; } - /** - * @see db.RecordIterator#previous() - */ @Override public Record previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -2576,9 +2562,6 @@ public class Table { return null; } - /** - * @see db.RecordIterator#delete() - */ @Override public boolean delete() throws IOException { if (lastRecord == null) @@ -2604,9 +2587,8 @@ public class Table { private int iterCnt = 0; /** - * Construct a record iterator. - * @param startKey the first primary key value. - * @throws IOException + * Construct a record iterator over all records. + * @throws IOException thrown if IO error occurs */ LongKeyIterator() throws IOException { keyIter = new LongKeyIterator2(); @@ -2617,15 +2599,12 @@ public class Table { * @param minKey minimum allowed primary key. * @param maxKey maximum allowed primary key. * @param startKey the first primary key value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ LongKeyIterator(long minKey, long maxKey, long startKey) throws IOException { keyIter = new LongKeyIterator1(minKey, maxKey, startKey); } - /* - * @see ghidra.framework.store.db.DBLongIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -2639,9 +2618,6 @@ public class Table { } } - /* - * @see ghidra.framework.store.db.DBLongIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -2655,25 +2631,16 @@ public class Table { } } - /* - * @see ghidra.framework.store.db.DBLongIterator#next() - */ @Override public long next() throws IOException { return keyIter.next(); } - /* - * @see ghidra.framework.store.db.DBLongIterator#previous() - */ @Override public long previous() throws IOException { return keyIter.previous(); } - /* - * @see ghidra.framework.store.db.DBLongIterator#delete() - */ @Override public boolean delete() throws IOException { return keyIter.delete(); @@ -2733,7 +2700,7 @@ public class Table { * @param minKey minimum allowed primary key. * @param maxKey maximum allowed primary key. * @param startKey the first primary key value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ LongKeyIterator1(long minKey, long maxKey, long startKey) throws IOException { @@ -2754,7 +2721,7 @@ public class Table { * following a delete. * @param targetKey the initial key. For construction this is the startKey, * following a delete this is the deleted key. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private void initialize(long targetKey) throws IOException { @@ -2871,9 +2838,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -2920,9 +2884,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -2971,9 +2932,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#next() - */ @Override public long next() throws IOException { if (hasNext || hasNext()) { @@ -2986,9 +2944,6 @@ public class Table { throw new NoSuchElementException(); } - /** - * @see ghidra.framework.store.db.LongIterator#previous() - */ @Override public long previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -3001,9 +2956,6 @@ public class Table { throw new NoSuchElementException(); } - /** - * @see db.DBLongIterator#delete() - */ @Override public boolean delete() throws IOException { if (hasLastKey) { @@ -3052,9 +3004,8 @@ public class Table { private long maxKey; /** - * Construct a record iterator. - * @param startKey the first primary key value. - * @throws IOException + * Construct a record iterator over all records. + * @throws IOException thrown if IO error occurs */ LongKeyIterator2() throws IOException { this(Long.MIN_VALUE, Long.MAX_VALUE, Long.MIN_VALUE); @@ -3066,7 +3017,7 @@ public class Table { * @param minKey minimum allowed primary key. * @param maxKey maximum allowed primary key. * @param startKey the first primary key value. - * @throws IOException + * @throws IOException thrown if IO error occurs */ LongKeyIterator2(long minKey, long maxKey, long startKey) throws IOException { @@ -3087,7 +3038,7 @@ public class Table { * following a delete. * @param targetKey the initial key. For construction this is the startKey, * following a delete this is the deleted key. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private void initialize(long targetKey) throws IOException { @@ -3152,9 +3103,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -3207,9 +3155,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -3262,9 +3207,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.LongIterator#next() - */ @Override public long next() throws IOException { if (hasNext || hasNext()) { @@ -3277,9 +3219,6 @@ public class Table { throw new NoSuchElementException(); } - /** - * @see ghidra.framework.store.db.LongIterator#previous() - */ @Override public long previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -3292,9 +3231,6 @@ public class Table { throw new NoSuchElementException(); } - /** - * @see db.DBLongIterator#delete() - */ @Override public boolean delete() throws IOException { if (hasLastKey) { @@ -3312,7 +3248,7 @@ public class Table { * for a large number of iterations, the underlying iterator is switched * to one optimized for longer iterations. */ - private class VarKeyIterator implements DBFieldIterator { + private class FieldKeyIterator implements DBFieldIterator { private static final int SHORT_ITER_THRESHOLD = 10; @@ -3325,35 +3261,35 @@ public class Table { * @param maxKey maximum key value. Null corresponds to maximum key value. * @param startKey the first primary key value. If null minKey will be assumed, * if still null the minimum indexed value will be assumed. - * @throws IOException + * @throws IOException thrown if IO error occurs */ - VarKeyIterator(Field minKey, Field maxKey, Field startKey) throws IOException { - keyIter = new VarKeyIterator2(minKey, maxKey, startKey); + FieldKeyIterator(Field minKey, Field maxKey, Field startKey) throws IOException { + keyIter = new FieldKeyIterator2(minKey, maxKey, startKey); } /** * Construct a record iterator. * @param minKey minimum key value. Null corresponds to minimum key value. * @param maxKey maximum key value. Null corresponds to maximum key value. - * @param before - * @throws IOException + * @param before true if initial position is before range, else after range + * @throws IOException thrown if IO error occurs */ - VarKeyIterator(Field minKey, Field maxKey, boolean before) throws IOException { + FieldKeyIterator(Field minKey, Field maxKey, boolean before) throws IOException { Field startKey = before ? minKey : maxKey; if (startKey == null && !before && rootBufferId != -1) { try { - VarKeyNode rightmostLeaf = - nodeMgr.getVarKeyNode(rootBufferId).getRightmostLeafNode(); - startKey = rightmostLeaf.getKey(rightmostLeaf.keyCount - 1); + FieldKeyNode rightmostLeaf = + getFieldKeyNode(rootBufferId).getRightmostLeafNode(); + startKey = rightmostLeaf.getKeyField(rightmostLeaf.getKeyCount() - 1); } finally { nodeMgr.releaseNodes(); } } - keyIter = new VarKeyIterator2(minKey, maxKey, startKey); + keyIter = new FieldKeyIterator2(minKey, maxKey, startKey); } @Override @@ -3362,7 +3298,7 @@ public class Table { if (iterCnt < SHORT_ITER_THRESHOLD) { if (++iterCnt > SHORT_ITER_THRESHOLD) { // Long iterations should use LongKeyIterator1 - keyIter = new VarKeyIterator1((VarKeyIterator2) keyIter); + keyIter = new FieldKeyIterator1((FieldKeyIterator2) keyIter); } } return keyIter.hasNext(); @@ -3375,7 +3311,7 @@ public class Table { if (iterCnt < SHORT_ITER_THRESHOLD) { if (++iterCnt > SHORT_ITER_THRESHOLD) { // Long iterations should use LongKeyIterator1 - keyIter = new VarKeyIterator1((VarKeyIterator2) keyIter); + keyIter = new FieldKeyIterator1((FieldKeyIterator2) keyIter); } } return keyIter.hasPrevious(); @@ -3402,7 +3338,7 @@ public class Table { * A Field key iterator class - optimized for long iterations since * all keys are read for each record node. */ - private class VarKeyIterator1 implements DBFieldIterator { + private class FieldKeyIterator1 implements DBFieldIterator { private int bufferId; private int keyIndex; @@ -3418,7 +3354,7 @@ public class Table { private Field minKey; private Field maxKey; - VarKeyIterator1(VarKeyIterator2 keyIter) throws IOException { + FieldKeyIterator1(FieldKeyIterator2 keyIter) throws IOException { this.bufferId = keyIter.bufferId; this.keyIndex = keyIter.keyIndex; @@ -3436,7 +3372,7 @@ public class Table { reset(); } else { - VarKeyRecordNode leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(bufferId); + FieldKeyRecordNode leaf = (FieldKeyRecordNode) getFieldKeyNode(bufferId); getKeys(leaf); } @@ -3450,7 +3386,7 @@ public class Table { * following a delete. * @param targetKey the initial key. For construction this is the startKey, * following a delete this is the deleted key. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private void initialize(Field targetKey) throws IOException { @@ -3459,17 +3395,17 @@ public class Table { hasPrev = false; if (rootBufferId < 0) { - keys = new Field[0]; + keys = Field.EMPTY_ARRAY; bufferId = -1; return; } try { - VarKeyRecordNode leaf = null; + FieldKeyRecordNode leaf = null; if (keys == null || keys.length == 0) { - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); if (targetKey == null) { targetKey = minKey; @@ -3514,7 +3450,7 @@ public class Table { } leaf = leaf.getNextLeaf(); if (leaf == null) { - keys = new Field[0]; + keys = Field.EMPTY_ARRAY; bufferId = -1; return; } @@ -3536,11 +3472,11 @@ public class Table { } leaf = leaf.getPreviousLeaf(); if (leaf == null) { - keys = new Field[0]; + keys = Field.EMPTY_ARRAY; bufferId = -1; return; } - keyIndex = leaf.keyCount - 1; + keyIndex = leaf.getKeyCount() - 1; getKeys(leaf); } else { @@ -3572,19 +3508,17 @@ public class Table { } } - private void getKeys(VarKeyRecordNode node) throws IOException { + private void getKeys(FieldKeyRecordNode node) throws IOException { bufferId = node.getBufferId(); - if (keys == null || keys.length != node.keyCount) { - keys = new Field[node.keyCount]; + int keyCount = node.getKeyCount(); + if (keys == null || keys.length != keyCount) { + keys = new Field[keyCount]; } - for (int i = 0; i < node.keyCount; i++) { - keys[i] = node.getKey(i); + for (int i = 0; i < keyCount; i++) { + keys[i] = node.getKeyField(i); } } - /** - * @see ghidra.framework.store.db.FieldIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -3602,10 +3536,10 @@ public class Table { // Process next leaf if needed if (nextIndex >= keys.length) { try { - VarKeyRecordNode leaf = - ((VarKeyRecordNode) nodeMgr.getVarKeyNode(bufferId)).getNextLeaf(); + FieldKeyRecordNode leaf = + ((FieldKeyRecordNode) getFieldKeyNode(bufferId)).getNextLeaf(); if (leaf == null || - (maxKey != null && leaf.getKey(0).compareTo(maxKey) > 0)) + (maxKey != null && leaf.getKeyField(0).compareTo(maxKey) > 0)) return false; getKeys(leaf); key = keys[0]; @@ -3633,9 +3567,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.FieldIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -3653,12 +3584,12 @@ public class Table { // Process previous leaf if needed if (prevIndex < 0) { try { - VarKeyRecordNode leaf = ((VarKeyRecordNode) nodeMgr.getVarKeyNode( - bufferId)).getPreviousLeaf(); + FieldKeyRecordNode leaf = + ((FieldKeyRecordNode) getFieldKeyNode(bufferId)).getPreviousLeaf(); if (leaf == null) return false; - prevIndex = leaf.keyCount - 1; - if (minKey != null && leaf.getKey(prevIndex).compareTo(minKey) < 0) + prevIndex = leaf.getKeyCount() - 1; + if (minKey != null && leaf.getKeyField(prevIndex).compareTo(minKey) < 0) return false; getKeys(leaf); key = keys[prevIndex]; @@ -3685,9 +3616,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.FieldIterator#next() - */ @Override public Field next() throws IOException { if (hasNext || hasNext()) { @@ -3699,9 +3627,6 @@ public class Table { return null; } - /** - * @see ghidra.framework.store.db.FieldIterator#previous() - */ @Override public Field previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -3713,9 +3638,6 @@ public class Table { return null; } - /** - * @see db.DBFieldIterator#delete() - */ @Override public boolean delete() throws IOException { if (lastKey != null) { @@ -3747,7 +3669,7 @@ public class Table { * A Field key iterator class - optimized for short iterations since * the number of keys read from each record node is minimized. */ - private class VarKeyIterator2 implements DBFieldIterator { + private class FieldKeyIterator2 implements DBFieldIterator { private int bufferId; private int keyIndex; @@ -3770,7 +3692,7 @@ public class Table { * if still null the minimum indexed value will be assumed. * @throws IOException */ - VarKeyIterator2(Field minKey, Field maxKey, Field startKey) throws IOException { + FieldKeyIterator2(Field minKey, Field maxKey, Field startKey) throws IOException { this.minKey = minKey; this.maxKey = maxKey; @@ -3786,7 +3708,7 @@ public class Table { * following a delete. * @param targetKey the initial key. For construction this is the startKey, * following a delete this is the deleted key. - * @throws IOException + * @throws IOException thrown if IO error occurs */ private void initialize(Field targetKey) throws IOException { @@ -3801,8 +3723,8 @@ public class Table { try { - VarKeyRecordNode leaf; - VarKeyNode rootNode = nodeMgr.getVarKeyNode(rootBufferId); + FieldKeyRecordNode leaf; + FieldKeyNode rootNode = getFieldKeyNode(rootBufferId); if (targetKey == null) { targetKey = minKey; @@ -3812,7 +3734,7 @@ public class Table { if (targetKey == null) { leaf = rootNode.getLeftmostLeafNode(); bufferId = leaf.getBufferId(); - key = leaf.getKey(0); + key = leaf.getKeyField(0); keyIndex = 0; hasNext = true; return; @@ -3821,7 +3743,7 @@ public class Table { bufferId = leaf.getBufferId(); // Empty leaf node - special case - if (leaf.keyCount == 0) { + if (leaf.getKeyCount() == 0) { keyIndex = -1; return; } @@ -3830,7 +3752,7 @@ public class Table { // Start key was found if (keyIndex >= 0) { - key = leaf.getKey(keyIndex); + key = leaf.getKeyField(keyIndex); hasPrev = true; hasNext = true; } @@ -3838,13 +3760,13 @@ public class Table { // Start key was not found else { keyIndex = -(keyIndex + 1); - if (keyIndex == leaf.keyCount) { + if (keyIndex == leaf.getKeyCount()) { --keyIndex; - key = leaf.getKey(keyIndex); + key = leaf.getKeyField(keyIndex); hasPrev = minKey == null ? true : (key.compareTo(minKey) >= 0); } else { - key = leaf.getKey(keyIndex); + key = leaf.getKeyField(keyIndex); hasNext = maxKey == null ? true : (key.compareTo(maxKey) <= 0); } @@ -3859,16 +3781,13 @@ public class Table { private void reset() throws IOException { boolean hadNext = hasNext; boolean hadPrev = hasPrev; - initialize(key); // TODO is this right? + initialize(key); if (hasNext && hasPrev) { hasNext = hadNext; hasPrev = hadPrev; } } - /** - * @see ghidra.framework.store.db.FieldIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { synchronized (db) { @@ -3885,12 +3804,12 @@ public class Table { try { // Process next leaf if needed - VarKeyRecordNode leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(bufferId); - if (nextIndex >= leaf.keyCount) { + FieldKeyRecordNode leaf = (FieldKeyRecordNode) getFieldKeyNode(bufferId); + if (nextIndex >= leaf.getKeyCount()) { leaf = leaf.getNextLeaf(); if (leaf == null) return false; - Field nextKey = leaf.getKey(0); + Field nextKey = leaf.getKeyField(0); if (maxKey != null && nextKey.compareTo(maxKey) > 0) return false; bufferId = leaf.getBufferId(); @@ -3902,7 +3821,7 @@ public class Table { // else, use keys cache else { - Field nextKey = leaf.getKey(nextIndex); + Field nextKey = leaf.getKeyField(nextIndex); hasNext = maxKey == null ? true : (nextKey.compareTo(maxKey) <= 0); if (hasNext) { key = nextKey; @@ -3919,9 +3838,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.FieldIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { synchronized (db) { @@ -3938,13 +3854,13 @@ public class Table { try { // Process previous leaf if needed - VarKeyRecordNode leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(bufferId); + FieldKeyRecordNode leaf = (FieldKeyRecordNode) getFieldKeyNode(bufferId); if (prevIndex < 0) { leaf = leaf.getPreviousLeaf(); if (leaf == null) return false; - prevIndex = leaf.keyCount - 1; - Field prevKey = leaf.getKey(prevIndex); + prevIndex = leaf.getKeyCount() - 1; + Field prevKey = leaf.getKeyField(prevIndex); if (minKey != null && prevKey.compareTo(minKey) < 0) return false; bufferId = leaf.getBufferId(); @@ -3956,7 +3872,7 @@ public class Table { // else, use keys cache else { - Field prevKey = leaf.getKey(prevIndex); + Field prevKey = leaf.getKeyField(prevIndex); hasPrev = minKey == null ? true : (prevKey.compareTo(minKey) >= 0); if (hasPrev) { key = prevKey; @@ -3973,9 +3889,6 @@ public class Table { } } - /** - * @see ghidra.framework.store.db.FieldIterator#next() - */ @Override public Field next() throws IOException { if (hasNext || hasNext()) { @@ -3987,9 +3900,6 @@ public class Table { return null; } - /** - * @see ghidra.framework.store.db.FieldIterator#previous() - */ @Override public Field previous() throws IOException { if (hasPrev || hasPrevious()) { @@ -4001,9 +3911,6 @@ public class Table { return null; } - /** - * @see db.DBFieldIterator#delete() - */ @Override public boolean delete() throws IOException { if (lastKey != null) { @@ -4017,7 +3924,7 @@ public class Table { } /** - * @return + * @return true if table is valid and has not been invalidated */ public boolean isInvalid() { return nodeMgr == null; diff --git a/Ghidra/Framework/DB/src/main/java/db/TableRecord.java b/Ghidra/Framework/DB/src/main/java/db/TableRecord.java index e8cf6a0a80..ed8e7f98a4 100644 --- a/Ghidra/Framework/DB/src/main/java/db/TableRecord.java +++ b/Ghidra/Framework/DB/src/main/java/db/TableRecord.java @@ -15,6 +15,8 @@ */ package db; +import java.io.IOException; + import db.Field.UnsupportedFieldException; /** @@ -33,24 +35,27 @@ class TableRecord implements Comparable { private static final int MAX_KEY_COLUMN = 7; private static final int RECORD_COUNT_COLUMN = 8; - private static Class[] fieldClasses = { StringField.class, // name of table - IntField.class, // Schema version - IntField.class, // Root buffer ID (first buffer) - ByteField.class, // Key field type - BinaryField.class, // Schema field types - StringField.class, // Schema key/field names - IntField.class, // indexing column (-1 = primary) - LongField.class, // max primary key value ever used - IntField.class // number of records + //@formatter:off + private static Field[] fields = { + StringField.INSTANCE, // name of table + IntField.INSTANCE, // Schema version + IntField.INSTANCE, // Root buffer ID (first buffer) + ByteField.INSTANCE, // Key field type + BinaryField.INSTANCE, // Schema field types + StringField.INSTANCE, // Schema key/field names + IntField.INSTANCE, // indexing column (-1 = primary) + LongField.INSTANCE, // max primary key value ever used + IntField.INSTANCE // number of records }; + //@formatter:on private static String[] tableRecordFieldNames = { "TableName", "SchemaVersion", "RootBufferId", "KeyType", "FieldTypes", "FieldNames", "IndexColumn", "MaxKey", "RecordCount" }; - private static Schema schema = new Schema(0, "TableNum", fieldClasses, tableRecordFieldNames); + private static Schema schema = new Schema(0, "TableNum", fields, tableRecordFieldNames); private Record record; - + private Schema tableSchema; private Table table; /** @@ -61,10 +66,11 @@ class TableRecord implements Comparable { * @param indexedColumn primary table index key column, or -1 for primary table */ TableRecord(long tableNum, String name, Schema tableSchema, int indexedColumn) { + this.tableSchema = tableSchema; record = schema.createRecord(tableNum); record.setString(NAME_COLUMN, name); - record.setByteValue(KEY_TYPE_COLUMN, tableSchema.getKeyFieldType().getFieldType()); - record.setBinaryData(FIELD_TYPES_COLUMN, tableSchema.getFieldTypes()); + record.setByteValue(KEY_TYPE_COLUMN, tableSchema.getEncodedKeyFieldType()); + record.setBinaryData(FIELD_TYPES_COLUMN, tableSchema.getEncodedFieldTypes()); record.setString(FIELD_NAMES_COLUMN, tableSchema.getPackedFieldNames()); record.setIntValue(VERSION_COLUMN, tableSchema.getVersion()); record.setIntValue(COLUMN_INDEXED_COLUMN, indexedColumn); @@ -75,9 +81,13 @@ class TableRecord implements Comparable { /** * Construct an existing master table storage record. + * @param dbh database handle * @param record master table storage record. + * @throws UnsupportedFieldException stored schema contains unsupported field + * @throws IOException if IO error occurs */ - TableRecord(Record record) { + TableRecord(DBHandle dbh, Record record) throws IOException { + this.tableSchema = parseSchema(dbh, record); this.record = record; } @@ -100,9 +110,13 @@ class TableRecord implements Comparable { /** * Set the storage record for this instance. * Data is refreshed from the record provided. + * @param dbh database handle * @param record master table storage record. + * @throws UnsupportedFieldException stored schema contains unsupported field + * @throws IOException if IO error occurs */ - void setRecord(Record record) { + void setRecord(DBHandle dbh, Record record) throws IOException { + this.tableSchema = parseSchema(dbh, record); this.record = record; if (table != null) { table.tableRecordChanged(); @@ -120,6 +134,7 @@ class TableRecord implements Comparable { table = null; } this.record = null; + this.tableSchema = null; } /** @@ -140,20 +155,62 @@ class TableRecord implements Comparable { /** * Set the table name - * @param name + * @param name table name */ void setName(String name) { record.setString(NAME_COLUMN, name); } + /** + * + * @param dbh database handle + * @param record record which defines table schema + * @return table schema + * @throws UnsupportedFieldException stored schema contains unsupported field + * @throws IOException if IO error occurs + */ + private static Schema parseSchema(DBHandle dbh, Record record) throws IOException { + Schema tableSchema = + new Schema(record.getIntValue(VERSION_COLUMN), record.getByteValue(KEY_TYPE_COLUMN), + record.getBinaryData(FIELD_TYPES_COLUMN), record.getString(FIELD_NAMES_COLUMN)); + forceUseOfVariableLengthKeyNodesIfNeeded(dbh, tableSchema, + record.getIntValue(BUFFER_ID_COLUMN)); + return tableSchema; + } + + /** + * Determine if legacy schema should be forced to use {@link VarKeyNode} + * table storage for compatibility. Root buffer node for applicable + * primitive fixed-length key types will be checked. + * @param dbh database handle + * @param tableSchema table schema to be checked + * @param rootBufferId table root buffer ID + * @throws IOException if IO error occurs + */ + private static void forceUseOfVariableLengthKeyNodesIfNeeded(DBHandle dbh, Schema tableSchema, + int rootBufferId) throws IOException { + if (rootBufferId < 0) { + return; + } + Field keyType = tableSchema.getKeyFieldType(); + if (keyType.isVariableLength()) { + return; + } + if (keyType instanceof LongField || keyType instanceof IndexField || + keyType instanceof FixedField) { + return; + } + if (NodeMgr.isVarKeyNode(dbh.getBufferMgr(), rootBufferId)) { + tableSchema.forceUseOfVariableLengthKeyNodes(); + } + } + /** * Get the table schema * @return table schema - * @throws UnsupportedFieldException if unsupported schema field encountered */ - Schema getSchema() throws UnsupportedFieldException { - return new Schema(record.getIntValue(VERSION_COLUMN), record.getByteValue(KEY_TYPE_COLUMN), - record.getBinaryData(FIELD_TYPES_COLUMN), record.getString(FIELD_NAMES_COLUMN)); + Schema getSchema() { + return tableSchema; } /** diff --git a/Ghidra/Framework/DB/src/main/java/db/VarIndexTable.java b/Ghidra/Framework/DB/src/main/java/db/VarIndexTable.java deleted file mode 100644 index befeece1a0..0000000000 --- a/Ghidra/Framework/DB/src/main/java/db/VarIndexTable.java +++ /dev/null @@ -1,323 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package db; - -import java.io.IOException; - -/** - * The VarIndexTable provides a secondary index on a variable-length table column - * (e.g., StringField). For each unique secondary index value, an IndexBuffer is - * stored within an underlying index table record. The secondary index value is used as the long - * key to access this record. Within a single IndexBuffer is stored all primary keys which - * correspond to an index value. - */ -class VarIndexTable extends IndexTable { - - private static final Class[] fieldClasses = { BinaryField.class, // index data - }; - - private static final String[] fieldNames = { "IndexBuffer" }; - - private Schema indexSchema; - - /** - * Construct a new secondary index which is based upon a field within the - * primary table specified by name. - * @param primaryTable primary table. - * @param colIndex identifies the indexed column within the primary table. - * @throws IOException thrown if an IO error occurs - */ - VarIndexTable(Table primaryTable, int colIndex) throws IOException { - this(primaryTable, - primaryTable.getDBHandle().getMasterTable().createTableRecord(primaryTable.getName(), - new Schema(0, primaryTable.getSchema().getField(colIndex).getClass(), "IndexKey", - fieldClasses, fieldNames), - colIndex)); - } - - /** - * Construct a new or existing secondary index. An existing index must have - * its root ID specified within the tableRecord. - * @param primaryTable primary table. - * @param indexTableRecord specifies the index parameters. - * @throws IOException thrown if an IO error occurs - */ - VarIndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException { - super(primaryTable, indexTableRecord); - this.indexSchema = indexTable.getSchema(); - } - - /** - * Find all primary keys which correspond to the specified indexed field - * value. - * @param indexValue the field value to search for. - * @return list of primary keys - * @throws IOException thrown if an IO error occurs - */ - @Override - long[] findPrimaryKeys(Field indexValue) throws IOException { - if (!indexValue.getClass().equals(fieldType.getClass())) { - throw new IllegalArgumentException("Incorrect indexed field type"); - } - Record indexRecord = indexTable.getRecord(indexValue); - if (indexRecord == null) { - return emptyKeyArray; - } - IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0)); - return indexBuffer.getPrimaryKeys(); - } - - /** - * Get the number of primary keys which correspond to the specified indexed field - * value. - * @param indexValue the field value to search for. - * @return key count - * @throws IOException thrown if an IO error occurs - */ - @Override - int getKeyCount(Field indexValue) throws IOException { - if (!indexValue.getClass().equals(fieldType.getClass())) { - throw new IllegalArgumentException("Incorrect indexed field type"); - } - Record indexRecord = indexTable.getRecord(indexValue); - if (indexRecord == null) { - return 0; - } - IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0)); - return indexBuffer.keyCount; - } - - /* - * @see ghidra.framework.store.db.IndexTable#addEntry(ghidra.framework.store.db.Record) - */ - @Override - void addEntry(Record record) throws IOException { - Field indexField = record.getField(colIndex); - Record indexRecord = indexTable.getRecord(indexField); - if (indexRecord == null) { - indexRecord = indexSchema.createRecord(indexField); - } - IndexBuffer indexBuffer = new IndexBuffer(indexField, indexRecord.getBinaryData(0)); - indexBuffer.addEntry(record.getKey()); - indexRecord.setBinaryData(0, indexBuffer.getData()); - indexTable.putRecord(indexRecord); - } - - /* - * @see ghidra.framework.store.db.IndexTable#deleteEntry(ghidra.framework.store.db.Record) - */ - @Override - void deleteEntry(Record record) throws IOException { - Field indexField = record.getField(colIndex); - Record indexRecord = indexTable.getRecord(indexField); - if (indexRecord != null) { - IndexBuffer indexBuffer = new IndexBuffer(indexField, indexRecord.getBinaryData(0)); - indexBuffer.deleteEntry(record.getKey()); - byte[] data = indexBuffer.getData(); - if (data == null) { - indexTable.deleteRecord(indexField); - } - else { - indexRecord.setBinaryData(0, data); - indexTable.putRecord(indexRecord); - } - } - } - - /** - * Get the index buffer associated with the specified index key - * @param indexKey index key - * @return index buffer or null if not found - * @throws IOException thrown if IO error occurs - */ - private IndexBuffer getIndexBuffer(Field indexKey) throws IOException { - Record indexRec = indexTable.getRecord(indexKey); - return indexRec != null ? new IndexBuffer(indexKey, indexRec.getBinaryData(0)) : null; - } - - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator() - */ - @Override - DBFieldIterator indexIterator() throws IOException { - return new IndexVarFieldIterator(); - } - - /* - * @see ghidra.framework.store.db.IndexTable#indexIterator(ghidra.framework.store.db.Field, ghidra.framework.store.db.Field, boolean) - */ - @Override - DBFieldIterator indexIterator(Field minField, Field maxField, boolean before) - throws IOException { - return new IndexVarFieldIterator(minField, maxField, before); - } - - /* - * @see db.IndexTable#indexIterator(db.Field, db.Field, db.Field, boolean) - */ - @Override - DBFieldIterator indexIterator(Field minField, Field maxField, Field startField, boolean before) - throws IOException { - return new IndexVarFieldIterator(minField, maxField, startField, before); - } - - /** - * Iterates over index field values within a specified range. - */ - class IndexVarFieldIterator implements DBFieldIterator { - - private Field lastKey; - private Field keyField; - private DBFieldIterator indexIterator; - private boolean hasNext = false; - private boolean hasPrev = false; - - /** - * Construct an index field iterator starting with the minimum index value. - */ - IndexVarFieldIterator() throws IOException { - this(null, null, true); - } - - /** - * Construct an index field iterator. The iterator is positioned at index - * value identified by startValue. - * @param minValue minimum index value. Null corresponds to minimum indexed value. - * @param maxValue maximum index value. Null corresponds to maximum indexed value. - * @param before if true initial position is before minValue, else position - * is after maxValue. - * @throws IOException - */ - IndexVarFieldIterator(Field minValue, Field maxValue, boolean before) throws IOException { - - indexIterator = indexTable.fieldKeyIterator(minValue, maxValue, before); - - if (indexIterator.hasNext()) { - indexIterator.next(); - if (before) { - indexIterator.previous(); - } - } - } - - /** - * Construct an index field iterator. The iterator is positioned at index - * value identified by startValue. - * @param minValue minimum index value. Null corresponds to minimum indexed value. - * @param maxValue maximum index value. Null corresponds to maximum indexed value. - * @param startValue identify initial position by value - * @param before if true initial position is before minValue, else position - * is after maxValue. - * @throws IOException - */ - IndexVarFieldIterator(Field minValue, Field maxValue, Field startValue, boolean before) - throws IOException { - - if (startValue == null) { - throw new IllegalArgumentException("starting index value required"); - } - indexIterator = indexTable.fieldKeyIterator(minValue, maxValue, startValue); - - if (indexIterator.hasNext()) { - Field f = indexIterator.next(); - if (before || !f.equals(startValue)) { - indexIterator.previous(); - } - } - } - - @Override - public boolean hasNext() throws IOException { - if (hasNext) { - return true; - } - Field key = indexIterator.next(); - if (key == null) { - return false; - } - keyField = key; - hasNext = true; - hasPrev = false; - return true; - } - - @Override - public boolean hasPrevious() throws IOException { - if (hasPrev) { - return true; - } - Field key = indexIterator.previous(); - if (key == null) { - return false; - } - keyField = key; - hasNext = false; - hasPrev = true; - return true; - } - - @Override - public Field next() throws IOException { - if (hasNext || hasNext()) { - hasNext = false; - hasPrev = true; - lastKey = keyField; - return keyField; - } - return null; - } - - @Override - public Field previous() throws IOException { - if (hasPrev || hasPrevious()) { - hasNext = true; - hasPrev = false; - lastKey = keyField; - return keyField; - } - return null; - } - - /** - * Delete all primary records which have the current - * index value (lastKey). - * @see db.DBFieldIterator#delete() - */ - @Override - public boolean delete() throws IOException { - if (lastKey == null) { - return false; - } - synchronized (db) { - IndexBuffer indexBuf = getIndexBuffer(lastKey); - if (indexBuf != null) { - long[] keys = indexBuf.getPrimaryKeys(); - for (long key : keys) { - primaryTable.deleteRecord(key); - } - // The following does not actually delete the index record since it - // should already have been removed with the removal of all associated - // primary records. Invoking this method allows the iterator to - // recover from the index table change. -// indexIterator.delete(); - } - lastKey = null; - return true; - } - } - } - -} diff --git a/Ghidra/Framework/DB/src/main/java/db/VarKeyInteriorNode.java b/Ghidra/Framework/DB/src/main/java/db/VarKeyInteriorNode.java index fe90e14929..b5a4b3f151 100644 --- a/Ghidra/Framework/DB/src/main/java/db/VarKeyInteriorNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/VarKeyInteriorNode.java @@ -29,9 +29,9 @@ import ghidra.util.task.TaskMonitor; * has the following layout within a single DataBuffer (field size in bytes): * * | NodeType(1) | KeyType(1) | KeyCount(4) | KeyOffset0(4) | ID0(4) | ... | KeyOffsetN(4) | IDN(4) | - * ...... | KeyN | ... | Key0 | + * ...<FreeSpace>... | KeyN | ... | Key0 | */ -class VarKeyInteriorNode extends VarKeyNode { +class VarKeyInteriorNode extends VarKeyNode implements FieldKeyInteriorNode { private static final int BASE = VARKEY_NODE_HEADER_SIZE; @@ -83,7 +83,7 @@ class VarKeyInteriorNode extends VarKeyNode { void logConsistencyError(String tableName, String msg, Throwable t) throws IOException { Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); - Msg.debug(this, " parent.key[0]=" + getKey(0) + " bufferID=" + getBufferId()); + Msg.debug(this, " parent.key[0]=" + getKeyField(0) + " bufferID=" + getBufferId()); if (t != null) { Msg.error(this, "Consistency Error (" + tableName + ")", t); } @@ -98,26 +98,24 @@ class VarKeyInteriorNode extends VarKeyNode { for (int i = 0; i < keyCount; i++) { // Compare each key entry with the previous entries key-range - Field key = getKey(i); - if (i != 0) { - if (key.compareTo(lastMinKey) <= 0) { - consistent = false; - logConsistencyError(tableName, - "child[" + i + "].minKey <= child[" + (i - 1) + "].minKey", null); - Msg.debug(this, - " child[" + i + "].minKey = " + key + " bufferID=" + getBufferId(i)); - Msg.debug(this, " child[" + (i - 1) + "].minKey = " + lastMinKey + - " bufferID=" + getBufferId(i - 1)); - } - else if (key.compareTo(lastMaxKey) <= 0) { - consistent = false; - logConsistencyError(tableName, - "child[" + i + "].minKey <= child[" + (i - 1) + "].maxKey", null); - Msg.debug(this, - " child[" + i + "].minKey = " + key + " bufferID=" + getBufferId(i)); - Msg.debug(this, " child[" + (i - 1) + "].maxKey = " + lastMaxKey + - " bufferID=" + getBufferId(i - 1)); - } + Field key = getKeyField(i); + if (lastMinKey != null && key.compareTo(lastMinKey) <= 0) { + consistent = false; + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].minKey", null); + Msg.debug(this, + " child[" + i + "].minKey = " + key + " bufferID=" + getBufferId(i)); + Msg.debug(this, " child[" + (i - 1) + "].minKey = " + lastMinKey + " bufferID=" + + getBufferId(i - 1)); + } + else if (lastMaxKey != null && key.compareTo(lastMaxKey) <= 0) { + consistent = false; + logConsistencyError(tableName, + "child[" + i + "].minKey <= child[" + (i - 1) + "].maxKey", null); + Msg.debug(this, + " child[" + i + "].minKey = " + key + " bufferID=" + getBufferId(i)); + Msg.debug(this, " child[" + (i - 1) + "].maxKey = " + lastMaxKey + " bufferID=" + + getBufferId(i - 1)); } lastMinKey = key; @@ -143,10 +141,10 @@ class VarKeyInteriorNode extends VarKeyNode { continue; // skip child } - lastMaxKey = node.getKey(node.getKeyCount() - 1); + lastMaxKey = node.getKeyField(node.getKeyCount() - 1); // Verify key match-up between parent and child - Field childKey0 = node.getKey(0); + Field childKey0 = node.getKeyField(0); if (!key.equals(childKey0)) { consistent = false; logConsistencyError(tableName, @@ -182,10 +180,16 @@ class VarKeyInteriorNode extends VarKeyNode { /** * Perform a binary search to locate the specified key and derive an index - * into the Buffer ID storage. This method is used to identify the child - * node which contains the specified record key. - * @param key - * @return int buffer ID index. + * into the Buffer ID storage. This method is intended to locate the child + * node which contains the specified key. The returned index corresponds + * to a child's stored buffer/node ID and may correspond to another interior + * node or a leaf record node. Each stored key within this interior node + * effectively identifies the maximum key contained within the corresponding + * child node. + * @param key key to search for + * @return int buffer ID index of child node. An existing positive index + * value will always be returned. + * @throws IOException if IO error occurs */ int getIdIndex(Field key) throws IOException { @@ -194,12 +198,11 @@ class VarKeyInteriorNode extends VarKeyNode { while (min <= max) { int i = (min + max) / 2; - Field k = getKey(i); - int rc = k.compareTo(key); + int rc = compareKeyField(key, i); if (rc == 0) { return i; } - else if (rc < 0) { + else if (rc > 0) { min = i + 1; } else { @@ -209,26 +212,19 @@ class VarKeyInteriorNode extends VarKeyNode { return max; } - /** - * Perform a binary search to locate the specified key and derive an index - * into the Buffer ID storage. This method is intended to find the insertion - * index or exact match for a child key. - * @param key - * @return int buffer ID index. - */ - private int getKeyIndex(Field key) throws IOException { + @Override + public int getKeyIndex(Field key) throws IOException { int min = 0; int max = keyCount - 1; while (min <= max) { int i = (min + max) / 2; - Field k = getKey(i); - int rc = k.compareTo(key); + int rc = compareKeyField(key, i); if (rc == 0) { return i; } - else if (rc < 0) { + else if (rc > 0) { min = i + 1; } else { @@ -271,7 +267,8 @@ class VarKeyInteriorNode extends VarKeyNode { * @param index key index * @return record key offset */ - private int getKeyOffset(int index) { + @Override + public int getKeyOffset(int index) { return buffer.getInt(BASE + (index * ENTRY_SIZE)); } @@ -284,11 +281,8 @@ class VarKeyInteriorNode extends VarKeyNode { buffer.putInt(BASE + (index * ENTRY_SIZE), offset); } - /* - * @see ghidra.framework.store.db.VarKeyNode#getKey(int) - */ @Override - Field getKey(int index) throws IOException { + public Field getKeyField(int index) throws IOException { Field key = keyType.newField(); key.read(buffer, buffer.getInt(BASE + (index * ENTRY_SIZE))); return key; @@ -313,15 +307,6 @@ class VarKeyInteriorNode extends VarKeyNode { return buffer.getInt(BASE + (index * ENTRY_SIZE) + KEY_OFFSET_SIZE); } -// /** -// * Store the child node buffer ID associated with the specified key index -// * @param index child key index -// * @param id child node buffer ID -// */ -// private void putBufferId(int index, int id) { -// buffer.putInt(BASE + (index * ENTRY_SIZE) + KEY_OFFSET_SIZE, id); -// } - /** * @return unused free space within node */ @@ -432,8 +417,11 @@ class VarKeyInteriorNode extends VarKeyNode { * Callback method for when a child node's leftmost key changes. * @param oldKey previous leftmost key. * @param newKey new leftmost key. + * @param node child node containing oldKey + * @throws IOException if IO error occurs */ - void keyChanged(Field oldKey, Field newKey, VarKeyNode node) throws IOException { + @Override + public void keyChanged(Field oldKey, Field newKey, FieldKeyNode node) throws IOException { int index = getKeyIndex(oldKey); if (index < 0) { @@ -443,7 +431,7 @@ class VarKeyInteriorNode extends VarKeyNode { int lenChange = newKey.length() - oldKey.length(); if (lenChange > 0 && lenChange > getFreeSpace()) { // Split node if updated key won't fit - split(index, oldKey, newKey, node); + split(index, oldKey, newKey, (VarKeyNode) node); } else { @@ -461,7 +449,8 @@ class VarKeyInteriorNode extends VarKeyNode { * @param oldIndex index of key to be updated * @param oldKey old key value stored at oldIndex * @param newKey new key value - * @throws IOException thrown if IO error occurs + * @param node child node containing oldKey + * @throws IOException if IO error occurs */ private void split(int oldIndex, Field oldKey, Field newKey, VarKeyNode node) throws IOException { @@ -497,7 +486,7 @@ class VarKeyInteriorNode extends VarKeyNode { parent.insert(newNode); if (newNode.parent != parent) { // Fix my parent - if (parent.getKeyIndex(getKey(0)) < 0) { + if (parent.getKeyIndex(getKeyField(0)) < 0) { parent = newNode.parent; } } @@ -505,8 +494,8 @@ class VarKeyInteriorNode extends VarKeyNode { } // New parent node becomes root - parent = new VarKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), newNode.getKey(0), - newNode.getBufferId()); + parent = new VarKeyInteriorNode(nodeMgr, getKeyField(0), buffer.getId(), + newNode.getKeyField(0), newNode.getBufferId()); newNode.parent = parent; } @@ -518,7 +507,7 @@ class VarKeyInteriorNode extends VarKeyNode { */ VarKeyNode insert(VarKeyNode node) throws IOException { - Field key = node.getKey(0); + Field key = node.getKeyField(0); int id = node.getBufferId(); // Split this node if full @@ -536,6 +525,7 @@ class VarKeyInteriorNode extends VarKeyNode { * @param key leftmost key associated with new node. * @param node child node which corresponds to the id and key. * @return root node. + * @throws IOException thrown if an IO error occurs */ VarKeyNode insert(int id, Field key, VarKeyNode node) throws IOException { @@ -549,7 +539,7 @@ class VarKeyInteriorNode extends VarKeyNode { node.parent = this; if (index == 0 && parent != null) { - parent.keyChanged(getKey(1), key, this); + parent.keyChanged(getKeyField(1), key, this); } return getRoot(); @@ -568,7 +558,6 @@ class VarKeyInteriorNode extends VarKeyNode { // Create new interior node VarKeyInteriorNode newNode = new VarKeyInteriorNode(nodeMgr, keyType); -// DataBuffer newBuf = newNode.buffer; int halfway = ((keyCount == 0 ? buffer.length() : getKeyOffset(keyCount - 1)) + buffer.length()) / 2; @@ -576,7 +565,7 @@ class VarKeyInteriorNode extends VarKeyNode { moveKeysRight(this, newNode, keyCount - getOffsetIndex(halfway)); // Insert new key/id - Field rightKey = newNode.getKey(0); + Field rightKey = newNode.getKeyField(0); if (newKey.compareTo(rightKey) < 0) { insert(newId, newKey, node); } @@ -588,7 +577,7 @@ class VarKeyInteriorNode extends VarKeyNode { VarKeyNode rootNode = parent.insert(newNode); if (newNode.parent != parent) { // Fix my parent - if (parent.getKeyIndex(getKey(0)) < 0) { + if (parent.getKeyIndex(getKeyField(0)) < 0) { parent = newNode.parent; } } @@ -596,33 +585,27 @@ class VarKeyInteriorNode extends VarKeyNode { } // New parent node becomes root - parent = new VarKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), rightKey, + parent = new VarKeyInteriorNode(nodeMgr, getKeyField(0), buffer.getId(), rightKey, newNode.getBufferId()); newNode.parent = parent; return parent; } - /* - * @see ghidra.framework.store.db.VarKeyNode#getLeafNode(long) - */ @Override - VarKeyRecordNode getLeafNode(Field key) throws IOException { + public VarKeyRecordNode getLeafNode(Field key) throws IOException { VarKeyNode node = nodeMgr.getVarKeyNode(getBufferId(getIdIndex(key))); node.parent = this; return node.getLeafNode(key); } - /* - * @see ghidra.framework.store.db.VarKeyNode#getLeftmostLeafNode() - */ @Override - VarKeyRecordNode getLeftmostLeafNode() throws IOException { + public VarKeyRecordNode getLeftmostLeafNode() throws IOException { VarKeyNode node = nodeMgr.getVarKeyNode(getBufferId(0)); return node.getLeftmostLeafNode(); } @Override - VarKeyRecordNode getRightmostLeafNode() throws IOException { + public VarKeyRecordNode getRightmostLeafNode() throws IOException { VarKeyNode node = nodeMgr.getVarKeyNode(getBufferId(keyCount - 1)); return node.getRightmostLeafNode(); } @@ -654,7 +637,7 @@ class VarKeyInteriorNode extends VarKeyNode { // Delete child entry deleteEntry(index); if (index == 0 && parent != null) { - parent.keyChanged(key, getKey(0), this); + parent.keyChanged(key, getKeyField(0), this); } return (parent != null) ? parent.balanceChild(this) : this; @@ -676,7 +659,7 @@ class VarKeyInteriorNode extends VarKeyNode { // balance with right sibling except if node corresponds to the right-most // key within this interior node - in that case balance with left sibling. - int index = getIdIndex(node.getKey(0)); + int index = getIdIndex(node.getKeyField(0)); if (index == (keyCount - 1)) { return balanceChild((VarKeyInteriorNode) nodeMgr.getVarKeyNode(getBufferId(index - 1)), node); @@ -700,14 +683,11 @@ class VarKeyInteriorNode extends VarKeyNode { int leftKeyCount = leftNode.keyCount; int rightKeyCount = rightNode.keyCount; -// if (leftKeyCount == rightKeyCount) { -// return getRoot(); -// } int len = buffer.length(); int leftKeySpace = len - leftNode.getKeyOffset(leftKeyCount - 1); int rightKeySpace = len - rightNode.getKeyOffset(rightKeyCount - 1); - Field rightKey = rightNode.getKey(0); + Field rightKey = rightNode.getKeyField(0); // Can right keys fit within left node if ((rightKeySpace + (rightKeyCount * ENTRY_SIZE)) <= (len - BASE - leftKeySpace - @@ -731,7 +711,7 @@ class VarKeyInteriorNode extends VarKeyNode { balanced = moveKeysLeft(leftNode, rightNode, rightKeyCount - index - 1); } if (balanced) { - this.keyChanged(rightKey, rightNode.getKey(0), rightNode); + this.keyChanged(rightKey, rightNode.getKeyField(0), rightNode); } return getRoot(); } @@ -775,16 +755,6 @@ class VarKeyInteriorNode extends VarKeyNode { return true; } -//private static void checkKeyOffsets(VarKeyInteriorNode node) { -// for (int i = 0; i < node.keyCount; i++) { -// int length = node.buffer.getInt(node.getKeyOffset(i)); -// -// if (length < -1 || length > 40) { -// throw new ArrayIndexOutOfBoundsException(); -// } -// } -//} - /** * Move some or all of the keys from the right node into the left node. * If all keys are moved, the caller is responsible for deleting the right @@ -802,9 +772,6 @@ class VarKeyInteriorNode extends VarKeyNode { int rightOffset = rightNode.getKeyOffset(count - 1); int len = rightNode.buffer.length() - rightOffset; int leftOffset = leftNode.getKeyOffset(leftKeyCount - 1) - len; -//if ((len + (ENTRY_SIZE * count)) > leftNode.getFreeSpace()) { -// throw new ArrayIndexOutOfBoundsException(); -//} // Move key data to left node leftNode.buffer.copy(leftOffset, rightNode.buffer, rightOffset, len); @@ -831,9 +798,6 @@ class VarKeyInteriorNode extends VarKeyNode { return true; } - /* - * @see ghidra.framework.store.db.VarKeyNode#delete() - */ @Override public void delete() throws IOException { @@ -846,9 +810,6 @@ class VarKeyInteriorNode extends VarKeyNode { nodeMgr.deleteNode(this); } - /* - * @see ghidra.framework.store.db.BTreeNode#getBufferReferences() - */ @Override public int[] getBufferReferences() { int[] ids = new int[keyCount]; @@ -871,7 +832,7 @@ class VarKeyInteriorNode extends VarKeyNode { public boolean isRightmostKey(Field key) throws IOException { if (getIdIndex(key) == (keyCount - 1)) { if (parent != null) { - return parent.isRightmostKey(getKey(0)); + return parent.isRightmostKey(getKeyField(0)); } return true; } diff --git a/Ghidra/Framework/DB/src/main/java/db/VarKeyNode.java b/Ghidra/Framework/DB/src/main/java/db/VarKeyNode.java index 22171353cf..a1786bd414 100644 --- a/Ghidra/Framework/DB/src/main/java/db/VarKeyNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/VarKeyNode.java @@ -22,8 +22,11 @@ import db.buffers.DataBuffer; /** * VarKeyNode is an abstract implementation of a BTree node * which utilizes variable-length Field key values. + *
+ *   | NodeType(1) | KeyType(1) | KeyCount(4) | ...
+ * 
*/ -abstract class VarKeyNode implements BTreeNode { +abstract class VarKeyNode implements FieldKeyNode { private static final int KEY_TYPE_SIZE = 1; private static final int KEY_COUNT_SIZE = 4; @@ -62,7 +65,7 @@ abstract class VarKeyNode implements BTreeNode { * @param nodeMgr table node manager. * @param nodeType node type * @param keyType key Field type - * @throws IOException thrown if IO error occurs + * @throws IOException if IO error occurs */ VarKeyNode(NodeMgr nodeMgr, byte nodeType, Field keyType) throws IOException { this.nodeMgr = nodeMgr; @@ -75,6 +78,11 @@ abstract class VarKeyNode implements BTreeNode { nodeMgr.addNode(this); } + @Override + public VarKeyInteriorNode getParent() { + return parent; + } + @Override public int getBufferId() { return buffer.getId(); @@ -91,8 +99,9 @@ abstract class VarKeyNode implements BTreeNode { * @return TableNode */ VarKeyNode getRoot() { - if (parent != null) + if (parent != null) { return parent.getRoot(); + } return this; } @@ -107,13 +116,26 @@ abstract class VarKeyNode implements BTreeNode { buffer.putInt(KEY_COUNT_OFFSET, keyCount); } + @Override + public int compareKeyField(Field k, int keyIndex) { + return k.compareTo(buffer, getKeyOffset(keyIndex)); + } + + /** + * Get the key offset within the buffer + * @param index key index + * @return record key offset + */ + public abstract int getKeyOffset(int index); + /** * Get the key value at a specific index. * @param index key index * @return key value * @throws IOException thrown if an IO error occurs */ - abstract Field getKey(int index) throws IOException; + @Override + public abstract Field getKeyField(int index) throws IOException; /** * Get the leaf node which contains the specified key. @@ -121,20 +143,23 @@ abstract class VarKeyNode implements BTreeNode { * @return leaf node * @throws IOException thrown if an IO error occurs */ - abstract VarKeyRecordNode getLeafNode(Field key) throws IOException; + @Override + public abstract VarKeyRecordNode getLeafNode(Field key) throws IOException; /** * Get the left-most leaf node within the tree. * @return left-most leaf node. * @throws IOException thrown if IO error occurs */ - abstract VarKeyRecordNode getLeftmostLeafNode() throws IOException; + @Override + public abstract VarKeyRecordNode getLeftmostLeafNode() throws IOException; /** * Get the right-most leaf node within the tree. * @return right-most leaf node. * @throws IOException thrown if IO error occurs */ - abstract VarKeyRecordNode getRightmostLeafNode() throws IOException; + @Override + public abstract VarKeyRecordNode getRightmostLeafNode() throws IOException; } diff --git a/Ghidra/Framework/DB/src/main/java/db/VarKeyRecordNode.java b/Ghidra/Framework/DB/src/main/java/db/VarKeyRecordNode.java index e8da06aaac..be1b499f65 100644 --- a/Ghidra/Framework/DB/src/main/java/db/VarKeyRecordNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/VarKeyRecordNode.java @@ -25,19 +25,19 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** - * LongKeyRecordNode is an implementation of a BTree leaf node + * VarKeyRecordNode is an implementation of a BTree leaf node * which utilizes variable-length key values and stores variable-length records. * This type of node has the following layout within a single DataBuffer * (field size in bytes): *
  *   |   NodeType(1) | KeyType(1) | KeyCount(4) | PrevLeafId(4) | NextLeafId(4) | KeyOffset0(4) | IndFlag0(1) |...      
  * 
- *   | KeyOffsetN(4) | IndFlagN(1) |...... | KeyN | RecN |... | Key0 | Rec0 |
+ *   | KeyOffsetN(4) | IndFlagN(1) |...<FreeSpace>... | KeyN | RecN |... | Key0 | Rec0 |
  * 
* IndFlag - if not zero the record has been stored within a chained DBBuffer * whose 4-byte integer buffer ID has been stored within this leaf at the record offset. */ -class VarKeyRecordNode extends VarKeyNode { +class VarKeyRecordNode extends VarKeyNode implements FieldKeyRecordNode { private static final int ID_SIZE = 4; @@ -94,7 +94,7 @@ class VarKeyRecordNode extends VarKeyNode { void logConsistencyError(String tableName, String msg, Throwable t) throws IOException { Msg.debug(this, "Consistency Error (" + tableName + "): " + msg); - Msg.debug(this, " bufferID=" + getBufferId() + " key[0]=" + getKey(0)); + Msg.debug(this, " bufferID=" + getBufferId() + " key[0]=" + getKeyField(0)); if (t != null) { Msg.error(this, "Consistency Error (" + tableName + ")", t); } @@ -107,7 +107,7 @@ class VarKeyRecordNode extends VarKeyNode { Field prevKey = null; for (int i = 0; i < keyCount; i++) { // Compare each key entry with the previous key - Field key = getKey(i); + Field key = getKeyField(i); if (i != 0) { if (key.compareTo(prevKey) <= 0) { consistent = false; @@ -119,14 +119,14 @@ class VarKeyRecordNode extends VarKeyNode { prevKey = key; } - if ((parent == null || parent.isLeftmostKey(getKey(0))) && getPreviousLeaf() != null) { + if ((parent == null || parent.isLeftmostKey(getKeyField(0))) && getPreviousLeaf() != null) { consistent = false; logConsistencyError(tableName, "previous-leaf should not exist", null); } VarKeyRecordNode node = getNextLeaf(); if (node != null) { - if (parent == null || parent.isRightmostKey(getKey(0))) { + if (parent == null || parent.isRightmostKey(getKeyField(0))) { consistent = false; logConsistencyError(tableName, "next-leaf should not exist", null); } @@ -138,7 +138,7 @@ class VarKeyRecordNode extends VarKeyNode { } } } - else if (parent != null && !parent.isRightmostKey(getKey(0))) { + else if (parent != null && !parent.isRightmostKey(getKeyField(0))) { consistent = false; logConsistencyError(tableName, "this leaf is not linked to next-leaf", null); } @@ -146,35 +146,36 @@ class VarKeyRecordNode extends VarKeyNode { return consistent; } - /* - * @see ghidra.framework.store.db.VarKeyNode#getLeafNode(long) - */ @Override - VarKeyRecordNode getLeafNode(Field key) throws IOException { + public VarKeyRecordNode getLeafNode(Field key) throws IOException { return this; } - /* - * @see ghidra.framework.store.db2.VarKeyNode#getLeftmostLeafNode() - */ @Override - VarKeyRecordNode getLeftmostLeafNode() throws IOException { + public VarKeyRecordNode getLeftmostLeafNode() throws IOException { VarKeyRecordNode leaf = getPreviousLeaf(); return leaf != null ? leaf.getLeftmostLeafNode() : this; } @Override - VarKeyRecordNode getRightmostLeafNode() throws IOException { + public VarKeyRecordNode getRightmostLeafNode() throws IOException { VarKeyRecordNode leaf = getNextLeaf(); return leaf != null ? leaf.getRightmostLeafNode() : this; } + @Override + public boolean hasNextLeaf() throws IOException { + int nextLeafId = buffer.getInt(NEXT_LEAF_ID_OFFSET); + return (nextLeafId >= 0); + } + /** * Get this leaf node's right sibling * @return this leaf node's right sibling or null if right sibling does not exist. * @throws IOException thrown if an IO error occurs */ - VarKeyRecordNode getNextLeaf() throws IOException { + @Override + public VarKeyRecordNode getNextLeaf() throws IOException { VarKeyRecordNode leaf = null; int nextLeafId = buffer.getInt(NEXT_LEAF_ID_OFFSET); if (nextLeafId >= 0) { @@ -183,40 +184,40 @@ class VarKeyRecordNode extends VarKeyNode { return leaf; } + @Override + public boolean hasPreviousLeaf() throws IOException { + int prevLeafId = buffer.getInt(PREV_LEAF_ID_OFFSET); + return (prevLeafId >= 0); + } + /** * Get this leaf node's left sibling * @return this leaf node's left sibling or null if left sibling does not exist. - * @throws IOException thrown if an IO error occurs + * @throws IOException if an IO error occurs */ - VarKeyRecordNode getPreviousLeaf() throws IOException { + @Override + public VarKeyRecordNode getPreviousLeaf() throws IOException { VarKeyRecordNode leaf = null; - int nextLeafId = buffer.getInt(PREV_LEAF_ID_OFFSET); - if (nextLeafId >= 0) { - leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(nextLeafId); + int prevLeafId = buffer.getInt(PREV_LEAF_ID_OFFSET); + if (prevLeafId >= 0) { + leaf = (VarKeyRecordNode) nodeMgr.getVarKeyNode(prevLeafId); } return leaf; } - /** - * Perform a binary search to locate the specified key and derive an index - * into the Buffer ID storage. - * @param key - * @return int buffer ID index. - * @throws IOException thrown if an IO error occurs - */ - int getKeyIndex(Field key) throws IOException { + @Override + public int getKeyIndex(Field key) throws IOException { int min = 0; int max = keyCount - 1; while (min <= max) { int i = (min + max) / 2; - Field k = getKey(i); - int rc = k.compareTo(key); + int rc = compareKeyField(key, i); if (rc == 0) { return i; } - else if (rc < 0) { + else if (rc > 0) { min = i + 1; } else { @@ -256,14 +257,14 @@ class VarKeyRecordNode extends VarKeyNode { } // New parent node becomes root - return new VarKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), newLeaf.getKey(0), - newBufId); + return new VarKeyInteriorNode(nodeMgr, getKeyField(0), buffer.getId(), + newLeaf.getKeyField(0), newBufId); } /** * Append a leaf which contains one or more keys and update tree. Leaf is inserted * as the new right sibling of this leaf. - * @param newLeaf new right sibling leaf (must be same node type as this leaf) + * @param leaf new right sibling leaf (must be same node type as this leaf) * @return root node which may have changed. * @throws IOException thrown if an IO error occurs */ @@ -290,17 +291,12 @@ class VarKeyRecordNode extends VarKeyNode { } // New parent node becomes root - return new VarKeyInteriorNode(nodeMgr, getKey(0), buffer.getId(), leaf.getKey(0), newBufId); + return new VarKeyInteriorNode(nodeMgr, getKeyField(0), buffer.getId(), leaf.getKeyField(0), + newBufId); } - /** - * Insert or Update a record. - * @param record data record with long key - * @param table table which will be notified when record is inserted or updated. - * @return root node which may have changed. - * @throws IOException thrown if IO error occurs - */ - VarKeyNode putRecord(Record record, Table table) throws IOException { + @Override + public VarKeyNode putRecord(Record record, Table table) throws IOException { Field key = record.getKeyField(); int index = getKeyIndex(key); @@ -318,7 +314,7 @@ class VarKeyRecordNode extends VarKeyNode { index = -index - 1; if (insertRecord(index, record)) { if (index == 0 && parent != null) { - parent.keyChanged(getKey(1), key, this); + parent.keyChanged(getKeyField(1), key, this); } if (table != null) { table.insertedRecord(record); @@ -359,7 +355,8 @@ class VarKeyRecordNode extends VarKeyNode { * @return root node which may have changed. * @throws IOException thrown if IO error occurs */ - VarKeyNode deleteRecord(Field key, Table table) throws IOException { + @Override + public VarKeyNode deleteRecord(Field key, Table table) throws IOException { // Handle non-existent key - do nothing int index = getKeyIndex(key); @@ -382,20 +379,14 @@ class VarKeyRecordNode extends VarKeyNode { // Notify parent of leftmost key change if (index == 0 && parent != null) { - parent.keyChanged(key, getKey(0), this); + parent.keyChanged(key, getKeyField(0), this); } return getRoot(); } - /** - * Get the first record whoose key is less than the specified key. - * @param key record key - * @param schema record data schema - * @return Record requested or null if record not found. - * @throws IOException thrown if IO error occurs - */ - Record getRecordBefore(Field key, Schema schema) throws IOException { + @Override + public Record getRecordBefore(Field key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { index = -index - 2; @@ -410,14 +401,8 @@ class VarKeyRecordNode extends VarKeyNode { return getRecord(schema, index); } - /** - * Get the first record whoose key is greater than the specified key. - * @param key record key - * @param schema record data schema - * @return Record requested or null if record not found. - * @throws IOException thrown if IO error occurs - */ - Record getRecordAfter(Field key, Schema schema) throws IOException { + @Override + public Record getRecordAfter(Field key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { index = -(index + 1); @@ -432,15 +417,8 @@ class VarKeyRecordNode extends VarKeyNode { return getRecord(schema, index); } - /** - * Get the first record whoose key is less than or equal to the specified - * key. - * @param key record key - * @param schema record data schema - * @return Record requested or null if record not found. - * @throws IOException thrown if IO error occurs - */ - Record getRecordAtOrBefore(Field key, Schema schema) throws IOException { + @Override + public Record getRecordAtOrBefore(Field key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { index = -index - 2; @@ -452,15 +430,8 @@ class VarKeyRecordNode extends VarKeyNode { return getRecord(schema, index); } - /** - * Get the first record whoose key is greater than or equal to the specified - * key. - * @param key record key - * @param schema record data schema - * @return Record requested or null if record not found. - * @throws IOException thrown if IO error occurs - */ - Record getRecordAtOrAfter(Field key, Schema schema) throws IOException { + @Override + public Record getRecordAtOrAfter(Field key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) { index = -(index + 1); @@ -484,23 +455,25 @@ class VarKeyRecordNode extends VarKeyNode { return new VarKeyRecordNode(nodeMgr, prevLeafId, nextLeafId, keyType); } - /* - * @see ghidra.framework.store.db.VarKeyNode#getKey(int) - */ @Override - Field getKey(int index) throws IOException { + public Field getKeyField(int index) throws IOException { Field key = keyType.newField(); - key.read(buffer, buffer.getInt(HEADER_SIZE + (index * ENTRY_SIZE))); + key.read(buffer, getKeyOffset(index)); return key; } + @Override + public int getKeyOffset(int index) { + return buffer.getInt(HEADER_SIZE + (index * ENTRY_SIZE)); + } + /** * Get the record data offset within the buffer * @param index key index * @return record data offset */ private int getRecordDataOffset(int index) throws IOException { - int offset = buffer.getInt(HEADER_SIZE + (index * ENTRY_SIZE)); + int offset = getKeyOffset(index); return offset + keyType.readLength(buffer, offset); } @@ -552,13 +525,13 @@ class VarKeyRecordNode extends VarKeyNode { /** * Get the length of a stored record with key. - * @param keyIndex key index associated with record. + * @param index key index associated with record. */ - private int getFullRecordLength(int keyIndex) { - if (keyIndex == 0) { + private int getFullRecordLength(int index) { + if (index == 0) { return buffer.length() - getRecordKeyOffset(0); } - return getRecordKeyOffset(keyIndex - 1) - getRecordKeyOffset(keyIndex); + return getRecordKeyOffset(index - 1) - getRecordKeyOffset(index); } /** @@ -600,8 +573,9 @@ class VarKeyRecordNode extends VarKeyNode { * @param index key index * @return Record */ - Record getRecord(Schema schema, int index) throws IOException { - Field key = getKey(index); + @Override + public Record getRecord(Schema schema, int index) throws IOException { + Field key = getKeyField(index); Record record = schema.createRecord(key); if (hasIndirectStorage(index)) { int bufId = buffer.getInt(getRecordDataOffset(index)); @@ -614,14 +588,16 @@ class VarKeyRecordNode extends VarKeyNode { return record; } - /** - * Get the record identified by the specified key. - * @param key record key - * @param schema record data schema - * @return Record requested or null if record not found. - * @throws IOException thrown if IO error occurs - */ - Record getRecord(Field key, Schema schema) throws IOException { + @Override + public int getRecordOffset(int index) throws IOException { + if (hasIndirectStorage(index)) { + return -buffer.getInt(getRecordDataOffset(index)); + } + return getRecordDataOffset(index); + } + + @Override + public Record getRecord(Field key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) return null; @@ -658,7 +634,7 @@ class VarKeyRecordNode extends VarKeyNode { /** * Split the contents of this leaf node; placing the right half of the records into the * empty leaf node provided. - * @param newRightLeaf empty right sibling leaf + * @param rightNode empty right sibling leaf */ private void splitData(VarKeyRecordNode rightNode) { @@ -752,12 +728,12 @@ class VarKeyRecordNode extends VarKeyNode { /** * Inserts the record at the given index if there is sufficient space in * the buffer. - * @param keyIndex insertion index + * @param index insertion index * @param record record to be inserted * @return true if the record was successfully inserted. * @throws IOException thrown if IO error occurs */ - private boolean insertRecord(int keyIndex, Record record) throws IOException { + private boolean insertRecord(int index, Record record) throws IOException { Field key = record.getKeyField(); int keyLen = key.length(); @@ -776,11 +752,11 @@ class VarKeyRecordNode extends VarKeyNode { return false; // insufficient space for record storage // Make room for new record - int offset = moveRecords(keyIndex, -(len + keyLen)); + int offset = moveRecords(index, -(len + keyLen)); // Make room for new key/offset entry - int start = HEADER_SIZE + (keyIndex * ENTRY_SIZE); - len = (keyCount - keyIndex) * ENTRY_SIZE; + int start = HEADER_SIZE + (index * ENTRY_SIZE); + len = (keyCount - index) * ENTRY_SIZE; buffer.move(start, start + ENTRY_SIZE, len); // Store new record key/offset @@ -798,7 +774,7 @@ class VarKeyRecordNode extends VarKeyNode { else { record.write(buffer, offset + keyLen); } - enableIndirectStorage(keyIndex, useIndirect); + enableIndirectStorage(index, useIndirect); return true; } @@ -809,7 +785,8 @@ class VarKeyRecordNode extends VarKeyNode { * @param index record index * @throws IOException thrown if IO error occurs */ - void remove(int index) throws IOException { + @Override + public void remove(int index) throws IOException { if (index < 0 || index >= keyCount) throw new AssertException(); @@ -833,7 +810,8 @@ class VarKeyRecordNode extends VarKeyNode { * @return root node which may have changed. * @throws IOException thrown if IO error occurs */ - VarKeyNode removeLeaf() throws IOException { + @Override + public VarKeyNode removeLeaf() throws IOException { // Remove all chained buffers associated with this leaf for (int index = 0; index < keyCount; ++index) { @@ -842,7 +820,7 @@ class VarKeyRecordNode extends VarKeyNode { } } - Field key = getKey(0); + Field key = getKeyField(0); int prevBufferId = buffer.getInt(PREV_LEAF_ID_OFFSET); int nextBufferId = buffer.getInt(NEXT_LEAF_ID_OFFSET); if (prevBufferId >= 0) { @@ -870,9 +848,6 @@ class VarKeyRecordNode extends VarKeyNode { chainedBuffer.delete(); } - /* - * @see ghidra.framework.store.db.VarKeyNode#delete() - */ @Override public void delete() throws IOException { @@ -890,9 +865,6 @@ class VarKeyRecordNode extends VarKeyNode { nodeMgr.deleteNode(this); } - /* - * @see ghidra.framework.store.db.BTreeNode#getBufferReferences() - */ @Override public int[] getBufferReferences() { IntArrayList idList = new IntArrayList(); @@ -903,6 +875,7 @@ class VarKeyRecordNode extends VarKeyNode { idList.add(buffer.getInt(offset)); } catch (IOException e) { + // ignore } } } diff --git a/Ghidra/Framework/DB/src/main/java/db/VarRecNode.java b/Ghidra/Framework/DB/src/main/java/db/VarRecNode.java index 946a149f6b..5042035960 100644 --- a/Ghidra/Framework/DB/src/main/java/db/VarRecNode.java +++ b/Ghidra/Framework/DB/src/main/java/db/VarRecNode.java @@ -15,12 +15,11 @@ */ package db; -import ghidra.util.datastruct.IntArrayList; -import ghidra.util.exception.AssertException; - import java.io.IOException; import db.buffers.DataBuffer; +import ghidra.util.datastruct.IntArrayList; +import ghidra.util.exception.AssertException; /** * VarRecNode is an implementation of a BTree leaf node @@ -37,19 +36,19 @@ import db.buffers.DataBuffer; * whose 4-byte integer buffer ID has been stored within this leaf at the record offset. */ class VarRecNode extends LongKeyRecordNode { - + private static final int HEADER_SIZE = RECORD_LEAF_HEADER_SIZE; - + private static final int KEY_SIZE = 8; private static final int OFFSET_SIZE = 4; private static final int INDIRECT_OPTION_SIZE = 1; private static final int ENTRY_SIZE = KEY_SIZE + OFFSET_SIZE + INDIRECT_OPTION_SIZE; - + private static final int KEY_BASE_OFFSET = HEADER_SIZE; private static final int DATA_OFFSET_BASE_OFFSET = KEY_BASE_OFFSET + KEY_SIZE; private static final int IND_OPTION_BASE_OFFSET = DATA_OFFSET_BASE_OFFSET + OFFSET_SIZE; - + /** * Construct an existing long-key variable-length record leaf node. * @param nodeMgr table node manager instance @@ -58,7 +57,7 @@ class VarRecNode extends LongKeyRecordNode { VarRecNode(NodeMgr nodeMgr, DataBuffer buf) { super(nodeMgr, buf); } - + /** * Construct a new long-key variable-length record leaf node. * @param nodeMgr table node manager instance @@ -70,37 +69,27 @@ class VarRecNode extends LongKeyRecordNode { super(nodeMgr, NodeMgr.LONGKEY_VAR_REC_NODE, prevLeafId, nextLeafId); } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#createNewLeaf() - */ @Override - LongKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { + LongKeyRecordNode createNewLeaf(int prevLeafId, int nextLeafId) throws IOException { return new VarRecNode(nodeMgr, prevLeafId, nextLeafId); } - - /* - * @see ghidra.framework.store.db.LongKeyNode#getKey(int) - */ + @Override - long getKey(int index) { - return buffer.getLong(KEY_BASE_OFFSET + (index * ENTRY_SIZE)); + long getKey(int index) { + return buffer.getLong(getKeyOffset(index)); } - -// /** -// * Store a key at the specified index -// * @param index key index -// * @param key key value -// */ -// private void putKey(int index, long key) { -// buffer.putLong(KEY_BASE_OFFSET + (index * ENTRY_SIZE), key); -// } - + + @Override + public int getKeyOffset(int index) { + return KEY_BASE_OFFSET + (index * ENTRY_SIZE); + } + /** * Get the record offset within the buffer * @param index key index * @return record offset */ - private int getRecordOffset(int index) { + int getRecordDataOffset(int index) { return buffer.getInt(DATA_OFFSET_BASE_OFFSET + (index * ENTRY_SIZE)); } @@ -109,10 +98,10 @@ class VarRecNode extends LongKeyRecordNode { * @param index key index * @param offset record offset */ - private void putRecordOffset(int index, int offset) { + private void putRecordDataOffset(int index, int offset) { buffer.putInt(DATA_OFFSET_BASE_OFFSET + (index * ENTRY_SIZE), offset); } - + /** * Determine if a record is utilizing a chained DBBuffer for data storage * @param index key index @@ -128,42 +117,41 @@ class VarRecNode extends LongKeyRecordNode { * @param state indirect storage used (true) or not used (false) */ private void enableIndirectStorage(int index, boolean state) { - buffer.putByte(IND_OPTION_BASE_OFFSET + (index * ENTRY_SIZE), - state ? (byte)1 : (byte)0); + buffer.putByte(IND_OPTION_BASE_OFFSET + (index * ENTRY_SIZE), state ? (byte) 1 : (byte) 0); } - + /** * @return unused free space within node */ private int getFreeSpace() { - return (keyCount == 0 ? buffer.length() : getRecordOffset(keyCount - 1)) - - (keyCount * ENTRY_SIZE) - RECORD_LEAF_HEADER_SIZE; + return (keyCount == 0 ? buffer.length() : getRecordDataOffset(keyCount - 1)) - + (keyCount * ENTRY_SIZE) - RECORD_LEAF_HEADER_SIZE; } /** * Get the length of a stored record. - * @param keyIndex key index associated with record. + * @param index index associated with record. */ - private int getRecordLength(int keyIndex) { - if (keyIndex == 0) { - return buffer.length() - getRecordOffset(0); + private int getRecordLength(int index) { + if (index == 0) { + return buffer.length() - getRecordDataOffset(0); } - return getRecordOffset(keyIndex - 1) - getRecordOffset(keyIndex); + return getRecordDataOffset(index - 1) - getRecordDataOffset(index); } - + /** * Get the length of a stored record. Optimized if record offset * already known. - * @param keyIndex key index associated with record. + * @param index index associated with record. * @param offset record offset */ - private int getRecordLength(int keyIndex, int offset) { - if (keyIndex == 0) { + private int getRecordLength(int index, int offset) { + if (index == 0) { return buffer.length() - offset; } - return getRecordOffset(keyIndex - 1) - offset; + return getRecordDataOffset(index - 1) - offset; } - + /** * Move all record data, starting with index, by the specified offset amount. * If the node contains 5 records, an index of 3 would shift the record data @@ -174,76 +162,77 @@ class VarRecNode extends LongKeyRecordNode { * @return insertion offset immediately following moved block. */ private int moveRecords(int index, int offset) { - + int lastIndex = keyCount - 1; - + // No movement needed for appended record if (index == keyCount) { if (index == 0) { - return buffer.length() + offset; + return buffer.length() + offset; } - return getRecordOffset(lastIndex) + offset; + return getRecordDataOffset(lastIndex) + offset; } - + // Determine block to be moved - int start = getRecordOffset(lastIndex); - int end = (index == 0) ? buffer.length() : getRecordOffset(index - 1); + int start = getRecordDataOffset(lastIndex); + int end = (index == 0) ? buffer.length() : getRecordDataOffset(index - 1); int len = end - start; - + // Move record data buffer.move(start, start + offset, len); - + // Adjust stored offsets for (int i = index; i < keyCount; i++) { - putRecordOffset(i, getRecordOffset(i) + offset); + putRecordDataOffset(i, getRecordDataOffset(i) + offset); } return end + offset; } - - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#getRecord(ghidra.framework.store.db.Schema, int) - */ + @Override - Record getRecord(Schema schema, int index) throws IOException { + public Record getRecord(Schema schema, int index) throws IOException { long key = getKey(index); Record record = schema.createRecord(key); if (hasIndirectStorage(index)) { - int bufId = buffer.getInt(getRecordOffset(index)); - ChainedBuffer chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), - bufId); + int bufId = buffer.getInt(getRecordDataOffset(index)); + ChainedBuffer chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), bufId); record.read(chainedBuffer, 0); } else { - record.read(buffer, getRecordOffset(index)); + record.read(buffer, getRecordDataOffset(index)); } - return record; + return record; } - - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#getRecord(long, ghidra.framework.store.db.Schema) - */ + @Override - Record getRecord(long key, Schema schema) throws IOException { + public int getRecordOffset(int index) throws IOException { + if (hasIndirectStorage(index)) { + return -buffer.getInt(getRecordDataOffset(index)); + } + return getRecordDataOffset(index); + } + + @Override + Record getRecord(long key, Schema schema) throws IOException { int index = getKeyIndex(key); if (index < 0) return null; return getRecord(schema, index); } - + /** * Find the index which represents the halfway point within the record data. * @return key index. */ private int getSplitIndex() { - - int halfway = ((keyCount == 0 ? buffer.length() : getRecordOffset(keyCount - 1)) - + buffer.length()) / 2; + + int halfway = ((keyCount == 0 ? buffer.length() : getRecordDataOffset(keyCount - 1)) + + buffer.length()) / 2; int min = 1; int max = keyCount - 1; - + while (min < max) { - int i = (min + max)/2; - int offset = getRecordOffset(i); + int i = (min + max) / 2; + int offset = getRecordDataOffset(i); if (offset == halfway) { return i; } @@ -257,59 +246,53 @@ class VarRecNode extends LongKeyRecordNode { return min; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#splitData(ghidra.framework.store.db.LongKeyRecordNode) - */ @Override - void splitData(LongKeyRecordNode newRightLeaf) { + void splitData(LongKeyRecordNode newRightLeaf) { VarRecNode rightNode = (VarRecNode) newRightLeaf; - + int splitIndex = getSplitIndex(); int count = keyCount - splitIndex; - int start = getRecordOffset(keyCount - 1); // start of block to be moved - int end = getRecordOffset(splitIndex - 1); // end of block to be moved + int start = getRecordDataOffset(keyCount - 1); // start of block to be moved + int end = getRecordDataOffset(splitIndex - 1); // end of block to be moved int splitLen = end - start; // length of block to be moved int rightOffset = buffer.length() - splitLen; // data offset within new leaf node - + // Copy data to new leaf node DataBuffer newBuf = rightNode.buffer; - newBuf.copy(rightOffset, buffer, start, splitLen); - newBuf.copy(KEY_BASE_OFFSET, buffer, KEY_BASE_OFFSET + (splitIndex * ENTRY_SIZE), count * ENTRY_SIZE); - + newBuf.copy(rightOffset, buffer, start, splitLen); + newBuf.copy(KEY_BASE_OFFSET, buffer, KEY_BASE_OFFSET + (splitIndex * ENTRY_SIZE), + count * ENTRY_SIZE); + // Fix record offsets in new leaf node int offsetCorrection = buffer.length() - end; for (int i = 0; i < count; i++) { - rightNode.putRecordOffset(i, rightNode.getRecordOffset(i) + offsetCorrection); + rightNode.putRecordDataOffset(i, rightNode.getRecordDataOffset(i) + offsetCorrection); } - + // Adjust key counts setKeyCount(keyCount - count); rightNode.setKeyCount(count); } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#updateRecord(int, ghidra.framework.store.db.Record) - */ @Override - LongKeyNode updateRecord(int index, Record record) throws IOException { - - int offset = getRecordOffset(index); + LongKeyNode updateRecord(int index, Record record) throws IOException { + + int offset = getRecordDataOffset(index); int oldLen = getRecordLength(index, offset); int len = record.length(); - + // Check for use of indirect chained record node(s) int maxRecordLength = ((buffer.length() - HEADER_SIZE) >> 2) - ENTRY_SIZE; // min 4 records per node boolean wasIndirect = hasIndirectStorage(index); boolean useIndirect = (len > maxRecordLength); - + if (useIndirect) { // Store record in chained buffers len = 4; ChainedBuffer chainedBuffer = null; if (wasIndirect) { - chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), - buffer.getInt(offset)); + chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), buffer.getInt(offset)); chainedBuffer.setSize(record.length(), false); } else { @@ -323,15 +306,15 @@ class VarRecNode extends LongKeyRecordNode { removeChainedBuffer(buffer.getInt(offset)); enableIndirectStorage(index, false); } - + // See if updated record will fit in current buffer if (useIndirect || len <= (getFreeSpace() + oldLen)) { - + // Overwrite record data - move other data if needed int dataShift = oldLen - len; if (dataShift != 0) { offset = moveRecords(index + 1, dataShift); - putRecordOffset(index, offset); + putRecordDataOffset(index, offset); } if (!useIndirect) { record.write(buffer, offset); @@ -340,22 +323,13 @@ class VarRecNode extends LongKeyRecordNode { } // Insufficient room for updated record - remove and re-add - long key = record.getKey(); + long key = record.getKey(); LongKeyRecordNode leaf = deleteRecord(key, null).getLeafNode(key); return leaf.putRecord(record, null); } - /** - * Insert the specified record at the specified key index. - * Existing data may be shifted within the buffer to make room for - * the new record. Parent must be notified if this changes the leftmost - * key. - * @param keyIndex - * @param record - * @throws IOException - */ @Override - boolean insertRecord(int keyIndex, Record record) throws IOException { + boolean insertRecord(int index, Record record) throws IOException { // Check for use of indirect chained record node(s) int len = record.length(); @@ -364,77 +338,74 @@ class VarRecNode extends LongKeyRecordNode { if (useIndirect) { len = 4; } - + if ((len + ENTRY_SIZE) > getFreeSpace()) return false; // insufficient space for record storage // Make room for new record - int offset = moveRecords(keyIndex, -len); - + int offset = moveRecords(index, -len); + // Make room for new key/offset entry - int start = KEY_BASE_OFFSET + (keyIndex * ENTRY_SIZE); - len = (keyCount - keyIndex) * ENTRY_SIZE; + int start = KEY_BASE_OFFSET + (index * ENTRY_SIZE); + len = (keyCount - index) * ENTRY_SIZE; buffer.move(start, start + ENTRY_SIZE, len); - + // Store new record key/offset buffer.putLong(start, record.getKey()); buffer.putInt(start + KEY_SIZE, offset); setKeyCount(keyCount + 1); - + // Store record data if (useIndirect) { - ChainedBuffer chainedBuffer = new ChainedBuffer(record.length(), nodeMgr.getBufferMgr()); + ChainedBuffer chainedBuffer = + new ChainedBuffer(record.length(), nodeMgr.getBufferMgr()); buffer.putInt(offset, chainedBuffer.getId()); record.write(chainedBuffer, 0); } else { record.write(buffer, offset); } - enableIndirectStorage(keyIndex, useIndirect); + enableIndirectStorage(index, useIndirect); return true; } - /* - * @see ghidra.framework.store.db.LongKeyRecordNode#remove(int) - */ @Override - void remove(int index) throws IOException { + public void remove(int index) throws IOException { + + if (index < 0 || index >= keyCount) + throw new AssertException(); -if (index < 0 || index >= keyCount) -throw new AssertException(); - if (hasIndirectStorage(index)) { - removeChainedBuffer(buffer.getInt(getRecordOffset(index))); + removeChainedBuffer(buffer.getInt(getRecordDataOffset(index))); enableIndirectStorage(index, false); } - + int len = getRecordLength(index); moveRecords(index + 1, len); - int start = KEY_BASE_OFFSET + ((index+1) * ENTRY_SIZE); + int start = KEY_BASE_OFFSET + ((index + 1) * ENTRY_SIZE); len = (keyCount - index - 1) * ENTRY_SIZE; buffer.move(start, start - ENTRY_SIZE, len); - setKeyCount(keyCount-1); + setKeyCount(keyCount - 1); } - - + /** * Removes this leaf and all associated chained buffers. * @see db.LongKeyRecordNode#removeLeaf() */ @Override - LongKeyNode removeLeaf() throws IOException { - + public LongKeyNode removeLeaf() throws IOException { + // Remove all chained buffers associated with this leaf for (int index = 0; index < keyCount; ++index) { if (hasIndirectStorage(index)) { - removeChainedBuffer(buffer.getInt(getRecordOffset(index))); + removeChainedBuffer(buffer.getInt(getRecordDataOffset(index))); } } return super.removeLeaf(); } - + /** * Remove a chained buffer. * @param bufferId chained buffer ID @@ -443,35 +414,30 @@ throw new AssertException(); ChainedBuffer chainedBuffer = new ChainedBuffer(nodeMgr.getBufferMgr(), bufferId); chainedBuffer.delete(); } - - /* - * @see ghidra.framework.store.db.LongKeyNode#delete() - */ + @Override - public void delete() throws IOException { - + public void delete() throws IOException { + // Remove all chained buffers associated with this node. for (int index = 0; index < keyCount; index++) { if (hasIndirectStorage(index)) { - int offset = getRecordOffset(index); + int offset = getRecordDataOffset(index); int bufferId = buffer.getInt(offset); removeChainedBuffer(bufferId); buffer.putInt(offset, -1); } } - + // Remove this node nodeMgr.deleteNode(this); } - - /* - * @see ghidra.framework.store.db.BTreeNode#getBufferReferences() - */ + + @Override public int[] getBufferReferences() { IntArrayList idList = new IntArrayList(); for (int i = 0; i < keyCount; i++) { if (hasIndirectStorage(i)) { - int offset = getRecordOffset(i); + int offset = getRecordDataOffset(i); idList.add(buffer.getInt(offset)); } } diff --git a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java index a157bbe596..f896f77b06 100644 --- a/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java +++ b/Ghidra/Framework/DB/src/main/java/db/buffers/BufferMgr.java @@ -28,7 +28,6 @@ import ghidra.util.SystemUtilities; import ghidra.util.datastruct.ObjectArray; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; /** * BufferMgr provides low-level buffer management and caching. @@ -176,7 +175,7 @@ public class BufferMgr { * @param sourceFile buffer file * @throws IOException if source or cache file access error occurs */ - public BufferMgr(BufferFile sourceFile) throws FileNotFoundException, IOException { + public BufferMgr(BufferFile sourceFile) throws IOException { this(sourceFile, DEFAULT_BUFFER_SIZE, DEFAULT_CACHE_SIZE, DEFAULT_CHECKPOINT_COUNT); } @@ -188,8 +187,7 @@ public class BufferMgr { * @param maxUndos maximum number of checkpoints retained for undo (Minimum=1). * @throws IOException if source or cache file access error occurs */ - public BufferMgr(BufferFile sourceFile, long approxCacheSize, int maxUndos) - throws FileNotFoundException, IOException { + public BufferMgr(BufferFile sourceFile, long approxCacheSize, int maxUndos) throws IOException { this(sourceFile, 0, approxCacheSize, maxUndos); } @@ -202,9 +200,9 @@ public class BufferMgr { * @param maxUndos maximum number of checkpoints retained for undo (Minimum=1). * @throws IOException if source or cache file access error occurs */ - private BufferMgr(BufferFile sourceFile, int requestedbufferSize, long approxCacheSize, + private BufferMgr(BufferFile sourceFile, int requestedBufferSize, long approxCacheSize, int maxUndos) throws FileNotFoundException, IOException { - bufferSize = requestedbufferSize; + bufferSize = requestedBufferSize; if (sourceFile != null) { this.sourceFile = sourceFile; int cnt = sourceFile.getIndexCount(); @@ -362,6 +360,9 @@ public class BufferMgr { /** * Get file parameter + * @param name parameter name/key + * @return parameter value + * @throws NoSuchElementException if parameter not found */ int getParameter(String name) throws NoSuchElementException { return cacheFile.getParameter(name); @@ -369,8 +370,8 @@ public class BufferMgr { /** * Set file parameter - * @param name - * @param value + * @param name parameter name/key + * @param value parameter value */ void setParameter(String name, int value) { cacheFile.setParameter(name, value); @@ -391,7 +392,8 @@ public class BufferMgr { * buffer file. * This method should be called when this buffer manager instance * is no longer needed. - * @param keepRecoveryData + * @param keepRecoveryData true if existing snapshot recovery files + * should not be deleted. */ public void dispose(boolean keepRecoveryData) { @@ -626,7 +628,6 @@ public class BufferMgr { /** * Remove a buffer node from memory cache. * @param node buffer node - * @return buffer object, or null if buffer node was not cached */ private void removeFromCache(BufferNode node) { if (node.buffer != null) { @@ -1010,9 +1011,11 @@ public class BufferMgr { } /** - * Return buffer. + * Release buffer back to buffer manager. * After invoking this method, the buffer object should not * be used and all references should be dropped. + * @param buf data buffer + * @throws IOException if IO error occurs */ public void releaseBuffer(DataBuffer buf) throws IOException { @@ -1031,9 +1034,9 @@ public class BufferMgr { /** * Handle exception which indicates a potential corruption of the BufferMgr state - * @param exception - * @param errorText - * @throws IOException + * @param exception exception + * @param errorText associated error text + * @throws IOException exception thrown if instance of IOException */ private void handleCorruptionException(Exception exception, String errorText) throws IOException { @@ -1182,7 +1185,7 @@ public class BufferMgr { } /** - * Returns true if unsaved "buffer" changes exist. + * @return true if unsaved "buffer" changes exist. * If no changes have been made, or all changes have been * "undone", false will be returned. Parameter changes * are no considered. @@ -1194,9 +1197,6 @@ public class BufferMgr { /** * Create a new checkpoint node list. * The redo stack will be cleared. - * @param force if true the checkpoint will be performed regardless of - * the lock count. - * @return true if checkpoint successful, or false if buffers are read-only */ private void startCheckpoint() { @@ -1235,21 +1235,25 @@ public class BufferMgr { } /** - * Returns number of undo-able transactions + * @return number of undo-able transactions */ public int getAvailableUndoCount() { return checkpointHeads.size() - 1; } /** - * Returns the number of redo-able transactions + * @return the number of redo-able transactions */ public int getAvailableRedoCount() { return redoCheckpointHeads.size(); } /** - * Backup to previous checkpoint. + * Backup to previous checkpoint. Method should not be invoked + * when one or more buffers are locked. + * @param redoable true if currrent checkpoint should be moved to redo stack + * @return true if successful else false + * @throws IOException if IO error occurs */ public boolean undo(boolean redoable) throws IOException { synchronized (snapshotLock) { @@ -1337,7 +1341,9 @@ public class BufferMgr { } /** - * Redo next checkpoint. + * Redo next checkpoint. Method should not be invoked + * when one or more buffers are locked. + * @return true if successful else false */ public boolean redo() { synchronized (snapshotLock) { @@ -1414,7 +1420,8 @@ public class BufferMgr { } /** - * Returns true if save operation can be performed. + * @return true if save operation can be performed. + * @throws IOException if IO error occurs */ public boolean canSave() throws IOException { if (corruptedState) { @@ -1427,7 +1434,7 @@ public class BufferMgr { } /** - * Returns true if buffers have been modified since opening or since + * @return true if buffers have been modified since opening or since * last snapshot. */ public synchronized boolean modifiedSinceSnapshot() { @@ -1440,6 +1447,8 @@ public class BufferMgr { * made since the last version. * @param monitor task monitor * @return true if snapshot successful, false if + * @throws IOException if IO error occurs + * @throws CancelledException if task monitor is cancelled */ public boolean takeRecoverySnapshot(DBChangeSet changeSet, TaskMonitor monitor) throws IOException, CancelledException { @@ -1548,7 +1557,8 @@ public class BufferMgr { * Returns the recovery changeSet data file for reading or null if one is not available. * The caller must dispose of the returned file before peforming generating any new * recovery snapshots. - * @throws IOException + * @return recovery change set buffer file + * @throws IOException if IO error occurs */ public LocalBufferFile getRecoveryChangeSetFile() throws IOException { if (recoveryMgr != null) { @@ -1580,6 +1590,9 @@ public class BufferMgr { * If recovery is cancelled, this buffer manager must be disposed. * since the underlying state will be corrupt. * @param monitor task monitor + * @return true if recovery successful else false + * @throws IOException if IO error occurs + * @throws CancelledException if task monitor is cancelled */ public boolean recover(TaskMonitor monitor) throws IOException, CancelledException { synchronized (snapshotLock) { @@ -1600,9 +1613,12 @@ public class BufferMgr { /** * Recover data from recovery file - * @param recoveryFile - * @param monitor - * @throws CancelledException + * @param recoveryFile recovery file + * @param recoveryIndex recovery index (0 or 1) which corresponds to + * recoveryFile. + * @param monitor task monitor + * @throws IOException if IO error occurs + * @throws CancelledException if task monitor is cancelled */ synchronized void recover(RecoveryFile recoveryFile, int recoveryIndex, TaskMonitor monitor) throws IOException, CancelledException { @@ -1755,7 +1771,7 @@ public class BufferMgr { } if (monitor == null) { - monitor = TaskMonitorAdapter.DUMMY_MONITOR; + monitor = TaskMonitor.DUMMY; } boolean oldCancelState = monitor.isCancelEnabled(); @@ -1840,7 +1856,7 @@ public class BufferMgr { } if (monitor == null) { - monitor = TaskMonitorAdapter.DUMMY_MONITOR; + monitor = TaskMonitor.DUMMY; } int indexCnt = indexProvider.getIndexCount(); @@ -1871,7 +1887,7 @@ public class BufferMgr { * Write all changes to the specified outFile * @param outFile output buffer file * @param monitor task monitor - * @throws IOException + * @throws IOException if IO error occurs * @throws CancelledException thrown if task cancelled */ private void doSave(BufferFile outFile, TaskMonitor monitor) @@ -1880,7 +1896,7 @@ public class BufferMgr { int preSaveCnt = outFile.getIndexCount(); if (monitor == null) { - monitor = TaskMonitorAdapter.DUMMY_MONITOR; + monitor = TaskMonitor.DUMMY; } monitor.initialize(indexCnt); monitor.setMessage("Saving file..."); diff --git a/Ghidra/Framework/DB/src/main/java/db/buffers/DataBuffer.java b/Ghidra/Framework/DB/src/main/java/db/buffers/DataBuffer.java index 7497b69c1f..0cff6aa14a 100644 --- a/Ghidra/Framework/DB/src/main/java/db/buffers/DataBuffer.java +++ b/Ghidra/Framework/DB/src/main/java/db/buffers/DataBuffer.java @@ -143,34 +143,22 @@ public class DataBuffer implements Buffer, Externalizable { empty = state; } - /* - * @see ghidra.framework.store.Buffer#length() - */ @Override public int length() { return data.length; } - /* - * @see ghidra.framework.store.Buffer#get(int, byte[], int, int) - */ @Override public void get(int offset, byte[] bytes, int dataOffset, int length) throws ArrayIndexOutOfBoundsException { System.arraycopy(data, offset, bytes, dataOffset, length); } - /* - * @see ghidra.framework.store.Buffer#get(int, byte[]) - */ @Override public void get(int offset, byte[] bytes) { System.arraycopy(data, offset, bytes, 0, bytes.length); } - /* - * @see ghidra.framework.store.Buffer#get(int, int) - */ @Override public byte[] get(int offset, int length) throws ArrayIndexOutOfBoundsException { byte[] bytes = new byte[length]; @@ -178,34 +166,22 @@ public class DataBuffer implements Buffer, Externalizable { return bytes; } - /* - * @see ghidra.framework.store.Buffer#getByte(int) - */ @Override public byte getByte(int offset) { return data[offset]; } - /* - * @see ghidra.framework.store.Buffer#getInt(int) - */ @Override public int getInt(int offset) { return ((data[offset] & 0xff) << 24) | ((data[++offset] & 0xff) << 16) | ((data[++offset] & 0xff) << 8) | (data[++offset] & 0xff); } - /* - * @see ghidra.framework.store.Buffer#getShort(int) - */ @Override public short getShort(int offset) { return (short) (((data[offset] & 0xff) << 8) | (data[++offset] & 0xff)); } - /* - * @see ghidra.framework.store.Buffer#getLong(int) - */ @Override public long getLong(int offset) { return (((long) data[offset] & 0xff) << 56) | (((long) data[++offset] & 0xff) << 48) | @@ -214,9 +190,6 @@ public class DataBuffer implements Buffer, Externalizable { (((long) data[++offset] & 0xff) << 8) | ((long) data[++offset] & 0xff); } - /* - * @see ghidra.framework.store.Buffer#put(int, byte[], int, int) - */ @Override public int put(int offset, byte[] bytes, int dataOffset, int length) { dirty = true; @@ -224,9 +197,6 @@ public class DataBuffer implements Buffer, Externalizable { return offset + length; } - /* - * @see ghidra.framework.store.Buffer#put(int, byte[]) - */ @Override public int put(int offset, byte[] bytes) { dirty = true; @@ -234,9 +204,6 @@ public class DataBuffer implements Buffer, Externalizable { return offset + bytes.length; } - /* - * @see ghidra.framework.store.Buffer#putByte(int, byte) - */ @Override public int putByte(int offset, byte b) { dirty = true; @@ -244,9 +211,6 @@ public class DataBuffer implements Buffer, Externalizable { return ++offset; } - /* - * @see ghidra.framework.store.Buffer#putInt(int, int) - */ @Override public int putInt(int offset, int v) { dirty = true; @@ -257,9 +221,6 @@ public class DataBuffer implements Buffer, Externalizable { return ++offset; } - /* - * @see ghidra.framework.store.Buffer#putShort(int, short) - */ @Override public int putShort(int offset, short v) { dirty = true; @@ -268,9 +229,6 @@ public class DataBuffer implements Buffer, Externalizable { return ++offset; } - /* - * @see ghidra.framework.store.Buffer#putLong(int, long) - */ @Override public int putLong(int offset, long v) { dirty = true; @@ -380,9 +338,8 @@ public class DataBuffer implements Buffer, Externalizable { int compressedDataOffset = 0; while (!deflate.finished() && compressedDataOffset < compressedData.length) { - compressedDataOffset += - deflate.deflate(compressedData, compressedDataOffset, compressedData.length - - compressedDataOffset, Deflater.SYNC_FLUSH); + compressedDataOffset += deflate.deflate(compressedData, compressedDataOffset, + compressedData.length - compressedDataOffset, Deflater.SYNC_FLUSH); } if (!deflate.finished()) { @@ -423,6 +380,30 @@ public class DataBuffer implements Buffer, Externalizable { } } + /** + * Perform an unsigned data comparison + * @param otherData other data to be compared + * @param offset offset within this buffer + * @param len length of data within this buffer + * @return unsigned comparison result + * @throws ArrayIndexOutOfBoundsException if specified region is not + * contained within this buffer. + */ + public int unsignedCompareTo(byte[] otherData, int offset, int len) { + + int otherLen = otherData.length; + int otherOffset = 0; + int n = Math.min(len, otherLen); + while (n-- != 0) { + int b = data[offset++] & 0xff; + int otherByte = otherData[otherOffset++] & 0xff; + if (b != otherByte) { + return b - otherByte; + } + } + return len - otherLen; + } + /** * Inflate compressedData into a properly sized data array. * @param compressedData array containing compressed data diff --git a/Ghidra/Framework/DB/src/test/java/db/DBFieldMapTest.java b/Ghidra/Framework/DB/src/test/java/db/DBFieldMapTest.java deleted file mode 100644 index 29f0176b8a..0000000000 --- a/Ghidra/Framework/DB/src/test/java/db/DBFieldMapTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.NoSuchElementException; - -import org.junit.*; - -import generic.test.AbstractGenericTest; -import ghidra.util.LongIterator; - -public class DBFieldMapTest extends AbstractGenericTest { - - private DBFieldMap map; - - public DBFieldMapTest() { - - } - - /* - * @see TestCase#setUp() - */ - @Before - public void setUp() throws Exception { - map = new DBFieldMap(StringField.class, 1); - } - - /* - * @see TestCase#tearDown() - */ - @After - public void tearDown() throws Exception { - if (map != null) { - map.dispose(); - } - } - - private void addEntries() { - map.addEntry(new StringField("f3"), 5); - map.addEntry(new StringField("f2"), 3); - map.addEntry(new StringField("f1"), 1); - map.addEntry(new StringField("f2"), 2); - map.addEntry(new StringField("f4"), 6); - map.addEntry(new StringField("f3"), 4); - } - - @Test - public void testAddEntry() { - - addEntries(); - - assertTrue(map.hasEntry(new StringField("f1"), 1)); - assertTrue(map.hasEntry(new StringField("f2"), 2)); - assertTrue(map.hasEntry(new StringField("f2"), 3)); - assertTrue(map.hasEntry(new StringField("f3"), 4)); - assertTrue(map.hasEntry(new StringField("f3"), 5)); - assertTrue(map.hasEntry(new StringField("f4"), 6)); - } - - @Test - public void testDeleteEntry() { - - addEntries(); - - map.deleteEntry(new StringField("f2"), 2); - map.deleteEntry(new StringField("f3"), 4); - - assertTrue(map.hasEntry(new StringField("f1"), 1)); - assertTrue(!map.hasEntry(new StringField("f2"), 2)); - assertTrue(map.hasEntry(new StringField("f2"), 3)); - assertTrue(!map.hasEntry(new StringField("f3"), 4)); - assertTrue(map.hasEntry(new StringField("f3"), 5)); - assertTrue(map.hasEntry(new StringField("f4"), 6)); - } - - @Test - public void testIterator() { - - addEntries(); - - LongIterator iter = map.iterator(); - - assertEquals(1, iter.next()); - assertEquals(2, iter.next()); - assertEquals(3, iter.next()); - assertEquals(4, iter.next()); - assertEquals(5, iter.next()); - assertEquals(6, iter.next()); - - try { - iter.next(); - Assert.fail(); - } - catch (NoSuchElementException e) { - // expected - } - - assertEquals(6, iter.previous()); - assertEquals(5, iter.previous()); - assertEquals(4, iter.previous()); - assertEquals(3, iter.previous()); - assertEquals(2, iter.previous()); - assertEquals(1, iter.previous()); - - try { - iter.previous(); - Assert.fail(); - } - catch (NoSuchElementException e) { - // expected - } - - assertEquals(1, iter.next()); - assertEquals(2, iter.next()); - assertEquals(3, iter.next()); - assertEquals(4, iter.next()); - - assertEquals(4, iter.previous()); - assertEquals(3, iter.previous()); - assertEquals(2, iter.previous()); - assertEquals(1, iter.previous()); - - } -} diff --git a/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyIndexedTableTest.java b/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyIndexedTableTest.java new file mode 100644 index 0000000000..ceab776c42 --- /dev/null +++ b/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyIndexedTableTest.java @@ -0,0 +1,1204 @@ +/* ### + * 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 db; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import org.junit.*; + +import db.buffers.*; +import generic.test.AbstractGenericTest; +import ghidra.util.exception.CancelledException; +import utilities.util.FileUtilities; + +public class DBFixedKeyIndexedTableTest extends AbstractGenericTest { + + private static final int BUFFER_SIZE = 2048;// keep small for chained buffer testing + private static final int CACHE_SIZE = 4 * 1024 * 1024; + + private static final int ITER_REC_CNT = 1000; + + private static final String table1Name = "TABLE1"; + + private File testDir; + private static final String dbName = "test"; + + private BufferFileManager fileMgr; + private DBHandle dbh; + private BufferFile bfile; + + @Before + public void setUp() throws Exception { + + testDir = createTempDirectory(getClass().getSimpleName()); + dbh = new DBHandle(BUFFER_SIZE, CACHE_SIZE); + } + + @After + public void tearDown() throws Exception { + if (dbh != null) { + dbh.close(); + } + if (bfile != null) { + bfile.close(); + } + FileUtilities.deleteDir(testDir); + + } + + private void saveAsAndReopen(String name) throws IOException { + try { + BufferFileManager mgr = DBTestUtils.getBufferFileManager(testDir, name); + BufferFile bf = new LocalManagedBufferFile(dbh.getBufferSize(), mgr, -1); + dbh.saveAs(bf, true, null); + dbh.close(); + fileMgr = mgr; + } + catch (CancelledException e) { + Assert.fail("Should not happen"); + } + bfile = new LocalManagedBufferFile(fileMgr, true, -1, -1); + dbh = new DBHandle(bfile); + } + + /** + * Insert the specified number of records using random keys. + * + * @param table table instance, if null one will be created + * @param recordCnt number of records to insert. + * @param varDataSize size of variable length data fields. + * @return Record[] records which were inserted. + */ + private Record[] createRandomTableRecords(int schemaType, int recordCnt, int varDataSize) + throws IOException { + long txId = dbh.startTransaction(); + Table table = DBTestUtils.createFixedKeyTable(dbh, table1Name, schemaType, true); + Record[] recs = new Record[recordCnt]; + for (int i = 0; i < recordCnt; i++) { + try { + recs[i] = DBTestUtils.createFixedKeyRecord(table, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + } + dbh.endTransaction(txId, true); + return recs; + } + + /** + * Insert the specified number of records using random keys. + * + * @param recordCnt number of records to insert. + * @param varDataSize size of variable length data fields. + * @return Record[] records which were inserted. + */ + private Record[] createOrderedTableRecords(int schemaType, int recordCnt, long keyIncrement, + int varDataSize) throws IOException { + long txId = dbh.startTransaction(); + Table table = DBTestUtils.createFixedKeyTable(dbh, table1Name, schemaType, true); + FixedField key = new FixedField10(new byte[] { 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }); + Record[] recs = new Record[recordCnt]; + for (int i = 0; i < recordCnt; i++) { + try { + recs[i] = DBTestUtils.createRecord(table, key, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + key = DBTestUtils.addToFixedField(key, keyIncrement); + } + dbh.endTransaction(txId, true); + return recs; + } + + private Field[] matchingKeys(Record[] recs, int columnIx, Record matchRec) { + ArrayList recList = new ArrayList<>(); + Field f = matchRec.getField(columnIx); + for (Record rec : recs) { + if (f.equals(rec.getField(columnIx))) { + recList.add(rec); + } + } + Field[] keys = new FixedField[recList.size()]; + Iterator iter = recList.iterator(); + int i = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + keys[i++] = rec.getKeyField(); + } + Arrays.sort(keys); + return keys; + } + + private void findRecords(boolean testStoredDB, int recordCnt, int findCnt, int varDataSize) + throws IOException { + Record[] recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, recordCnt, varDataSize);// short var-len fields + if (testStoredDB) { + saveAsAndReopen(dbName); + } + Table table = dbh.getTable(table1Name); + int[] indexedColumns = table.getIndexedColumns(); + int step = recordCnt / findCnt; + for (int n = 0; n < indexedColumns.length; n++) { + int indexColumn = indexedColumns[n]; + for (int i = 0; i < recordCnt; i += step) { + Field[] keys = table.findRecords(recs[i].getField(indexColumn), indexColumn); + Arrays.sort(keys); + assertTrue(Arrays.equals(matchingKeys(recs, indexColumn, recs[i]), keys)); + assertEquals(keys.length, + table.getMatchingRecordCount(recs[i].getField(indexColumn), indexColumn)); + } + } + } + + @Test + public void testEmptyFixedKeyIterator() throws IOException { + createRandomTableRecords(DBTestUtils.ALL_TYPES, 0, 1); + + saveAsAndReopen(dbName); + + Table table = dbh.getTable(table1Name); + assertEquals(0, table.getRecordCount()); + + dbh.undo(); + dbh.redo(); + + Field startKey = new FixedField10(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }); + Field minKey = new FixedField10(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }); + Field maxKey = new FixedField10(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0 }); + DBFieldIterator iter = table.fieldKeyIterator(); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); + + iter = table.fieldKeyIterator(startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); + + iter = table.fieldKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); + + startKey = FixedField10.INSTANCE.getMinValue(); + iter = table.fieldKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); + + startKey = FixedField10.INSTANCE.getMaxValue(); + iter = table.fieldKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); + } + + @Test + public void testFindRecordsSmallVLR() throws IOException { + findRecords(false, 1000, 100, 8); + } + + @Test + public void testFindRecordsBigVLR() throws IOException { + findRecords(false, 1000, 100, 16); + } + + @Test + public void testFindStoredRecordsSmallVLR() throws IOException { + findRecords(true, 1000, 100, 8); + } + + @Test + public void testFindStoredRecordsEmptyVLR() throws IOException { + findRecords(true, 1000, 100, 0); + } + + @Test + public void testFindStoredRecordsNullVLR() throws IOException { + findRecords(true, 1000, 100, -1); + } + + @Test + public void testFindStoredRecordsBigVLR() throws IOException { + findRecords(true, 1000, 100, 16); + } + + private void updateRecordsIterator(boolean testStoredDB, int schemaType, int recordCnt, + long keyIncrement, int varDataSize) throws IOException { + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomTableRecords(schemaType, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(schemaType, recordCnt, keyIncrement, varDataSize); + } + if (testStoredDB) { + saveAsAndReopen(dbName); + } + Table table = dbh.getTable(table1Name); + + // Find string and binary columns + int strColumn = -1; + int binColumn = -1; + Field[] fields = table.getSchema().getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].isSameType(StringField.INSTANCE)) { + strColumn = i; + } + else if (fields[i].isSameType(BinaryField.INSTANCE)) { + binColumn = i; + } + } + + // Random update + long txId = dbh.startTransaction(); + Random random = new Random(1); + int updateCnt = recordCnt / 4;// update 1/4th of the records + int varFldUpdate = 0; + updateCnt = 206; + for (int i = 0; i < updateCnt; i++) { + int ran = random.nextInt(); + if (ran < 0) { + ran = -ran; + } + int ix = ran % recordCnt; + + DBTestUtils.fillRecord(recs[ix], varDataSize); + int r = varFldUpdate % 4; + if ((r & 0x01) == 1 && strColumn >= 0) { + // Update String field with null + StringField sf = (StringField) recs[ix].getField(strColumn); + sf.setString(null); + } + else if ((r & 0x02) == 2 && binColumn >= 0) { + // Update binary field with null + BinaryField bf = (BinaryField) recs[ix].getField(binColumn); + bf.setBinaryData(null); + } + table.putRecord(recs[ix]); + ++varFldUpdate; + } + dbh.endTransaction(txId, true); + + assertEquals(recordCnt, table.getRecordCount()); + + int[] indexedColumns = table.getIndexedColumns(); + for (int colIx : indexedColumns) { + + Arrays.sort(recs, new RecColumnComparator(colIx)); + + // Forward iteration (no start) + int recIx = 0; + RecordIterator iter = table.indexIterator(colIx); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Forward iteration (start in middle - specify primary key) + recIx = recordCnt / 2; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Reverse iteration (end - specify primary key) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Reverse iteration (start in middle - specify primary key) + recIx = recordCnt / 2; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Forward iteration (start in middle) + recIx = findStart(recs, recordCnt / 2, colIx); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Reverse iteration (end) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Reverse iteration (start in middle) + recIx = findEnd(recs, recordCnt / 2, colIx); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + if (recs[0].getField(colIx) instanceof LongField) { + + // Forward iteration (start in middle - specify primary key) + recIx = findStart(recs, recordCnt / 2, colIx); + Field startValue = new LongField(); + startValue.setLongValue(recs[recIx].getField(colIx).getLongValue() - 1); + iter = table.indexIteratorAfter(colIx, startValue); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Reverse iteration (start in middle) + recIx = findEnd(recs, recordCnt / 2, colIx); + startValue.setLongValue(recs[recIx].getField(colIx).getLongValue() + 1); + iter = table.indexIteratorBefore(colIx, startValue); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + } + + } + + } + + /** + * Test record iterator. + * + * @param testStoredDB test against a stored database if true, else test against cached database + * only. + * @param recordCnt number of records to test + * @param keyIncrement key increment, 0 = random + * @param varDataSize size of variable length data fields. + * @throws IOException + */ + private void iterateRecords(boolean testStoredDB, int schemaType, int recordCnt, + long keyIncrement, int varDataSize, boolean doUndoRedo) throws IOException { + + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomTableRecords(schemaType, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(schemaType, recordCnt, keyIncrement, varDataSize); + } + + if (testStoredDB) { + saveAsAndReopen(dbName); + } + Table table = dbh.getTable(table1Name); + assertEquals(recordCnt, table.getRecordCount()); + + if (doUndoRedo) { + dbh.undo(); + dbh.redo(); + table = dbh.getTable(table1Name); + assertEquals(recordCnt, table.getRecordCount()); + } + + int[] indexedColumns = table.getIndexedColumns(); + for (int colIx : indexedColumns) { + Arrays.sort(recs, new RecColumnComparator(colIx)); + + int recIx; + RecordIterator iter; + ArrayList indexFields = new ArrayList<>();// list of unique index field values + + // Forward iteration (default iterator with no start) - also collect unique fields + Field lastIndex = null; + recIx = 0; + iter = table.indexIterator(colIx); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx], rec); + Field indexField = recs[recIx].getField(colIx); + if (lastIndex == null || !lastIndex.equals(indexField)) { + indexFields.add(indexField); + lastIndex = indexField; + } + ++recIx; + } + assertEquals(recordCnt, recIx); + + // Backward iteration (default iterator with no start) + recIx = 0; + iter = table.indexIterator(colIx); + assertTrue(!iter.hasPrevious()); + + // Forward iteration (after end) + iter = table.indexIteratorAfter(colIx, recs[recordCnt - 1].getField(colIx)); + assertTrue(!iter.hasNext()); + + // Backward iteration (before first) + recIx = 0; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + assertTrue(!iter.hasPrevious()); + + // Backward iteration (after first) + int startIx = 0; + recIx = findEnd(recs, startIx, colIx); + iter = table.indexIteratorAfter(colIx, recs[startIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + assertTrue(!iter.hasPrevious()); + + // Forward iteration (before first - specify primary key) + startIx = 0; + recIx = findStart(recs, startIx, colIx); + iter = table.indexIteratorBefore(colIx, recs[startIx].getField(colIx), + recs[startIx].getKeyField()); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward iteration (before first - specify primary key) + startIx = 0; + recIx = findStart(recs, startIx, colIx); + iter = table.indexIteratorBefore(colIx, recs[startIx].getField(colIx), + recs[startIx].getKeyField()); + assertTrue(!iter.hasPrevious()); + + // Forward iteration (before first) + recIx = 0; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward iteration (before first) + recIx = 0; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + assertTrue(!iter.hasPrevious()); + + // Forward iteration (end - specify primary key) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + assertTrue(!iter.hasNext()); + + // Backward iteration (end - specify primary key) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Forward iteration (end) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + assertTrue(!iter.hasNext()); + + // Backward iteration (end) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Forward iteration (start in middle - specify primary key) + recIx = recordCnt / 2; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward iteration (start in middle - specify primary key) + recIx = recordCnt / 2; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Forward iteration (from start in middle) + recIx = findStart(recs, recordCnt / 2, colIx); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward iteration (from start in middle) + recIx = findStart(recs, recordCnt / 2, colIx); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[--recIx], rec); + } + assertEquals(0, recIx); + + // Forward iteration (from end in middle) + recIx = findEnd(recs, recordCnt / 2, colIx); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[++recIx], rec); + } + assertEquals(recordCnt - 1, recIx); + + // Reverse iteration (from end in middle) + recIx = findEnd(recs, recordCnt / 2, colIx); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Forward Range iterator + int minIx = findStart(recs, recordCnt / 10, colIx); + int maxIx = findEnd(recs, recordCnt / 5, colIx); + recIx = minIx; + iter = table.indexIterator(colIx, recs[minIx].getField(colIx), + recs[maxIx].getField(colIx), true); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(maxIx + 1, recIx); + + // Full forward record iterator + recIx = 0; + iter = table.indexIterator(colIx, null, null, true); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward Range iterator + minIx = findStart(recs, recordCnt / 10, colIx); + maxIx = findEnd(recs, recordCnt / 5, colIx); + recIx = maxIx; + iter = table.indexIterator(colIx, recs[minIx].getField(colIx), + recs[maxIx].getField(colIx), false); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(minIx - 1, recIx); + + // Full reverse record iterator + recIx = recordCnt - 1; + iter = table.indexIterator(colIx, null, null, false); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + if (recs[0].getField(colIx) instanceof LongField) { + + // Forward iteration (after end) + long k = recs[recordCnt - 1].getField(colIx).getLongValue() + 1; + iter = table.indexIteratorBefore(colIx, new LongField(k)); + assertTrue(!iter.hasNext()); + + // Forward iteration (start in middle - specify primary key) + recIx = findStart(recs, recordCnt / 2, colIx); + Field startValue = new LongField(); + startValue.setLongValue(recs[recIx].getField(colIx).getLongValue() - 1); + iter = table.indexIteratorAfter(colIx, startValue); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Backward iteration (start in middle) + recIx = findEnd(recs, recordCnt / 2, colIx); + startValue.setLongValue(recs[recIx].getField(colIx).getLongValue() + 1); + iter = table.indexIteratorBefore(colIx, startValue); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + } + + //******************************************************************* + + // Multi-direction check starting with forward iteration (start in middle) + recIx = findStart(recs, recordCnt / 2, colIx); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + for (int i = 0; i < 2; i++) { + if (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + } + --recIx; + for (int i = 0; i < 2; i++) { + if (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + } + ++recIx; + for (int i = 0; i < 2; i++) { + if (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + } + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // Multi-direction check starting with backward iteration (start in middle) + recIx = findStart(recs, recordCnt / 2, colIx); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + --recIx; + for (int i = 0; i < 2; i++) { + if (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + } + ++recIx; + for (int i = 0; i < 2; i++) { + if (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + } + --recIx; + for (int i = 0; i < 2; i++) { + if (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + } + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Multi-direction check starting with forward iteration (start at End) + recIx = recordCnt - 1; + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx)); + for (int i = 0; i < 3; i++) { + if (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + } + assertEquals(recordCnt - 1, recIx); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(-1, recIx); + + // Multi-direction check starting with backward iteration (start at Front) + recIx = 0; + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx)); + for (int i = 0; i < 3; i++) { + if (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[--recIx], rec); + } + } + assertEquals(0, recIx); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recordCnt, recIx); + + // + // Index Field Iterator does not support variable length fields + // + if (lastIndex.isVariableLength()) { + continue;// skip remain tests + } + + // Index field iterator (all unique index values) + minIx = 0; + maxIx = indexFields.size() - 1; + DBFieldIterator fiter = table.indexFieldIterator(colIx); + int ix = minIx; + while (fiter.hasNext()) { + Field f = fiter.next(); + assertEquals(indexFields.get(ix++), f); + } + assertEquals(maxIx + 1, ix); + + // Index field iterator (forward range of unique index values) + minIx = indexFields.size() / 10; + maxIx = minIx * 2; + fiter = table.indexFieldIterator(indexFields.get(minIx), indexFields.get(maxIx), true, + colIx); + ix = minIx; + while (fiter.hasNext()) { + Field f = fiter.next(); + assertEquals(indexFields.get(ix++), f); + } + assertEquals(maxIx + 1, ix); + + // Index field iterator (forward over all indexed fields) + minIx = 0; + maxIx = indexFields.size() - 1; + fiter = table.indexFieldIterator(null, null, true, colIx); + ix = minIx; + assertTrue("Failed to position before min field", fiter.hasNext()); + while (fiter.hasNext()) { + Field f = fiter.next(); + assertEquals(indexFields.get(ix++), f); + } + assertEquals(maxIx + 1, ix); + + // Index field iterator (reverse range of unique index values) +// minIx = indexFields.size() / 10; +// maxIx = minIx * 2; + fiter = table.indexFieldIterator(indexFields.get(minIx), indexFields.get(maxIx), false, + colIx); + ix = maxIx; + while (fiter.hasPrevious()) { + Field f = fiter.previous(); + assertEquals(indexFields.get(ix--), f); + } + assertEquals(minIx - 1, ix); + + // Index field iterator (reverse over all indexed fields) + minIx = 0; + maxIx = indexFields.size() - 1; + fiter = table.indexFieldIterator(null, null, false, colIx); + ix = maxIx; + assertTrue("Failed to position after max field", fiter.hasPrevious()); + while (fiter.hasPrevious()) { + Field f = fiter.previous(); + assertEquals(indexFields.get(ix--), f); + } + assertEquals(-1, ix); + + // Index field iterator (forward range of unique index values) +// minIx = indexFields.size() / 10; +// maxIx = minIx * 2; + startIx = (minIx + maxIx) / 2; + fiter = table.indexFieldIterator(indexFields.get(minIx), indexFields.get(maxIx), + indexFields.get(startIx), true, colIx); + ix = startIx; + while (fiter.hasNext()) { + Field f = fiter.next(); + assertEquals(indexFields.get(ix++), f); + } + assertEquals(maxIx + 1, ix); + + // Index field iterator (reverse range of unique index values) +// minIx = indexFields.size() / 10; +// maxIx = minIx * 2; +// startIx = (minIx + maxIx) / 2; + fiter = table.indexFieldIterator(indexFields.get(minIx), indexFields.get(maxIx), + indexFields.get(startIx), false, colIx); + ix = startIx; + while (fiter.hasPrevious()) { + Field f = fiter.previous(); + assertEquals(indexFields.get(ix--), f); + } + assertEquals(minIx - 1, ix); + } + + } + + /** + * Test record iterator. + * + * @param testStoredDB test against a stored database if true, else test against cached database + * only. + * @param recordCnt number of records to test + * @param keyIncrement key increment, 0 = random + * @param varDataSize size of variable length data fields. + * @throws IOException + */ + private void deleteIteratedRecords(int recordCnt, int testColIx, long keyIncrement, + int varDataSize) throws IOException { + + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(DBTestUtils.ALL_TYPES, recordCnt, keyIncrement, + varDataSize); + } + + Table table = dbh.getTable(table1Name); + + Arrays.sort(recs, new RecColumnComparator(testColIx)); + + // Forward - delete all records + long txId = dbh.startTransaction(); + RecordIterator iter = table.indexIterator(testColIx); + int recIx = 0; + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + assertTrue(iter.delete()); + } + assertEquals(recIx, recs.length); + + dbh.deleteTable(table1Name); + + dbh.endTransaction(txId, true); + + if (keyIncrement == 0) { + recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(DBTestUtils.ALL_TYPES, recordCnt, keyIncrement, + varDataSize); + } + + table = dbh.getTable(table1Name); + + Arrays.sort(recs, new RecColumnComparator(testColIx)); + + // Reverse - delete all records + txId = dbh.startTransaction(); + recIx = recs.length - 1; + iter = table.indexIterator(testColIx, recs[0].getField(testColIx), + recs[recIx].getField(testColIx), false); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + assertTrue(iter.delete()); + } + assertEquals(recIx, -1); + + dbh.deleteTable(table1Name); + + dbh.endTransaction(txId, true); + } + + /** + * Test record iterator. + * + * @param testStoredDB test against a stored database if true, else test against cached database + * only. + * @param recordCnt number of records to test + * @param keyIncrement key increment, 0 = random + * @param varDataSize size of variable length data fields. + * @throws IOException + */ + private void deleteIteratedIndexFields(int recordCnt, int testColIx, long keyIncrement, + int varDataSize) throws IOException { + + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(DBTestUtils.ALL_TYPES, recordCnt, keyIncrement, + varDataSize); + } + long txId = dbh.startTransaction(); + try { + Table table = dbh.getTable(table1Name); + + Arrays.sort(recs, new RecColumnComparator(testColIx)); + + // Count unique index values + int fieldCnt = 0; + Field lastField = null; + ArrayList fieldList = new ArrayList<>(); + for (Record rec : recs) { + Field f = rec.getField(testColIx); + if (lastField == null || !lastField.equals(f)) { + lastField = f; + fieldList.add(f); + ++fieldCnt; + } + } + + // + // Index Field Iterator does not support variable length fields + // + if (lastField.isVariableLength()) { + return;// skip test + } + + // Forward - delete all records + DBFieldIterator iter = table.indexFieldIterator(testColIx); + int cnt = 0; + while (iter.hasNext()) { + assertEquals(fieldList.get(cnt++), iter.next()); + assertTrue(iter.delete()); + } + assertEquals(fieldCnt, cnt); + assertEquals(0, table.getRecordCount()); + } + finally { + dbh.deleteTable(table1Name); + dbh.endTransaction(txId, true); + } + + if (keyIncrement == 0) { + recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, recordCnt, varDataSize); + } + else { + recs = createOrderedTableRecords(DBTestUtils.ALL_TYPES, recordCnt, keyIncrement, + varDataSize); + } + txId = dbh.startTransaction(); + + try { + Table table = dbh.getTable(table1Name); + + Arrays.sort(recs, new RecColumnComparator(testColIx)); + + // Count unique index values + int fieldCnt = 0; + Field lastField = null; + ArrayList fieldList = new ArrayList<>(); + for (Record rec : recs) { + Field f = rec.getField(testColIx); + if (lastField == null || !lastField.equals(f)) { + lastField = f; + fieldList.add(f); + ++fieldCnt; + } + } + + // Reverse - delete all records + + int cnt = fieldCnt - 1; + int lastIx = recs.length - 1; + DBFieldIterator iter = table.indexFieldIterator(recs[0].getField(testColIx), + recs[lastIx].getField(testColIx), false, testColIx); + while (iter.hasPrevious()) { + assertEquals(fieldList.get(cnt--), iter.previous()); + assertTrue(iter.delete()); + } + assertEquals(-1, cnt); + assertEquals(0, table.getRecordCount()); + } + finally { + dbh.deleteTable(table1Name); + dbh.endTransaction(txId, true); + } + } + + private int findStart(Record[] recs, int startIx, int colIx) { + Field f = recs[startIx].getField(colIx); + --startIx; + while (startIx >= 0 && f.equals(recs[startIx].getField(colIx))) { + if (startIx == 0) { + return startIx; + } + --startIx; + } + return ++startIx; + } + + private int findEnd(Record[] recs, int startIx, int colIx) { + Field f = recs[startIx].getField(colIx); + ++startIx; + while (startIx < recs.length && f.equals(recs[startIx].getField(colIx))) { + if (startIx == recs.length) { + return startIx; + } + ++startIx; + } + return --startIx; + } + + private class RecColumnComparator implements Comparator { + + int columnIx; + + RecColumnComparator(int columnIx) { + this.columnIx = columnIx; + } + + @Override + public int compare(Record rec1, Record rec2) { + int r = rec1.getField(columnIx).compareTo(rec2.getField(columnIx)); + if (r == 0) { + return rec1.getKeyField().compareTo(rec2.getKeyField()); + } + return r; + } + } + + @Test + public void testRandomRecordIterator() throws IOException { + iterateRecords(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 0, -1, false); + } + + @Test + public void testForwardRecordIterator() throws IOException { + iterateRecords(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 1, 1, false); + } + + @Test + public void testBackwardRecordIterator() throws IOException { + iterateRecords(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, -1, 1, false); + } + + @Test + public void testStoredRandomRecordIterator() throws IOException { + iterateRecords(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 0, 1, false); + } + + @Test + public void testStoredForwardRecordIterator() throws IOException { + iterateRecords(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 1, 1, false); + } + + @Test + public void testStoredBackwardRecordIterator() throws IOException { + iterateRecords(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, -1, 1, false); + } + + @Test + public void testRandomUpdateRecordIterator() throws IOException { + updateRecordsIterator(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 0, 1); + } + + @Test + public void testForwardUpdateRecordIterator() throws IOException { + updateRecordsIterator(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 1, 1); + } + + @Test + public void testBackwardUpdateRecordIterator() throws IOException { + updateRecordsIterator(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, -1, 1); + } + + @Test + public void testStoredUpdateRandomRecordIterator() throws IOException { + updateRecordsIterator(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 0, 1); + } + + @Test + public void testStoredUpdateForwardRecordIterator() throws IOException { + updateRecordsIterator(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 1, 1); + } + + @Test + public void testStoredUpdateBackwardRecordIterator() throws IOException { + updateRecordsIterator(true, DBTestUtils.ALL_TYPES, ITER_REC_CNT, -1, 1); + } + + @Test + public void testRandomRecordIteratorUndoRedo() throws IOException { + iterateRecords(false, DBTestUtils.ALL_TYPES, ITER_REC_CNT, 0, -1, true); + } + + @Test + public void testRecordIteratorExtents() throws IOException { + + Record[] recs = null; + recs = createOrderedRecordRange(DBTestUtils.SINGLE_SHORT, 30, 2, 1); + Table table = dbh.getTable(table1Name); + assertEquals(recs.length, table.getRecordCount()); + + // Backward Range iterator + int colIx = 0; + Arrays.sort(recs, new RecColumnComparator(colIx)); + int recIx = recs.length - 1; +// RecordIterator iter = table.indexIterator(colIx, recs[minIx].getField(colIx), +// recs[maxIx].getField(colIx), false); + Field minField = new ShortField(Short.MIN_VALUE); + Field maxField = new ShortField(Short.MAX_VALUE); + RecordIterator iter = table.indexIterator(colIx, minField, maxField, false); + while (iter.hasPrevious()) { + Record rec = iter.previous(); + assertEquals(recs[recIx--], rec); + } + assertEquals(recIx, -1); + } + + private Record[] createOrderedRecordRange(int schemaType, int recordCnt, long keyIncrement, + int varDataSize) throws IOException { + long txId = dbh.startTransaction(); + Table table = DBTestUtils.createLongKeyTable(dbh, table1Name, schemaType, true); + Record[] recs = new Record[recordCnt]; + for (int key = 0; key < recordCnt; key++) { + try { + recs[key] = DBTestUtils.createMidRangeRecord(table, key, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + } + dbh.endTransaction(txId, true); + return recs; + } + + @Test + public void testRecordIteratorDelete() throws IOException { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { + deleteIteratedRecords(ITER_REC_CNT, colIx, 1, 1); + } + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { + deleteIteratedRecords(ITER_REC_CNT, colIx, 0, 1); + } + } + + @Test + public void testIndexFieldIteratorDelete() throws IOException { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { + deleteIteratedIndexFields(ITER_REC_CNT, colIx, 1, 1); + } + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { + deleteIteratedIndexFields(ITER_REC_CNT, colIx, 0, 1); + } + } + +} diff --git a/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyTableTest.java b/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyTableTest.java new file mode 100644 index 0000000000..27ac6e04e9 --- /dev/null +++ b/Ghidra/Framework/DB/src/test/java/db/DBFixedKeyTableTest.java @@ -0,0 +1,1201 @@ +/* ### + * 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 db; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.junit.*; + +import db.buffers.*; +import generic.test.AbstractGenericTest; +import ghidra.util.exception.CancelledException; +import utilities.util.FileUtilities; + +public class DBFixedKeyTableTest extends AbstractGenericTest { + + private static final int BUFFER_SIZE = 256;// keep small for chained buffer testing + private static final int CACHE_SIZE = 4 * 1024 * 1024; + + private static final int SMALL_ITER_REC_CNT = 16000; + private static final int BIG_ITER_REC_CNT = 48000; + + private static final String table1Name = "TABLE1"; + + private File testDir; + private static final String dbName = "test"; + + private BufferFileManager fileMgr; + private DBHandle dbh; + private BufferFile bfile; + + private static FixedField10 MAX_VALUE = new FixedField10(); + static { + byte[] maxBytes = new byte[10]; + Arrays.fill(maxBytes, (byte) 0xff); + MAX_VALUE.setBinaryData(maxBytes); + } + private static FixedField10 MIN_VALUE = new FixedField10(); // all zeros + + @Before + public void setUp() throws Exception { + + testDir = createTempDirectory(getClass().getSimpleName()); + dbh = new DBHandle(BUFFER_SIZE, CACHE_SIZE); + } + + @After + public void tearDown() throws Exception { + if (dbh != null) { + dbh.close(); + } + if (bfile != null) { + bfile.close(); + } + FileUtilities.deleteDir(testDir); + } + + private void saveAsAndReopen(String name) throws IOException { + try { + BufferFileManager mgr = DBTestUtils.getBufferFileManager(testDir, name); + BufferFile bf = new LocalManagedBufferFile(dbh.getBufferSize(), mgr, -1); + dbh.saveAs(bf, true, null); + dbh.close(); + fileMgr = mgr; + } + catch (CancelledException e) { + Assert.fail("Should not happen"); + } + bfile = new LocalManagedBufferFile(fileMgr, true, -1, -1); + dbh = new DBHandle(bfile); + } + + private FixedField10 insertOneFixedKeyRecord(boolean testStoredDB, boolean testGetRecord, + int varDataSize) throws IOException { + long txId = dbh.startTransaction(); + Table table = + DBTestUtils.createFixedKeyTable(dbh, table1Name, DBTestUtils.ALL_TYPES, false); + Record rec = null; + try { + rec = DBTestUtils.createFixedKeyRecord(table, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + dbh.endTransaction(txId, true); + if (testStoredDB) { + saveAsAndReopen(dbName); + table = dbh.getTable(table1Name); + } + if (testGetRecord) { + assertEquals(rec, table.getRecord(rec.getKeyField())); + } + return (FixedField10) rec.getKeyField(); + } + + /** + * Test normal record storage within a leaf node. + * Chained-buffers will not be utilized. + * @throws IOException + */ + @Test + public void testInsertSmallFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, false, 1); + } + + /** + * Test normal record storage within a leaf node. + * Use empty value for variable length fields. + * Chained-buffers will not be utilized. + * @throws IOException + */ + @Test + public void testInsertFixedKeyRecordWithEmptyField() throws IOException { + insertOneFixedKeyRecord(false, false, 0); + } + + /** + * Test normal record storage within a leaf node. + * Use NULL value for variable length fields. + * Chained-buffers will not be utilized. + * @throws IOException + */ + @Test + public void testInsertFixedKeyRecordWithNullField() throws IOException { + insertOneFixedKeyRecord(false, false, -1); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will not utilize an index buffer. + * @throws IOException + */ + @Test + public void testInsertSingleChainedBufferFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, false, BUFFER_SIZE / 4); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 1 buffer for storing indexes. + * @throws IOException + */ + @Test + public void testInsertMultChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, false, 2 * BUFFER_SIZE); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 3 buffers for storing indexes. + * @throws IOException + */ + @Test + public void testInsertVeryLargeChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, false, 2 * BUFFER_SIZE * (BUFFER_SIZE / 4)); + } + + /** + * Test normal record storage within a leaf node. + * Chained-buffers will not be utilized. + * @throws IOException + */ + @Test + public void testGetSmallFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, true, 1); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will not utilize an index buffer. + * @throws IOException + */ + @Test + public void testGetSingleChainedBufferFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, true, BUFFER_SIZE / 4); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 1 buffer for storing indexes. + * @throws IOException + */ + @Test + public void testGetMultChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, true, 2 * BUFFER_SIZE); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 3 buffers for storing indexes. + * @throws IOException + */ + @Test + public void testGetVeryLargeChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(false, true, 2 * BUFFER_SIZE * (BUFFER_SIZE / 4)); + } + + /** + * Test normal record storage within a leaf node. + * Chained-buffers will not be utilized. + * @throws IOException + */ + @Test + public void testGetStoredSmallFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(true, true, 1); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will not utilize an index buffer. + * @throws IOException + */ + @Test + public void testGetStoredSingleChainedBufferFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(true, true, BUFFER_SIZE / 4); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 1 buffer for storing indexes. + * @throws IOException + */ + @Test + public void testGetStoredMultChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(true, true, 2 * BUFFER_SIZE); + } + + /** + * Test the use of chained-buffers for record storage. + * Chained-buffers will be forced to utilize 3 buffers for storing indexes. + * @throws IOException + */ + @Test + public void testGetStoredVeryLargeChainedBuffersFixedKeyRecord() throws IOException { + insertOneFixedKeyRecord(true, true, 2 * BUFFER_SIZE * (BUFFER_SIZE / 4)); + } + + @Test + public void testGetMissingFixedKeyRecord() throws IOException { + FixedField10 key = insertOneFixedKeyRecord(false, false, 1); + byte[] k = key.getBinaryData(); + ++k[0]; + FixedField10 otherKey = new FixedField10(k); // incremented key + assertNull(dbh.getTable(table1Name).getRecord(otherKey)); + } + + @Test + public void testStoredGetMissingFixedKeyRecord() throws IOException { + FixedField10 key = insertOneFixedKeyRecord(true, false, 1); + byte[] k = key.getBinaryData(); + ++k[0]; + FixedField10 otherKey = new FixedField10(k); // incremented key + assertNull(dbh.getTable(table1Name).getRecord(otherKey)); + } + + /** + * Insert the specified number of records using random keys. + * @param table table instance, if null one will be created + * @param recordCnt number of records to insert. + * @param varDataSize size of variable length data fields. + * @return Record[] records which were inserted. + */ + private Record[] createRandomFixedKeyTableRecords(Table table, int recordCnt, int varDataSize) + throws IOException { + long txId = dbh.startTransaction(); + if (table == null) { + table = DBTestUtils.createFixedKeyTable(dbh, table1Name, DBTestUtils.ALL_TYPES, false); + } + Record[] recs = new Record[recordCnt]; + for (int i = 0; i < recordCnt; i++) { + try { + recs[i] = DBTestUtils.createFixedKeyRecord(table, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + } + dbh.endTransaction(txId, true); + return recs; + } + + /** + * Insert the specified number of records using random keys. + * @param recordCnt number of records to insert. + * @param varDataSize size of variable length data fields. + * @return Record[] records which were inserted. + */ + private Record[] createOrderedFixedKeyTableRecords(int recordCnt, long keyIncrement, + int varDataSize) throws IOException { + long txId = dbh.startTransaction(); + Table table = + DBTestUtils.createFixedKeyTable(dbh, table1Name, DBTestUtils.ALL_TYPES, false); + FixedField10 key = new FixedField10(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + Record[] recs = new Record[recordCnt]; + for (int i = 0; i < recordCnt; i++) { + try { + recs[i] = DBTestUtils.createRecord(table, key, varDataSize, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + key = (FixedField10) DBTestUtils.addToFixedField(key, keyIncrement); + } + dbh.endTransaction(txId, true); + return recs; + } + + /** + * Test record iterator. + * @param testStoredDB test against a stored database if true, else test against cached database only. + * @param recordCnt number of records to test + * @param keyIncrement key increment, 0 = random + * @param varDataSize size of variable length data fields. + * @throws IOException + */ + private void iterateFixedKeyRecords(boolean testStoredDB, int recordCnt, long keyIncrement, + int varDataSize) throws IOException { + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomFixedKeyTableRecords(null, recordCnt, varDataSize); + } + else { + recs = createOrderedFixedKeyTableRecords(recordCnt, keyIncrement, varDataSize); + } + Arrays.sort(recs); + if (testStoredDB) { + saveAsAndReopen(dbName); + } + Table table = dbh.getTable(table1Name); + assertEquals(recordCnt, table.getRecordCount()); + //assertEquals(recs[recordCnt - 1].getKeyField(), table.getMaxKey()); + + // Forward iteration (no start) + int recIx = 0; + RecordIterator iter = table.iterator(); + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + } + assertEquals(recordCnt, recIx); + + // Forward iteration (start in middle) + recIx = recordCnt / 2; + iter = table.iterator(recs[recIx].getKeyField()); + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + } + assertEquals(recordCnt, recIx); + + // Reverse iteration (no start) + recIx = recordCnt - 1; + iter = table.iterator(MAX_VALUE); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + } + assertEquals(-1, recIx); + + // Reverse iteration (start in middle) + recIx = recordCnt / 2; + iter = table.iterator(recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + } + assertEquals(-1, recIx); + + // Range iteration (forward) + int minIx = recordCnt / 10; + int maxIx = 2 * minIx; + iter = table.iterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[minIx].getKeyField()); + recIx = minIx; + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + } + assertEquals(recIx, maxIx + 1); + + // Range iteration (reverse) + iter = table.iterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[maxIx].getKeyField()); + recIx = maxIx; + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Assumes consecutive keys do not exist - assumption could be a problem with random keys + if (keyIncrement > 1 || keyIncrement < -1) { + + // Range iteration (forward) starting at min and not on existing record. + FixedField missingMinKey = DBTestUtils.addToFixedField(recs[minIx].getKeyField(), -1); + FixedField missingMaxKey = DBTestUtils.addToFixedField(recs[maxIx].getKeyField(), 1); + iter = table.iterator(DBTestUtils.addToFixedField(missingMinKey, 1), missingMaxKey, + missingMinKey); + recIx = minIx; + assertTrue(!iter.hasPrevious()); + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + } + assertEquals(recIx, maxIx + 1); + + // Range iteration - no records (forward and reverse). + iter = table.iterator(missingMinKey, missingMinKey, missingMinKey); + assertTrue(!iter.hasNext()); + assertTrue(!iter.hasPrevious()); + iter = table.iterator(missingMaxKey, missingMaxKey, missingMaxKey); + assertTrue(!iter.hasNext()); + assertTrue(!iter.hasPrevious()); + + // Range iteration (reverse) starting at max and not on existing record + iter = table.iterator(missingMinKey, DBTestUtils.addToFixedField(missingMaxKey, -1), + missingMaxKey); + recIx = maxIx; + assertTrue(!iter.hasNext()); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Range iteration (reverse) starting at mid and not on existing record + int midIx = (minIx + maxIx) >>> 1; + FixedField missingMidKey = DBTestUtils.addToFixedField(recs[midIx].getKeyField(), -1); + iter = table.iterator(missingMinKey, missingMaxKey, missingMidKey); + recIx = midIx - 1; + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Range iteration (forward) starting at mid and not on existing record + iter = table.iterator(missingMinKey, missingMaxKey, missingMidKey); + recIx = midIx; + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + } + assertEquals(recIx, maxIx + 1); + } + + // delete (forward) + long txId = dbh.startTransaction(); + iter = table.iterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[minIx].getKeyField()); + recIx = minIx; + while (iter.hasNext()) { + iter.next(); + iter.delete(); + ++recIx; + } + dbh.endTransaction(txId, true); + + recordCnt -= maxIx - minIx + 1; + assertEquals(table.getRecordCount(), recordCnt); + iter = table.iterator(recs[minIx - 1].getKeyField(), recs[maxIx + 1].getKeyField(), + recs[minIx - 1].getKeyField()); + assertEquals(recs[minIx - 1], iter.next()); + assertEquals(recs[maxIx + 1], iter.next()); + + // Range iteration (reverse) + txId = dbh.startTransaction(); + minIx = minIx * 3; + maxIx += minIx; + iter = table.iterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[maxIx].getKeyField()); + recIx = maxIx; + while (iter.hasPrevious()) { + iter.previous(); + iter.delete(); + --recIx; + } + dbh.endTransaction(txId, true); + + recordCnt -= maxIx - minIx + 1; + assertEquals(table.getRecordCount(), recordCnt); + iter = table.iterator(recs[minIx - 1].getKeyField(), recs[maxIx + 1].getKeyField(), + recs[minIx - 1].getKeyField()); + assertEquals(recs[minIx - 1], iter.next()); + assertEquals(recs[maxIx + 1], iter.next()); + } + + @Test + public void testFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, SMALL_ITER_REC_CNT, 1, 1); + } + + @Test + public void testRandomFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, SMALL_ITER_REC_CNT, 0, 1); + } + + @Test + public void testForwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, SMALL_ITER_REC_CNT, 2, 1); + } + + @Test + public void testBackwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, SMALL_ITER_REC_CNT, -2, 1); + } + + @Test + public void testStoredFixedKeyRandomRecordIterator() throws IOException { + iterateFixedKeyRecords(true, SMALL_ITER_REC_CNT, 0, 1); + } + + @Test + public void testStoredForwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(true, SMALL_ITER_REC_CNT, 2, 1); + } + + @Test + public void testStoredBackwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(true, SMALL_ITER_REC_CNT, -2, 1); + } + + @Test + public void testBigRandomFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, BIG_ITER_REC_CNT, 0, 1); + } + + @Test + public void testBigForwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, BIG_ITER_REC_CNT, 2, 1); + } + + @Test + public void testBigBackwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(false, BIG_ITER_REC_CNT, -2, 1); + } + + @Test + public void testBigStoredRandomFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(true, BIG_ITER_REC_CNT, 0, 1); + } + + @Test + public void testBigStoredForwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(true, BIG_ITER_REC_CNT, 2, 1); + } + + @Test + public void testBigStoredBackwardFixedKeyRecordIterator() throws IOException { + iterateFixedKeyRecords(true, BIG_ITER_REC_CNT, -2, 1); + } + + /** + * Test key iterator. + * @param testStoredDB test against a stored database if true, else test against cached database only. + * @param recordCnt number of records to test + * @param keyIncrement key increment, 0 = random + * @param varDataSize size of variable length data fields. + * @throws IOException + */ + private void iterateFixedKeys(boolean testStoredDB, int recordCnt, long keyIncrement, + int varDataSize) throws IOException { + Record[] recs = null; + if (keyIncrement == 0) { + recs = createRandomFixedKeyTableRecords(null, recordCnt, varDataSize); + } + else { + recs = createOrderedFixedKeyTableRecords(recordCnt, keyIncrement, varDataSize); + } + Arrays.sort(recs); + if (testStoredDB) { + saveAsAndReopen(dbName); + } + Table table = dbh.getTable(table1Name); + assertEquals(recordCnt, table.getRecordCount()); + + // Forward iteration (no start) + int recIx = 0; + DBFieldIterator iter = table.fieldKeyIterator(); + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + } + assertEquals(recordCnt, recIx); + + // Forward iteration (start in middle) + recIx = recordCnt / 2; + iter = table.fieldKeyIterator(recs[recIx].getKeyField()); + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + } + assertEquals(recordCnt, recIx); + + // Reverse iteration (no start) + recIx = recordCnt - 1; + iter = table.fieldKeyIterator(MAX_VALUE); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + } + assertEquals(-1, recIx); + + // Reverse iteration (start in middle) + recIx = recordCnt / 2; + iter = table.fieldKeyIterator(recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + } + assertEquals(-1, recIx); + + // Range iteration (forward) starting at min on existing record + int minIx = recordCnt / 10; + int maxIx = 2 * minIx; + iter = table.fieldKeyIterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[minIx].getKeyField()); + recIx = minIx; + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + } + assertEquals(recIx, maxIx + 1); + + // Range iteration (reverse) starting at max on existing record + iter = table.fieldKeyIterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[maxIx].getKeyField()); + recIx = maxIx; + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Assumes consecutive keys do not exist - assumption could be a problem with random keys + if (keyIncrement != 1) { + + // Range iteration (forward) starting at min and not on existing record. + FixedField missingMinKey = DBTestUtils.addToFixedField(recs[minIx].getKeyField(), -1); + FixedField missingMaxKey = DBTestUtils.addToFixedField(recs[maxIx].getKeyField(), 1); + iter = table.fieldKeyIterator(missingMinKey, missingMaxKey, missingMinKey); + recIx = minIx; + assertTrue(!iter.hasPrevious()); + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + } + assertEquals(recIx, maxIx + 1); + + // Range iteration - no records (forward and reverse). + iter = table.fieldKeyIterator(missingMinKey, missingMinKey, missingMinKey); + assertTrue(!iter.hasNext()); + assertTrue(!iter.hasPrevious()); + iter = table.fieldKeyIterator(missingMaxKey, missingMaxKey, missingMaxKey); + assertTrue(!iter.hasNext()); + assertTrue(!iter.hasPrevious()); + + // Range iteration (reverse) starting at max and not on existing record + iter = table.fieldKeyIterator(missingMinKey, missingMaxKey, missingMaxKey); + recIx = maxIx; + assertTrue(!iter.hasNext()); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Range iteration (reverse) starting at mid and not on existing record + int midIx = (minIx + maxIx) / 2; + FixedField missingMidKey = DBTestUtils.addToFixedField(recs[midIx].getKeyField(), -1); + iter = table.fieldKeyIterator(missingMinKey, missingMaxKey, missingMidKey); + recIx = midIx - 1; + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + } + assertEquals(recIx, minIx - 1); + + // Range iteration (forward) starting at mid and not on existing record + iter = table.fieldKeyIterator(missingMinKey, missingMaxKey, missingMidKey); + recIx = midIx; + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + } + assertEquals(recIx, maxIx + 1); + } + + // delete (forward) + long txId = dbh.startTransaction(); + iter = table.fieldKeyIterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[minIx].getKeyField()); + recIx = minIx; + while (iter.hasNext()) { + assertEquals(recs[recIx++].getKeyField(), iter.next()); + iter.delete(); + } + assertEquals(recIx, maxIx + 1); + dbh.endTransaction(txId, true); + + recordCnt -= maxIx - minIx + 1; + assertEquals(table.getRecordCount(), recordCnt); + iter = table.fieldKeyIterator(recs[minIx - 1].getKeyField(), recs[maxIx + 1].getKeyField(), + recs[minIx - 1].getKeyField()); + assertEquals(recs[minIx - 1].getKeyField(), iter.next()); + assertEquals(recs[maxIx + 1].getKeyField(), iter.next()); + + // Range iteration (reverse) + txId = dbh.startTransaction(); + minIx = minIx * 3; + maxIx += minIx; + iter = table.fieldKeyIterator(recs[minIx].getKeyField(), recs[maxIx].getKeyField(), + recs[maxIx].getKeyField()); + recIx = maxIx; + while (iter.hasPrevious()) { + assertEquals(recs[recIx--].getKeyField(), iter.previous()); + iter.delete(); + } + assertEquals(recIx, minIx - 1); + dbh.endTransaction(txId, true); + + recordCnt -= maxIx - minIx + 1; + assertEquals(table.getRecordCount(), recordCnt); + iter = table.fieldKeyIterator(recs[minIx - 1].getKeyField(), recs[maxIx + 1].getKeyField(), + recs[minIx - 1].getKeyField()); + assertEquals(recs[minIx - 1].getKeyField(), iter.next()); + assertEquals(recs[maxIx + 1].getKeyField(), iter.next()); + } + + @Test + public void testRandomFixedKeyIterator() throws IOException { + iterateFixedKeys(false, SMALL_ITER_REC_CNT, 0, 1); + } + + @Test + public void testForwardFixedKeyIterator() throws IOException { + iterateFixedKeys(false, SMALL_ITER_REC_CNT, 1, 1); + } + + @Test + public void testForwardSkippingFixedKeyIterator() throws IOException { + iterateFixedKeys(false, SMALL_ITER_REC_CNT, 3, 1); + } + + @Test + public void testForwardDeleteIterator() throws IOException { + + Record[] recs = createOrderedFixedKeyTableRecords(SMALL_ITER_REC_CNT, 2, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + assertEquals(SMALL_ITER_REC_CNT, table.getRecordCount()); + + int recIx = 0; + long txId = dbh.startTransaction(); + RecordIterator iter = table.iterator(); + while (iter.hasNext()) { + assertEquals(recs[recIx++], iter.next()); + iter.delete(); + } + assertEquals(SMALL_ITER_REC_CNT, recIx); + dbh.endTransaction(txId, true); + } + + @Test + public void testReverseDeleteIterator() throws IOException { + + Record[] recs = createOrderedFixedKeyTableRecords(SMALL_ITER_REC_CNT, 2, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + assertEquals(SMALL_ITER_REC_CNT, table.getRecordCount()); + + long txId = dbh.startTransaction(); + int recIx = SMALL_ITER_REC_CNT - 1; + RecordIterator iter = table.iterator(recs[recIx].getKeyField()); + while (iter.hasPrevious()) { + assertEquals(recs[recIx--], iter.previous()); + iter.delete(); + } + assertEquals(-1, recIx); + dbh.endTransaction(txId, true); + } + + @Test + public void testGetFixedKeyRecordAfter() throws IOException { + Record[] recs = createRandomFixedKeyTableRecords(null, 16000, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + // After test + for (int i = 1000; i < 16000; i += 1000) { + Record rec = table.getRecordAfter(recs[i].getKeyField()); + assertEquals(rec.getKeyField(), recs[i + 1].getKeyField()); + } + + // End test + Record rec = table.getRecordAfter(recs[15999].getKeyField()); + assertNull(rec); + } + + private int findHoleAfterFixedKey(Record[] recs, int startIx) { + for (int i = startIx; i < recs.length - 1; i++) { + FixedField f = DBTestUtils.addToFixedField(recs[i].getKeyField(), 1); + if (f.compareTo(recs[i + 1].getKeyField()) < 0) { + return i; + } + } + return -1; + } + + private int findHoleBeforeFixedKey(Record[] recs, int startIx) { + for (int i = startIx; i < recs.length; i++) { + FixedField f = DBTestUtils.addToFixedField(recs[i - 1].getKeyField(), 1); + if (f.compareTo(recs[i].getKeyField()) < 0) { + return i; + } + } + return -1; + } + + @Test + public void testGetFixedKeyRecordAtOrAfter() throws IOException { + Record[] recs = createRandomFixedKeyTableRecords(null, 16000, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + // At and After tests + for (int i = 1000; i < 16000; i += 1000) { + Record rec = table.getRecordAtOrAfter(recs[i].getKeyField()); + assertEquals(rec.getKeyField(), recs[i].getKeyField()); + int ix = findHoleAfterFixedKey(recs, i + 500); + if (ix < 0) { + Assert.fail("Bad test data"); + } + rec = table.getRecordAtOrAfter(DBTestUtils.addToFixedField(recs[ix].getKeyField(), 1)); + assertEquals(recs[ix + 1].getKeyField(), rec.getKeyField()); + } + + // End tests + Field lastKey = recs[15999].getKeyField(); + if (lastKey == MAX_VALUE) { + Assert.fail("Bad test data"); + } + Record rec = table.getRecordAtOrAfter(lastKey); + assertEquals(rec.getKeyField(), lastKey); + rec = table.getRecordAtOrAfter(DBTestUtils.addToFixedField(lastKey, 1)); + assertNull(rec); + } + + @Test + public void testGetFixedKeyRecordAtOrBefore() throws IOException { + Record[] recs = createRandomFixedKeyTableRecords(null, 16000, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + // At and Before tests + for (int i = 1000; i < 16000; i += 1000) { + Record rec = table.getRecordAtOrBefore(recs[i].getKeyField()); + assertEquals(rec.getKeyField(), recs[i].getKeyField()); + int ix = findHoleBeforeFixedKey(recs, i + 500); + if (ix < 0) { + Assert.fail("Bad test data"); + } + rec = + table.getRecordAtOrBefore(DBTestUtils.addToFixedField(recs[ix].getKeyField(), -1)); + assertEquals(recs[ix - 1].getKeyField(), rec.getKeyField()); + } + + // End tests + Field firstKey = recs[0].getKeyField(); + if (firstKey.equals(MIN_VALUE)) { + Assert.fail("Bad test data"); + } + Record rec = table.getRecordAtOrBefore(firstKey); + assertEquals(rec.getKeyField(), firstKey); + rec = table.getRecordAtOrBefore(DBTestUtils.addToFixedField(firstKey, -1)); + assertNull(rec); + } + + @Test + public void testDeleteFixedKeyRecord() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + long txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 1000) { + table.deleteRecord(recs[i].getKeyField()); + assertEquals(--cnt, table.getRecordCount()); + } + dbh.endTransaction(txId, true); + + RecordIterator iter = table.iterator(); + int recIx = 1; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + if ((recIx % 1000) == 0) { + ++recIx; + } + } + assertEquals(SMALL_ITER_REC_CNT + 1, recIx); + } + + @Test + public void testForwardDeleteFixedKeyRecord() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + long txId = dbh.startTransaction(); + int count = cnt; + for (int i = 0; i < count; i++) { + table.deleteRecord(recs[i].getKeyField()); + assertEquals(--cnt, table.getRecordCount()); + } + dbh.endTransaction(txId, true); + } + + @Test + public void testReverseDeleteFixedKeyRecord() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + long txId = dbh.startTransaction(); + for (int i = cnt - 1; i >= 0; i--) { + table.deleteRecord(recs[i].getKeyField()); + assertEquals(--cnt, table.getRecordCount()); + } + dbh.endTransaction(txId, true); + } + + @Test + public void testDeleteAllFixedKeyRecords() throws IOException { + + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + + long txId = dbh.startTransaction(); + Table table = dbh.getTable(table1Name); + table.deleteAll(); + dbh.endTransaction(txId, true); + + assertEquals(0, table.getRecordCount()); + + RecordIterator iter = table.iterator(); + assertTrue(!iter.hasNext()); + + // Repopulate table + recs = createRandomFixedKeyTableRecords(table, cnt, 1); + assertEquals(cnt, table.getRecordCount()); + Arrays.sort(recs); + iter = table.iterator(); + int recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + } + + private void deleteFixedKeyRangeRecords(int count, int startIx, int endIx) throws IOException { + + Record[] recs = createRandomFixedKeyTableRecords(null, count, 1); + Arrays.sort(recs); + + long txId = dbh.startTransaction(); + Table table = dbh.getTable(table1Name); + table.deleteRecords(recs[startIx].getKeyField(), recs[endIx].getKeyField()); + dbh.endTransaction(txId, true); + + RecordIterator iter = table.iterator(); + int recIx = startIx != 0 ? 0 : (endIx + 1); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + if (recIx == startIx) { + recIx = endIx + 1; + } + } + assertEquals(recIx, count); + } + + @Test + public void testDeleteFixedKeyRangeRecords() throws IOException { + deleteFixedKeyRangeRecords(SMALL_ITER_REC_CNT, SMALL_ITER_REC_CNT / 4, + SMALL_ITER_REC_CNT / 2); + } + + @Test + public void testDeleteFixedKeyRangeRecords2() throws IOException { + deleteFixedKeyRangeRecords(SMALL_ITER_REC_CNT, (SMALL_ITER_REC_CNT / 4) + 1, + (SMALL_ITER_REC_CNT / 2) + 1); + } + + @Test + public void testDeleteFixedKeyRangeAllRecords() throws IOException { + deleteFixedKeyRangeRecords(SMALL_ITER_REC_CNT, 0, SMALL_ITER_REC_CNT - 1); + } + + @Test + public void testUpdateFixedKeyRecord() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + long txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 100) { + DBTestUtils.fillRecord(recs[i], BUFFER_SIZE); + table.putRecord(recs[i]); + } + dbh.endTransaction(txId, true); + + RecordIterator iter = table.iterator(); + int recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + } + + @Test + public void testUpdateBigFixedKeyRecord() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + long txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 100) { + DBTestUtils.fillRecord(recs[i], (BUFFER_SIZE / 8) * BUFFER_SIZE); + table.putRecord(recs[i]); + } + dbh.endTransaction(txId, true); + + RecordIterator iter = table.iterator(); + int recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + + txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 100) { + DBTestUtils.fillRecord(recs[i], 1); + table.putRecord(recs[i]); + } + dbh.endTransaction(txId, true); + + iter = table.iterator(); + recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + } + + @Test + public void testUpdateUndoFixedKeyRecords() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + + assertTrue(!dbh.canUndo()); + assertTrue(!dbh.canRedo()); + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + assertTrue(dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + // Update records + long txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 100) { + Record rec = table.getSchema().createRecord(recs[i].getKeyField()); + DBTestUtils.fillRecord(rec, (BUFFER_SIZE / 8) * BUFFER_SIZE); + table.putRecord(rec); + } + assertEquals(cnt, table.getRecordCount()); + assertTrue(!dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + dbh.endTransaction(txId, false);// rollback updates + + assertTrue(dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + RecordIterator iter = table.iterator(); + int recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + + // Add records + txId = dbh.startTransaction(); + for (int i = 0; i < 100; i++) { + try { + DBTestUtils.createFixedKeyRecord(table, BUFFER_SIZE, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + } + dbh.endTransaction(txId, true); + + assertTrue(dbh.undo()); + + iter = table.iterator(); + recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + } + + @Test + public void testUpdateUndoRedoFixedKeyRecords() throws IOException { + int cnt = SMALL_ITER_REC_CNT; + + assertTrue(!dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + Record[] recs = createRandomFixedKeyTableRecords(null, cnt, 1); + + assertTrue(dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + Arrays.sort(recs); + Table table = dbh.getTable(table1Name); + + // Update records + long txId = dbh.startTransaction(); + for (int i = 0; i < cnt; i += 100) { + DBTestUtils.fillRecord(recs[i], (BUFFER_SIZE / 8) * BUFFER_SIZE); + table.putRecord(recs[i]); + } + assertEquals(cnt, table.getRecordCount()); + assertTrue(!dbh.canUndo()); + assertTrue(!dbh.canRedo()); + dbh.endTransaction(txId, true); + + assertTrue(dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + assertTrue(dbh.undo()); + + assertTrue(dbh.canUndo()); + assertTrue(dbh.canRedo()); + + assertTrue(dbh.redo()); + + assertTrue(dbh.canUndo()); + assertTrue(!dbh.canRedo()); + + RecordIterator iter = table.iterator(); + int recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(cnt, recIx); + + // Add records + Record[] newRecs = new Record[recs.length + 100]; + System.arraycopy(recs, 0, newRecs, 0, recs.length); + txId = dbh.startTransaction(); + for (int i = 0; i < 100; i++) { + try { + newRecs[i + recs.length] = + DBTestUtils.createFixedKeyRecord(table, BUFFER_SIZE, true); + } + catch (DuplicateKeyException e) { + Assert.fail("Duplicate key error"); + } + } + dbh.endTransaction(txId, true); + + recs = newRecs; + Arrays.sort(recs); + + assertTrue(dbh.undo()); + + assertTrue(dbh.redo()); + + iter = table.iterator(); + recIx = 0; + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(recs.length, recIx); + } + +} diff --git a/Ghidra/Framework/DB/src/test/java/db/DBIndexedTableTest.java b/Ghidra/Framework/DB/src/test/java/db/DBIndexedTableTest.java index 72b4c28414..8cf7b667d5 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBIndexedTableTest.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBIndexedTableTest.java @@ -15,8 +15,7 @@ */ package db; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.File; import java.io.IOException; @@ -27,6 +26,7 @@ import org.junit.*; import db.buffers.*; import generic.test.AbstractGenericTest; import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; import utilities.util.FileUtilities; public class DBIndexedTableTest extends AbstractGenericTest { @@ -130,7 +130,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { return recs; } - private long[] matchingKeys(Record[] recs, int columnIx, Record matchRec) { + private Field[] matchingKeys(Record[] recs, int columnIx, Record matchRec) { ArrayList recList = new ArrayList<>(); Field f = matchRec.getField(columnIx); for (Record rec : recs) { @@ -138,12 +138,12 @@ public class DBIndexedTableTest extends AbstractGenericTest { recList.add(rec); } } - long[] keys = new long[recList.size()]; + Field[] keys = new Field[recList.size()]; Iterator iter = recList.iterator(); int i = 0; while (iter.hasNext()) { Record rec = iter.next(); - keys[i++] = rec.getKey(); + keys[i++] = rec.getKeyField(); } Arrays.sort(keys); return keys; @@ -156,15 +156,15 @@ public class DBIndexedTableTest extends AbstractGenericTest { saveAsAndReopen(dbName); } Table table = dbh.getTable(table1Name); - int[] indexedColumns = table.getIndexedColumns(); - assertEquals(table.getSchema().getFieldClasses().length, indexedColumns.length); + int step = recordCnt / findCnt; - for (int n = 0; n < indexedColumns.length; n++) { + for (int indexColumn : table.getIndexedColumns()) { for (int i = 0; i < recordCnt; i += step) { - long[] keys = table.findRecords(recs[i].getField(n), n); + Field[] keys = table.findRecords(recs[i].getField(indexColumn), indexColumn); Arrays.sort(keys); - assertTrue(Arrays.equals(matchingKeys(recs, n, recs[i]), keys)); - assertEquals(keys.length, table.getMatchingRecordCount(recs[i].getField(n), n)); + assertTrue(Arrays.equals(matchingKeys(recs, indexColumn, recs[i]), keys)); + assertEquals(keys.length, + table.getMatchingRecordCount(recs[i].getField(indexColumn), indexColumn)); } } } @@ -181,36 +181,30 @@ public class DBIndexedTableTest extends AbstractGenericTest { dbh.undo(); dbh.redo(); - int[] indexedColumns = table.getIndexedColumns(); - assertEquals(table.getSchema().getFieldClasses().length, indexedColumns.length); - int max = indexedColumns.length > 3 ? 3 : indexedColumns.length; - for (int n = 0; n < max; n++) { + long startKey = 1500L; + long minKey = 100L; + long maxKey = 5000L; + DBLongIterator iter = table.longKeyIterator(); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); - long startKey = 1500L; - long minKey = 100L; - long maxKey = 5000L; - DBLongIterator iter = table.longKeyIterator(); - assertTrue(!iter.hasPrevious()); - assertTrue(!iter.hasNext()); + iter = table.longKeyIterator(startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); - iter = table.longKeyIterator(startKey); - assertTrue(!iter.hasPrevious()); - assertTrue(!iter.hasNext()); + iter = table.longKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); - iter = table.longKeyIterator(minKey, maxKey, startKey); - assertTrue(!iter.hasPrevious()); - assertTrue(!iter.hasNext()); + startKey = -1L; + iter = table.longKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); - startKey = -1L; - iter = table.longKeyIterator(minKey, maxKey, startKey); - assertTrue(!iter.hasPrevious()); - assertTrue(!iter.hasNext()); - - startKey = 10000L; - iter = table.longKeyIterator(minKey, maxKey, startKey); - assertTrue(!iter.hasPrevious()); - assertTrue(!iter.hasNext()); - } + startKey = 10000L; + iter = table.longKeyIterator(minKey, maxKey, startKey); + assertTrue(!iter.hasPrevious()); + assertTrue(!iter.hasNext()); } @Test @@ -260,12 +254,15 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Find string and binary columns int strColumn = -1; int binColumn = -1; - Class[] fieldClasses = table.getSchema().getFieldClasses(); - for (int i = 0; i < fieldClasses.length; i++) { - if (fieldClasses[i].equals(StringField.class)) { + Field[] fields = table.getSchema().getFields(); + for (int i = 0; i < fields.length; i++) { + if (!fields[i].isVariableLength()) { + continue; + } + if (fields[i] instanceof StringField) { strColumn = i; } - else if (fieldClasses[i].equals(BinaryField.class)) { + else if (fields[i] instanceof BinaryField) { binColumn = i; } } @@ -302,9 +299,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { assertEquals(recordCnt, table.getRecordCount()); - int[] indexedColumns = table.getIndexedColumns(); - assertEquals(table.getSchema().getFieldClasses().length, indexedColumns.length); - for (int colIx : indexedColumns) { + for (int colIx : table.getIndexedColumns()) { Arrays.sort(recs, new RecColumnComparator(colIx)); @@ -319,8 +314,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Forward iteration (start in middle - specify primary key) recIx = recordCnt / 2; - iter = - table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasNext()) { Record rec = iter.next(); assertEquals(recs[recIx++], rec); @@ -329,8 +324,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Reverse iteration (end - specify primary key) recIx = recordCnt - 1; - iter = - table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasPrevious()) { Record rec = iter.previous(); assertEquals(recs[recIx--], rec); @@ -339,8 +334,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Reverse iteration (start in middle - specify primary key) recIx = recordCnt / 2; - iter = - table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasPrevious()) { Record rec = iter.previous(); assertEquals(recs[recIx--], rec); @@ -437,9 +432,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { assertEquals(recordCnt, table.getRecordCount()); } - int[] indexedColumns = table.getIndexedColumns(); - assertEquals(table.getSchema().getFieldClasses().length, indexedColumns.length); - for (int colIx : indexedColumns) { + for (int colIx : table.getIndexedColumns()) { Arrays.sort(recs, new RecColumnComparator(colIx)); int recIx; @@ -491,7 +484,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { startIx = 0; recIx = findStart(recs, startIx, colIx); iter = table.indexIteratorBefore(colIx, recs[startIx].getField(colIx), - recs[startIx].getKey()); + recs[startIx].getKeyField()); while (iter.hasNext()) { Record rec = iter.next(); assertEquals(recs[recIx++], rec); @@ -502,7 +495,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { startIx = 0; recIx = findStart(recs, startIx, colIx); iter = table.indexIteratorBefore(colIx, recs[startIx].getField(colIx), - recs[startIx].getKey()); + recs[startIx].getKeyField()); assertTrue(!iter.hasPrevious()); // Forward iteration (before first) @@ -521,14 +514,14 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Forward iteration (end - specify primary key) recIx = recordCnt - 1; - iter = - table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); assertTrue(!iter.hasNext()); // Backward iteration (end - specify primary key) recIx = recordCnt - 1; - iter = - table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasPrevious()) { Record rec = iter.previous(); assertEquals(recs[recIx--], rec); @@ -551,8 +544,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Forward iteration (start in middle - specify primary key) recIx = recordCnt / 2; - iter = - table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorBefore(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasNext()) { Record rec = iter.next(); assertEquals(recs[recIx++], rec); @@ -561,8 +554,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { // Backward iteration (start in middle - specify primary key) recIx = recordCnt / 2; - iter = - table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), recs[recIx].getKey()); + iter = table.indexIteratorAfter(colIx, recs[recIx].getField(colIx), + recs[recIx].getKeyField()); while (iter.hasPrevious()) { Record rec = iter.previous(); assertEquals(recs[recIx--], rec); @@ -943,7 +936,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { * @throws IOException */ private void deleteIteratedIndexFields(int recordCnt, int testColIx, long keyIncrement, - int varDataSize) throws IOException { + int varDataSize) throws Exception { Record[] recs = null; if (keyIncrement == 0) { @@ -989,6 +982,10 @@ public class DBIndexedTableTest extends AbstractGenericTest { assertEquals(fieldCnt, cnt); assertEquals(0, table.getRecordCount()); } + catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { dbh.deleteTable(table1Name); dbh.endTransaction(txId, true); @@ -1066,7 +1063,7 @@ public class DBIndexedTableTest extends AbstractGenericTest { private class RecColumnComparator implements Comparator { - int columnIx; + final int columnIx; RecColumnComparator(int columnIx) { this.columnIx = columnIx; @@ -1160,12 +1157,12 @@ public class DBIndexedTableTest extends AbstractGenericTest { public void testRecordIteratorExtents() throws IOException { Record[] recs = null; - recs = createOrderedRecordRange(DBTestUtils.SINGLE_BYTE, 30, 2, 1); + recs = createOrderedRecordRange(DBTestUtils.SINGLE_SHORT, 30, 2, 1); Table table = dbh.getTable(table1Name); assertEquals(recs.length, table.getRecordCount()); int[] indexedColumns = table.getIndexedColumns(); - assertEquals(table.getSchema().getFieldClasses().length, indexedColumns.length); + assertEquals(1, indexedColumns.length); // Backward Range iterator int colIx = 0; @@ -1173,8 +1170,8 @@ public class DBIndexedTableTest extends AbstractGenericTest { int recIx = recs.length - 1; // RecordIterator iter = table.indexIterator(colIx, recs[minIx].getField(colIx), // recs[maxIx].getField(colIx), false); - Field minField = new ByteField(Byte.MIN_VALUE); - Field maxField = new ByteField(Byte.MAX_VALUE); + Field minField = new ShortField(Short.MIN_VALUE); + Field maxField = new ShortField(Short.MAX_VALUE); RecordIterator iter = table.indexIterator(colIx, minField, maxField, false); while (iter.hasPrevious()) { Record rec = iter.previous(); @@ -1202,22 +1199,67 @@ public class DBIndexedTableTest extends AbstractGenericTest { @Test public void testRecordIteratorDelete() throws IOException { - for (int colIx = 0; colIx < 6; colIx++) { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { deleteIteratedRecords(ITER_REC_CNT, colIx, 1, 1); } - for (int colIx = 0; colIx < 6; colIx++) { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { deleteIteratedRecords(ITER_REC_CNT, colIx, 0, 1); } } @Test - public void testIndexFieldIteratorDelete() throws IOException { - for (int colIx = 0; colIx < 6; colIx++) { + public void testIndexFieldIteratorDelete() throws Exception { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { deleteIteratedIndexFields(ITER_REC_CNT, colIx, 1, 1); } - for (int colIx = 0; colIx < 6; colIx++) { + for (int colIx : DBTestUtils.getIndexedColumns(DBTestUtils.ALL_TYPES)) { deleteIteratedIndexFields(ITER_REC_CNT, colIx, 0, 1); } } + @Test + public void testConsistencyAndIndexRebuild() throws IOException { + + Record[] recs = createRandomTableRecords(DBTestUtils.ALL_TYPES, ITER_REC_CNT, 10); + + long txId = dbh.startTransaction(); + try { + assertTrue(dbh.isConsistent(TaskMonitor.DUMMY)); + dbh.rebuild(TaskMonitor.DUMMY); + } + catch (CancelledException e) { + fail("unexpected cancel exception"); + } + finally { + dbh.endTransaction(txId, true); + } + + Table table = dbh.getTable(table1Name); + for (int colIx : table.getIndexedColumns()) { + Arrays.sort(recs, new RecColumnComparator(colIx)); + int recIx = 0; + RecordIterator iter = table.indexIterator(colIx); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(ITER_REC_CNT, recIx); + } + + saveAsAndReopen(dbName); + + table = dbh.getTable(table1Name); + for (int colIx : table.getIndexedColumns()) { + Arrays.sort(recs, new RecColumnComparator(colIx)); + int recIx = 0; + RecordIterator iter = table.indexIterator(colIx); + while (iter.hasNext()) { + Record rec = iter.next(); + assertEquals(recs[recIx++], rec); + } + assertEquals(ITER_REC_CNT, recIx); + } + + } + } diff --git a/Ghidra/Framework/DB/src/test/java/db/DBLongKeyChainedBufferUseTest.java b/Ghidra/Framework/DB/src/test/java/db/DBLongKeyChainedBufferUseTest.java index 34cdb7e0cc..1de9a8c055 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBLongKeyChainedBufferUseTest.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBLongKeyChainedBufferUseTest.java @@ -69,8 +69,8 @@ public class DBLongKeyChainedBufferUseTest extends AbstractGenericTest { long txId = dbh.startTransaction(); Schema schema = new Schema(0, "Enum ID", - new Class[] { StringField.class, StringField.class, LongField.class, ByteField.class, - ShortField.class, IntField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE, ShortField.INSTANCE, IntField.INSTANCE }, new String[] { "str1", "str2", "long", "byte", "short", "int" }); Table table = dbh.createTable("TABLE1", schema); @@ -108,8 +108,8 @@ public class DBLongKeyChainedBufferUseTest extends AbstractGenericTest { long txId = dbh.startTransaction(); Schema schema = new Schema(0, "Enum ID", - new Class[] { StringField.class, StringField.class, LongField.class, ByteField.class, - ShortField.class, IntField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE, ShortField.INSTANCE, IntField.INSTANCE }, new String[] { "str1", "str2", "long", "byte", "short", "int" }); Table table = dbh.createTable("TABLE1", schema); diff --git a/Ghidra/Framework/DB/src/test/java/db/DBTest.java b/Ghidra/Framework/DB/src/test/java/db/DBTest.java index 58155546ee..7cce3ce1d1 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBTest.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBTest.java @@ -265,8 +265,7 @@ public class DBTest extends AbstractGenericTest { for (TableRecord tableRecord : tableRecords) { if (tableRecord.getIndexedColumn() < 0) { if (tableCnt > 0) { - Schema schema = lastTable.getSchema(); - assertEquals(schema.getFieldClasses().length, indexCnt); + assertEquals(DBTestUtils.getIndexedColumnCount(tableCnt - 1), indexCnt); } String name = "TABLE" + tableCnt; lastTable = dbh.getTable(name); @@ -281,7 +280,9 @@ public class DBTest extends AbstractGenericTest { if (lastTable == null) { Assert.fail(); } - assertEquals(indexCnt, tableRecord.getIndexedColumn()); + int[] indexedColumns = DBTestUtils.getIndexedColumns(tableCnt - 1); + assertTrue(indexCnt < indexedColumns.length); + assertEquals(indexedColumns[indexCnt], tableRecord.getIndexedColumn()); assertEquals(lastTable.getName(), tableRecord.getName()); assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey()); assertEquals(0, tableRecord.getRecordCount()); @@ -290,8 +291,7 @@ public class DBTest extends AbstractGenericTest { } } - Schema schema = lastTable.getSchema(); - assertEquals(schema.getFieldClasses().length, indexCnt); + assertEquals(DBTestUtils.getIndexedColumnCount(tableCnt - 1), indexCnt); assertEquals(DBTestUtils.MAX_SCHEMA_TYPE + 1, tableCnt); } @@ -387,8 +387,7 @@ public class DBTest extends AbstractGenericTest { for (TableRecord tableRecord : tableRecords) { if (tableRecord.getIndexedColumn() < 0) { if (tableCnt > 0) { - Schema schema = lastTable.getSchema(); - assertEquals(schema.getFieldClasses().length, indexCnt); + assertEquals(DBTestUtils.getIndexedColumnCount(2 * (tableCnt - 1)), indexCnt); } String name = "TABLE" + (2 * tableCnt); lastTable = dbh.getTable(name); @@ -403,7 +402,9 @@ public class DBTest extends AbstractGenericTest { if (lastTable == null) { Assert.fail(); } - assertEquals(indexCnt, tableRecord.getIndexedColumn()); + int[] indexedColumns = DBTestUtils.getIndexedColumns(2 * (tableCnt - 1)); + assertTrue(indexCnt < indexedColumns.length); + assertEquals(indexedColumns[indexCnt], tableRecord.getIndexedColumn()); assertEquals(lastTable.getName(), tableRecord.getName()); assertEquals(Long.MIN_VALUE, tableRecord.getMaxKey()); assertEquals(0, tableRecord.getRecordCount()); @@ -413,7 +414,7 @@ public class DBTest extends AbstractGenericTest { } Schema schema = lastTable.getSchema(); - assertEquals(schema.getFieldClasses().length, indexCnt); + assertEquals(schema.getFields().length, indexCnt); assertEquals(totalTableCnt, tableCnt); } diff --git a/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java b/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java index b87e94a0cf..461aefffbc 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java @@ -17,6 +17,8 @@ package db; import java.io.File; import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; import java.util.Random; import org.junit.Assert; @@ -31,50 +33,85 @@ public class DBTestUtils { // Schema Types static final int EMPTY = 0; - static final int SINGLE_BYTE = 1; - static final int SINGLE_INT = 2; - static final int SINGLE_SHORT = 3; - static final int SINGLE_LONG = 4; - static final int SINGLE_STRING = 5; - static final int SINGLE_BINARY = 6; - static final int ALL_TYPES = 7; + static final int SINGLE_BOOLEAN = 1; + static final int SINGLE_BYTE = 2; + static final int SINGLE_INT = 3; + static final int SINGLE_SHORT = 4; + static final int SINGLE_LONG = 5; + static final int SINGLE_STRING = 6; + static final int SINGLE_BINARY = 7; + static final int SINGLE_FIXED = 8; + static final int ALL_TYPES = 9; - static final int MAX_SCHEMA_TYPE = 7; + static final int MAX_SCHEMA_TYPE = 9; - private static Class[][] schemaFields = { {}, // no columns - { ByteField.class }, { IntField.class }, { ShortField.class }, { LongField.class }, - { StringField.class }, { BinaryField.class }, { ByteField.class, IntField.class, - ShortField.class, LongField.class, StringField.class, BinaryField.class } }; + //@formatter:off + private static final Field[][] schemaFields = { + {}, // no columns + { BooleanField.INSTANCE }, + { ByteField.INSTANCE }, + { IntField.INSTANCE }, + { ShortField.INSTANCE }, + { LongField.INSTANCE }, + { StringField.INSTANCE }, + { BinaryField.INSTANCE }, + { FixedField10.INSTANCE }, + { BooleanField.INSTANCE, ByteField.INSTANCE, IntField.INSTANCE, ShortField.INSTANCE, + LongField.INSTANCE, StringField.INSTANCE, BinaryField.INSTANCE, FixedField10.INSTANCE } }; + //@formatter:on - private static String[][] schemaFieldNames = { {}, // no columns - { "Byte" }, { "Int" }, { "Short" }, { "Long" }, { "String" }, { "Binary" }, - { "Byte", "Int", "Short", "Long", "String", "Binary" } }; + private static final int[][] schemaIndexedColumns = + { {}, {}, {}, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 2, 3, 4, 5, 6, 7 } }; - private static Schema[] longKeySchemas = - { new Schema(0, "LongKey", schemaFields[0], schemaFieldNames[0]), - new Schema(0, "LongKey", schemaFields[1], schemaFieldNames[1]), - new Schema(0, "LongKey", schemaFields[2], schemaFieldNames[2]), - new Schema(0, "LongKey", schemaFields[3], schemaFieldNames[3]), - new Schema(0, "LongKey", schemaFields[4], schemaFieldNames[4]), - new Schema(0, "LongKey", schemaFields[5], schemaFieldNames[5]), - new Schema(0, "LongKey", schemaFields[6], schemaFieldNames[6]), - new Schema(0, "LongKey", schemaFields[7], schemaFieldNames[7]) }; + //@formatter:off + private static final String[][] schemaFieldNames = { + {}, // no columns + { "Boolean" }, { "Byte" }, { "Int" }, { "Short" }, { "Long" }, + { "String" }, { "Binary" }, { "Fixed" }, + { "Boolean", "Byte", "Int", "Short", "Long", "String", "Binary", "Fixed" } + }; + //@formatter:on - private static Field varKeyType = new BinaryField(); - private static Class varKeyClass = varKeyType.getClass(); + private static final Schema[] longKeySchemas; + static { + longKeySchemas = new Schema[MAX_SCHEMA_TYPE + 1]; + for (int i = 0; i < longKeySchemas.length; i++) { + longKeySchemas[i] = new Schema(0, "LongKey", schemaFields[i], schemaFieldNames[i]); + } + } - private static Schema[] binaryKeySchemas = - { new Schema(0, varKeyClass, "VarKey", schemaFields[0], schemaFieldNames[0]), - new Schema(0, varKeyClass, "VarKey", schemaFields[1], schemaFieldNames[1]), - new Schema(0, varKeyClass, "VarKey", schemaFields[2], schemaFieldNames[2]), - new Schema(0, varKeyClass, "VarKey", schemaFields[3], schemaFieldNames[3]), - new Schema(0, varKeyClass, "VarKey", schemaFields[4], schemaFieldNames[4]), - new Schema(0, varKeyClass, "VarKey", schemaFields[5], schemaFieldNames[5]), - new Schema(0, varKeyClass, "VarKey", schemaFields[6], schemaFieldNames[6]), - new Schema(0, varKeyClass, "VarKey", schemaFields[7], schemaFieldNames[7]) }; + private static final Field fixedKeyType = new FixedField10(); + + private static final Schema[] fixedKeySchemas; + static { + fixedKeySchemas = new Schema[MAX_SCHEMA_TYPE + 1]; + for (int i = 0; i < fixedKeySchemas.length; i++) { + fixedKeySchemas[i] = + new Schema(0, fixedKeyType, "FixedKey", schemaFields[i], schemaFieldNames[i]); + } + } + + private static final Field varKeyType = new BinaryField(); + + private static final Schema[] binaryKeySchemas; + static { + binaryKeySchemas = new Schema[MAX_SCHEMA_TYPE + 1]; + for (int i = 0; i < binaryKeySchemas.length; i++) { + binaryKeySchemas[i] = + new Schema(0, varKeyType, "VarKey", schemaFields[i], schemaFieldNames[i]); + } + } static Random random = new Random(0x123456789L); + static int[] getIndexedColumns(int schemaType) { + return schemaIndexedColumns[schemaType]; + } + + static int getIndexedColumnCount(int schemaType) { + return schemaIndexedColumns[schemaType].length; + } + /** * Create a new long-keyed table within the specified database. * @param db database handle @@ -89,11 +126,8 @@ public class DBTestUtils { Table t; int indexCnt = 0; if (createIndex) { - indexCnt = schemaFields[schemaType].length; - int[] indexedColumns = new int[indexCnt]; - for (int i = 0; i < indexedColumns.length; i++) { - indexedColumns[i] = i; - } + indexCnt = getIndexedColumnCount(schemaType); + int[] indexedColumns = getAllowedIndexColumns(schemaFields[schemaType]); t = db.createTable(name, longKeySchemas[schemaType], indexedColumns); } else { @@ -108,6 +142,49 @@ public class DBTestUtils { return t; } + static int[] getAllowedIndexColumns(Field[] columnFields) { + ArrayList list = new ArrayList<>(); + for (int i = 0; i < columnFields.length; i++) { + if (Field.canIndex(columnFields[i])) { + list.add(i); + } + } + int[] columnIndexes = new int[list.size()]; + for (int i = 0; i < columnIndexes.length; i++) { + columnIndexes[i] = list.get(i); + } + return columnIndexes; + } + + /** + * Create a new FixedField-keyed table within the specified database. + * @param db database handle + * @param name name of table + * @param schemaType type of schema (use static identifier) + * @param createIndex all fields will be indexed if true + * @return Table new table + * @throws IOException + */ + static Table createFixedKeyTable(DBHandle db, String name, int schemaType, boolean createIndex) + throws IOException { + Table t; + if (createIndex) { + int[] indexedColumns = getAllowedIndexColumns(schemaFields[schemaType]); + t = db.createTable(name, fixedKeySchemas[schemaType], indexedColumns); + Assert.assertArrayEquals(schemaIndexedColumns[schemaType], t.getIndexedColumns()); + } + else { + t = db.createTable(name, fixedKeySchemas[schemaType]); + Assert.assertEquals(0, t.getIndexedColumns().length); + } + Assert.assertEquals(name, t.getName()); + Assert.assertEquals(Long.MIN_VALUE, t.getMaxKey()); + Assert.assertEquals(0, t.getRecordCount()); + Assert.assertEquals(fixedKeySchemas[schemaType], t.getSchema()); + Assert.assertTrue(!t.useLongKeys()); + return t; + } + /** * Create a new BinaryField-keyed table within the specified database. * @param db database handle @@ -122,11 +199,7 @@ public class DBTestUtils { Table t; int indexCnt = 0; if (createIndex) { - indexCnt = schemaFields[schemaType].length; - int[] indexedColumns = new int[indexCnt]; - for (int i = 0; i < indexedColumns.length; i++) { - indexedColumns[i] = i; - } + int[] indexedColumns = getAllowedIndexColumns(schemaFields[schemaType]); t = db.createTable(name, binaryKeySchemas[schemaType], indexedColumns); } else { @@ -181,6 +254,33 @@ public class DBTestUtils { } } + /** + * Create a new random-FixedField-keyed record. + * @param table + * @param varDataSize + * @param doInsert + * @return Record + * @throws IOException + * @throws DuplicateKeyException + */ + static Record createFixedKeyRecord(Table table, int varDataSize, boolean doInsert) + throws IOException, DuplicateKeyException { + int keyLength = 10; + byte[] bytes = new byte[keyLength]; + random.nextBytes(bytes); + Field key = fixedKeyType.newField(); + key.setBinaryData(bytes); + + try { + Record rec = createRecord(table, key, varDataSize, doInsert); + Assert.assertEquals(key, rec.getKeyField()); + return rec; + } + catch (DuplicateKeyException dke) { + return createFixedKeyRecord(table, varDataSize, doInsert); + } + } + /** * Create a new random-BinaryField-keyed record. * @param table @@ -278,6 +378,31 @@ public class DBTestUtils { return rec; } + static FixedField addToFixedField(Field fixedField, long increment) { + FixedField f = (FixedField) fixedField; + byte[] valueBytes = f.getBinaryData(); + BigInteger v = new BigInteger(1, valueBytes); + v = v.add(BigInteger.valueOf(increment)); + byte[] resultBytes = v.toByteArray(); + if (resultBytes.length > valueBytes.length) { + if (resultBytes[0] != 0) { + throw new UnsupportedOperationException("overflow in test data"); + } + byte[] b = new byte[valueBytes.length]; + System.arraycopy(resultBytes, 1, b, 0, valueBytes.length); + resultBytes = b; + } + else if (resultBytes.length < valueBytes.length) { + byte[] b = new byte[valueBytes.length]; + System.arraycopy(resultBytes, 0, b, valueBytes.length - resultBytes.length, + resultBytes.length); + resultBytes = b; + } + FixedField r = f.newField(); + r.setBinaryData(resultBytes); + return r; + } + /** * Create a new record whose value is in the center portion of the valid * values range for byte, short, int, or long. @@ -359,7 +484,10 @@ public class DBTestUtils { Field[] fields = rec.getFields(); for (int i = 0; i < fields.length; i++) { - if (fields[i] instanceof ByteField) { + if (fields[i] instanceof BooleanField) { + rec.setBooleanValue(i, (random.nextInt() % 2) == 0); + } + else if (fields[i] instanceof ByteField) { rec.setByteValue(i, (byte) random.nextInt()); } else if (fields[i] instanceof ShortField) { @@ -389,7 +517,7 @@ public class DBTestUtils { } } else if (fields[i] instanceof BinaryField) { - int size = varDataSize; + int size = fields[i].isVariableLength() ? varDataSize : fields[i].length(); if (size < 0) { size = random.nextInt(6) - 1; } diff --git a/Ghidra/Framework/DB/src/test/java/db/TableTest.java b/Ghidra/Framework/DB/src/test/java/db/TableTest.java index 3541f8a8e1..8c26921b80 100644 --- a/Ghidra/Framework/DB/src/test/java/db/TableTest.java +++ b/Ghidra/Framework/DB/src/test/java/db/TableTest.java @@ -35,18 +35,19 @@ public class TableTest extends AbstractGenericTest { private static final int BUFFER_SIZE = 256; private static final int CACHE_SIZE = 4 * 1024 * 1024; - private static final Class[] FIXED_SIZE_SCHEMA_FIELD_CLASSES = - new Class[] { LongField.class, IntField.class, ShortField.class }; - private static final Class[] VARIABLE_SIZE_SCHEMA_FIELD_CLASSES = - new Class[] { StringField.class, }; + private static final Field[] FIXED_SIZE_SCHEMA_FIELDS = new Field[] { LongField.INSTANCE, + IntField.INSTANCE, ShortField.INSTANCE, FixedField10.INSTANCE }; + private static final Field[] VARIABLE_SIZE_SCHEMA_FIELDS = + new Field[] { StringField.INSTANCE, }; - private static final String[] FIXED_SIZE_SCHEMA_COLUMN_NAMES = { "Long1", "Int2", "Short3" }; + private static final String[] FIXED_SIZE_SCHEMA_COLUMN_NAMES = + { "Long1", "Int2", "Short3", "Fixed4" }; private static final String[] VARIABLE_SIZE_SCHEMA_COLUMN_NAMES = { "String" }; private static final Schema FIXED_SIZE_SCHEMA = - new Schema(0, "LongKey", FIXED_SIZE_SCHEMA_FIELD_CLASSES, FIXED_SIZE_SCHEMA_COLUMN_NAMES); - private static final Schema VARIABLE_SIZE_SCHEMA = new Schema(0, "LongKey", - VARIABLE_SIZE_SCHEMA_FIELD_CLASSES, VARIABLE_SIZE_SCHEMA_COLUMN_NAMES); + new Schema(0, "LongKey", FIXED_SIZE_SCHEMA_FIELDS, FIXED_SIZE_SCHEMA_COLUMN_NAMES); + private static final Schema VARIABLE_SIZE_SCHEMA = + new Schema(0, "LongKey", VARIABLE_SIZE_SCHEMA_FIELDS, VARIABLE_SIZE_SCHEMA_COLUMN_NAMES); private static final int BUFFER_COUNT = 5; private static final int FIRST_KEY = 0; private static final int END_KEY = BUFFER_COUNT * 100 - 10; @@ -272,6 +273,9 @@ public class TableTest extends AbstractGenericTest { Record rec = schema.createRecord(i * RECORD_KEY_SPACING); if (fixedSize) { rec.setLongValue(0, i); + rec.setIntValue(1, i); + rec.setShortValue(2, (short) i); + rec.setField(3, FixedField10.INSTANCE.getMaxValue()); } else { rec.setString(0, "abcdef"); diff --git a/Ghidra/Framework/FileSystem/src/test.slow/java/ghidra/framework/store/local/AbstractLocalFileSystemTest.java b/Ghidra/Framework/FileSystem/src/test.slow/java/ghidra/framework/store/local/AbstractLocalFileSystemTest.java index 1d68317388..23975e7b91 100644 --- a/Ghidra/Framework/FileSystem/src/test.slow/java/ghidra/framework/store/local/AbstractLocalFileSystemTest.java +++ b/Ghidra/Framework/FileSystem/src/test.slow/java/ghidra/framework/store/local/AbstractLocalFileSystemTest.java @@ -600,7 +600,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest { DBHandle dbh = new DBHandle(); long id = dbh.startTransaction(); dbh.createTable("test", - new Schema(0, "key", new Class[] { IntField.class }, new String[] { "dummy" })); + new Schema(0, "key", new Field[] { IntField.INSTANCE }, new String[] { "dummy" })); dbh.endTransaction(id, true); BufferFile bf = fs.createDatabase("/abc", "fred", null, "Database", dbh.getBufferSize(), "bob", null); @@ -741,7 +741,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest { DBHandle dbh = new DBHandle(); long id = dbh.startTransaction(); dbh.createTable("test", - new Schema(0, "key", new Class[] { IntField.class }, new String[] { "dummy" })); + new Schema(0, "key", new Field[] { IntField.INSTANCE }, new String[] { "dummy" })); dbh.endTransaction(id, true); BufferFile bf = fs.createDatabase("/abc", "greg", "123", "Database", dbh.getBufferSize(), "test", null); @@ -789,7 +789,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest { DBHandle dbh = new DBHandle(); long id = dbh.startTransaction(); dbh.createTable("test", - new Schema(0, "key", new Class[] { IntField.class }, new String[] { "dummy" })); + new Schema(0, "key", new Field[] { IntField.INSTANCE }, new String[] { "dummy" })); dbh.endTransaction(id, true); BufferFile bf = fs.createDatabase("/abc", "greg", "123", "Database", dbh.getBufferSize(), "test", null); @@ -933,7 +933,7 @@ public abstract class AbstractLocalFileSystemTest extends AbstractGenericTest { DBHandle dbh = new DBHandle(); long id = dbh.startTransaction(); dbh.createTable("test", - new Schema(0, "key", new Class[] { IntField.class }, new String[] { "dummy" })); + new Schema(0, "key", new Field[] { IntField.INSTANCE }, new String[] { "dummy" })); dbh.endTransaction(id, true); BufferFile bf = fs.createDatabase(folderPath, itemName, fileId, "Database", dbh.getBufferSize(), "test", null); diff --git a/Ghidra/Framework/FileSystem/src/test/java/db/RecoveryDBTest.java b/Ghidra/Framework/FileSystem/src/test/java/db/RecoveryDBTest.java index f2a3b6f12d..b797454d22 100644 --- a/Ghidra/Framework/FileSystem/src/test/java/db/RecoveryDBTest.java +++ b/Ghidra/Framework/FileSystem/src/test/java/db/RecoveryDBTest.java @@ -35,9 +35,10 @@ public class RecoveryDBTest extends AbstractGenericTest { private static int RECORD_COUNT = 1000; private static Schema SCHEMA = - new Schema(1, "key", new Class[] { StringField.class }, new String[] { "field1" }); + new Schema(1, "key", new Field[] { StringField.INSTANCE }, new String[] { "field1" }); - private static final File testDir = new File(AbstractGenericTest.getTestDirectoryPath(), "test"); + private static final File testDir = + new File(AbstractGenericTest.getTestDirectoryPath(), "test"); private LocalFileSystem fileSystem; diff --git a/Ghidra/Framework/FileSystem/src/test/java/ghidra/framework/store/db/PackedDatabaseTest.java b/Ghidra/Framework/FileSystem/src/test/java/ghidra/framework/store/db/PackedDatabaseTest.java index 25c7b5e7cd..7592b0a9e7 100644 --- a/Ghidra/Framework/FileSystem/src/test/java/ghidra/framework/store/db/PackedDatabaseTest.java +++ b/Ghidra/Framework/FileSystem/src/test/java/ghidra/framework/store/db/PackedDatabaseTest.java @@ -33,7 +33,7 @@ import utilities.util.FileUtilities; public class PackedDatabaseTest extends AbstractGenericTest { private static final Schema TEST_SCHEMA = - new Schema(1, "Key", new Class[] { StringField.class }, new String[] { "Col1" }); + new Schema(1, "Key", new Field[] { StringField.INSTANCE }, new String[] { "Col1" }); private File packedDbFile; private PackedDatabase db; diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/OptionsDB.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/OptionsDB.java index 69381ffaf9..7c029c01b0 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/OptionsDB.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/OptionsDB.java @@ -32,8 +32,9 @@ class OptionsDB extends AbstractOptions { private static final String PROPERTY_TABLE_NAME = "Property Table"; - private final static Schema PROPERTY_SCHEMA = new Schema(0, StringField.class, "Property Name", - new Class[] { StringField.class, ByteField.class }, new String[] { "Value", "Type" }); + private final static Schema PROPERTY_SCHEMA = new Schema(0, StringField.INSTANCE, + "Property Name", new Field[] { StringField.INSTANCE, ByteField.INSTANCE }, + new String[] { "Value", "Type" }); private static final int VALUE_COL = 0; private static final int TYPE_COL = 1; @@ -81,8 +82,8 @@ class OptionsDB extends AbstractOptions { throw new IllegalArgumentException("property alteration old-path may not be null"); } if (path != null && path.endsWith(DELIMITER_STRING)) { - throw new IllegalArgumentException("property alteration paths must not end with '" + - DELIMITER + "': " + path); + throw new IllegalArgumentException( + "property alteration paths must not end with '" + DELIMITER + "': " + path); } } @@ -118,8 +119,8 @@ class OptionsDB extends AbstractOptions { String keyName = ((StringField) rec.getKeyField()).getString(); if (keyName.startsWith(oldSubListPath)) { iterator.delete(); - rec.setKey(new StringField(newSubListPath + - keyName.substring(oldSubListPath.length()))); + rec.setKey( + new StringField(newSubListPath + keyName.substring(oldSubListPath.length()))); list.add(rec); } else { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java index 50b8c914fd..5bb75c0c1c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DBObjectCache.java @@ -158,6 +158,7 @@ public class DBObjectCache { * within the specified keyRanges. * @param keyRanges key ranges to delete */ +//TODO: Discourage large cases by only allowing a single range to be specified public synchronized void delete(List keyRanges) { hardCache.clear(); processQueue(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java index 847a22d0e4..ea02a2a1f5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java @@ -45,8 +45,11 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB * database schema associated with any of the managers. * 18-Sep-2008 - version 1 - added fields for synchronizing program data types with project archives. * 03-Dec-2009 - version 2 - Added source archive updating (consolidating windows.gdt, clib.gdt, ntddk.gdt) + * 14-Nov-2019 - version 3 - Corrected fixed length indexing implementation causing + * change in index table low-level storage for newly + * created tables. */ - static final int DB_VERSION = 2; + static final int DB_VERSION = 3; /** * UPGRADE_REQUIRED_BEFORE_VERSION should be changed to DB_VERSION any time the @@ -76,10 +79,10 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB private static final String DEFAULT_POINTER_SIZE = "Default Pointer Size"; - private final static Class[] COL_CLASS = new Class[] { StringField.class }; + private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE }; private final static String[] COL_TYPES = new String[] { "Value" }; private final static Schema SCHEMA = - new Schema(0, StringField.class, "Key", COL_CLASS, COL_TYPES); + new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_TYPES); private ProjectDataTypeManager dataTypeManager; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java index bcfd237992..bdb12f94c7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDBChangeSet.java @@ -30,7 +30,7 @@ import ghidra.program.model.listing.DataTypeArchiveChangeSet; class DataTypeArchiveDBChangeSet implements DataTypeArchiveChangeSet, DomainObjectDBChangeSet { private static final Schema STORED_ID_SCHEMA = - new Schema(0, "Key", new Class[] { LongField.class }, new String[] { "value" }); + new Schema(0, "Key", new Field[] { LongField.INSTANCE }, new String[] { "value" }); private static final String DATATYPE_ADDITIONS = "DataType Additions"; private static final String DATATYPE_CHANGES = "DataType Changes"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/IntRangeMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/IntRangeMapDB.java index 8367daf2f8..0b28d68073 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/IntRangeMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/IntRangeMapDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,10 @@ */ package ghidra.program.database; +import java.util.ConcurrentModificationException; + +import db.*; +import db.util.ErrorHandler; import ghidra.program.database.map.AddressMap; import ghidra.program.database.util.AddressRangeMapDB; import ghidra.program.model.address.*; @@ -25,11 +28,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; -import java.util.ConcurrentModificationException; - -import db.*; -import db.util.ErrorHandler; - public class IntRangeMapDB implements IntRangeMap { private static final String MY_PREFIX = "IntMap - "; @@ -65,8 +63,8 @@ public class IntRangeMapDB implements IntRangeMap { DBHandle dbh = program.getDBHandle(); String tableName = TABLE_PREFIX + mapName; if (dbh.getTable(tableName) != null) { - throw new DuplicateNameException("Address Set Property Map named " + mapName + - " already exists."); + throw new DuplicateNameException( + "Address Set Property Map named " + mapName + " already exists."); } return new IntRangeMapDB(program, mapName, program, addrMap, lock); @@ -82,9 +80,8 @@ public class IntRangeMapDB implements IntRangeMap { this.mapName = mapName; this.lock = lock; - propertyMap = - new AddressRangeMapDB(program.getDBHandle(), program.getAddressMap(), - program.getLock(), MY_PREFIX + mapName, errHandler, IntField.class, true); + propertyMap = new AddressRangeMapDB(program.getDBHandle(), program.getAddressMap(), + program.getLock(), MY_PREFIX + mapName, errHandler, IntField.INSTANCE, true); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java index d36def243d..452868a032 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java @@ -30,7 +30,8 @@ import ghidra.util.exception.DuplicateNameException; class OverlaySpaceAdapterDB { private static String TABLE_NAME = "Overlay Spaces"; static final Schema SCHEMA = new Schema(0, "ID", - new Class[] { StringField.class, StringField.class, LongField.class, LongField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE }, new String[] { "Overlay Space", "Template Space", "Minimum Offset", "Maximum Offset" }); private static final int OV_SPACE_NAME_COL = 0; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index ae38a8c340..59edc0550e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -93,8 +93,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM * Read of old symbol data3 format does not require upgrade. * 14-May-2020 - version 21 - added support for overlay mapped blocks and byte mapping * schemes other than the default 1:1 + * 19-Jun-2020 - version 22 - Corrected fixed length indexing implementation causing + * change in index table low-level storage for newly + * created tables. */ - static final int DB_VERSION = 21; + static final int DB_VERSION = 22; /** * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the @@ -133,10 +136,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM private static final String EXECUTE_FORMAT = "Execute Format"; private static final String IMAGE_OFFSET = "Image Offset"; - private final static Class[] COL_CLASS = new Class[] { StringField.class }; + private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE }; private final static String[] COL_TYPES = new String[] { "Value" }; private final static Schema SCHEMA = - new Schema(0, StringField.class, "Key", COL_CLASS, COL_TYPES); + new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_TYPES); // // The numbering of managers controls the order in which they are notified. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDBChangeSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDBChangeSet.java index ef2f8a98e9..1c72d22338 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDBChangeSet.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDBChangeSet.java @@ -34,10 +34,10 @@ import ghidra.program.model.listing.ProgramChangeSet; class ProgramDBChangeSet implements ProgramChangeSet, DomainObjectDBChangeSet { private static final Schema STORED_ID_SCHEMA = - new Schema(0, "Key", new Class[] { LongField.class }, new String[] { "value" }); + new Schema(0, "Key", new Field[] { LongField.INSTANCE }, new String[] { "value" }); private static final Schema STORED_ADDRESS_RANGE_SCHEMA = new Schema(0, "Key", - new Class[] { LongField.class, LongField.class }, new String[] { "addr1", "addr2" }); + new Field[] { LongField.INSTANCE, LongField.INSTANCE }, new String[] { "addr1", "addr2" }); private static final String DATATYPE_ADDITIONS = "DataType Additions"; private static final String DATATYPE_CHANGES = "DataType Changes"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java index edb64097ed..23778238ff 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java @@ -51,7 +51,7 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData * DB_VERSION should be incremented any time a change is made to the overall * database schema associated with any of the managers. */ - static final int DB_VERSION = 1; + static final int DB_VERSION = 2; /** * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION any time the @@ -59,13 +59,13 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData * until upgrade is performed). It is assumed that read-only mode is supported * if the data's version is >= UPGRADE_REQUIRED_BEFORE_VERSION and <= DB_VERSION. */ - private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 1; + private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 2; private static final String TABLE_NAME = "ProgramUserData"; - private final static Class[] COL_CLASS = new Class[] { StringField.class }; + private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE }; private final static String[] COL_NAMES = new String[] { "Value" }; private final static Schema SCHEMA = - new Schema(0, StringField.class, "Key", COL_CLASS, COL_NAMES); + new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_NAMES); private static final int VALUE_COL = 0; private static final String STORED_DB_VERSION = "DB Version"; @@ -73,12 +73,12 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData private static final String LANGUAGE_ID = "Language ID"; private static final String REGISTRY_TABLE_NAME = "PropertyRegistry"; - private final static Class[] REGISTRY_COL_CLASS = - new Class[] { StringField.class, StringField.class, IntField.class, StringField.class }; + private final static Field[] REGISTRY_COL_FIELDS = new Field[] { StringField.INSTANCE, + StringField.INSTANCE, IntField.INSTANCE, StringField.INSTANCE }; private final static String[] REGISTRY_COL_NAMES = new String[] { "Owner", "PropertyName", "PropertyType", "SaveableClass" }; private final static Schema REGISTRY_SCHEMA = - new Schema(0, "ID", REGISTRY_COL_CLASS, REGISTRY_COL_NAMES); + new Schema(0, "ID", REGISTRY_COL_FIELDS, REGISTRY_COL_NAMES); private static final int PROPERTY_OWNER_COL = 0; private static final int PROPERTY_NAME_COL = 1; private static final int PROPERTY_TYPE_COL = 2; @@ -467,7 +467,8 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData Class saveableClass, boolean create) throws PropertyTypeMismatchException { try { - for (long key : registryTable.findRecords(new StringField(owner), PROPERTY_OWNER_COL)) { + for (Field key : registryTable.findRecords(new StringField(owner), + PROPERTY_OWNER_COL)) { Record rec = registryTable.getRecord(key); if (propertyName.equals(rec.getString(PROPERTY_NAME_COL))) { int type = rec.getIntValue(PROPERTY_TYPE_COL); @@ -573,7 +574,8 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData public synchronized List getProperties(String owner) { List list = new ArrayList(); try { - for (long key : registryTable.findRecords(new StringField(owner), PROPERTY_OWNER_COL)) { + for (Field key : registryTable.findRecords(new StringField(owner), + PROPERTY_OWNER_COL)) { Record rec = registryTable.getRecord(key); list.add(getPropertyMap(rec)); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBAdapterV3.java index 20ee24b550..42ab0fdc91 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBAdapterV3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBAdapterV3.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.bookmark; -import ghidra.program.database.map.AddressMap; -import ghidra.program.database.util.EmptyRecordIterator; -import ghidra.program.model.address.*; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.HashSet; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.program.database.util.EmptyRecordIterator; +import ghidra.program.model.address.*; +import ghidra.util.exception.VersionException; public class BookmarkDBAdapterV3 extends BookmarkDBAdapter { @@ -35,8 +33,9 @@ public class BookmarkDBAdapterV3 extends BookmarkDBAdapter { static final int V3_COMMENT_COL = 2; static final int VERSION = 3; - static final Schema V3_SCHEMA = new Schema(VERSION, "ID", new Class[] { LongField.class, - StringField.class, StringField.class }, new String[] { "Address", "Category", "Comment" }); + static final Schema V3_SCHEMA = new Schema(VERSION, "ID", + new Field[] { LongField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE }, + new String[] { "Address", "Category", "Comment" }); static int[] INDEXED_COLUMNS = new int[] { V3_ADDRESS_COL, V3_CATEGORY_COL }; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java index 7e5a2b6ecf..57d06f9cc8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkDBManager.java @@ -296,9 +296,8 @@ public class BookmarkDBManager implements BookmarkManager, ErrorHandler, Manager bm.setComment(comment); } else { - Record rec = - bookmarkAdapter.createBookmark(typeId, category, addrMap.getKey(addr, true), - comment); + Record rec = bookmarkAdapter.createBookmark(typeId, category, + addrMap.getKey(addr, true), comment); bm = new BookmarkDB(this, cache, rec); // fire event @@ -606,9 +605,8 @@ public class BookmarkDBManager implements BookmarkManager, ErrorHandler, Manager RecordIterator it; try { if (bmt != null && bmt.hasBookmarks()) { - it = - bookmarkAdapter.getRecordsByTypeStartingAtAddress(bmt.getTypeId(), - addrMap.getKey(startAddress, false), forward); + it = bookmarkAdapter.getRecordsByTypeStartingAtAddress(bmt.getTypeId(), + addrMap.getKey(startAddress, false), forward); } else { it = new EmptyRecordIterator(); @@ -761,11 +759,10 @@ public class BookmarkDBManager implements BookmarkManager, ErrorHandler, Manager try { Table table = bookmarkAdapter.getTable(typeId); if (table != null) { - DBLongIterator it = - new AddressIndexPrimaryKeyIterator(table, BookmarkDBAdapter.ADDRESS_COL, - addrMap, set, true); + DBFieldIterator it = new AddressIndexPrimaryKeyIterator(table, + BookmarkDBAdapter.ADDRESS_COL, addrMap, set, true); while (it.hasNext()) { - BookmarkDB bm = (BookmarkDB) getBookmark(it.next()); + BookmarkDB bm = (BookmarkDB) getBookmark(it.next().getLongValue()); if (category == null || category.equals(bm.getCategory())) { doRemoveBookmark(bm); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkTypeDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkTypeDBAdapter.java index 717c0ad239..b15dc914ae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkTypeDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/bookmark/BookmarkTypeDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.bookmark; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; abstract class BookmarkTypeDBAdapter { @@ -28,8 +26,8 @@ abstract class BookmarkTypeDBAdapter { static final int TYPE_NAME_COL = 0; - static final Schema SCHEMA = new Schema(0, "ID", new Class[] { StringField.class }, - new String[] { "Name" }); + static final Schema SCHEMA = + new Schema(0, "ID", new Field[] { StringField.INSTANCE }, new String[] { "Name" }); static BookmarkTypeDBAdapter getAdapter(DBHandle dbHandle, int openMode) throws VersionException, IOException { @@ -58,8 +56,8 @@ abstract class BookmarkTypeDBAdapter { return new BookmarkTypeDBAdapterNoTable(dbHandle); } - private static BookmarkTypeDBAdapter upgrade(DBHandle dbHandle, BookmarkTypeDBAdapter oldAdapter) - throws VersionException, IOException { + private static BookmarkTypeDBAdapter upgrade(DBHandle dbHandle, + BookmarkTypeDBAdapter oldAdapter) throws VersionException, IOException { return new BookmarkTypeDBAdapterV0(dbHandle, true); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentHistoryAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentHistoryAdapter.java index 54138c8dfa..92e9cfa4d0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentHistoryAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentHistoryAdapter.java @@ -32,8 +32,8 @@ abstract class CommentHistoryAdapter { static final String COMMENT_HISTORY_TABLE_NAME = "Comment History"; static final Schema COMMENT_HISTORY_SCHEMA = new Schema(0, "Key", - new Class[] { LongField.class, ByteField.class, IntField.class, IntField.class, - StringField.class, StringField.class, LongField.class }, + new Field[] { LongField.INSTANCE, ByteField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE }, new String[] { "Address", "Comment Type", "Pos1", "Pos2", "String Data", "User", "Date" }); static final int HISTORY_ADDRESS_COL = 0; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java index e442439936..bb66b1e171 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CommentsDBAdapter.java @@ -57,8 +57,8 @@ abstract class CommentsDBAdapter { NAMES[REPEATABLE_COMMENT_COL] = "Repeatable"; COMMENTS_SCHEMA = - new Schema(1, "Address", new Class[] { StringField.class, StringField.class, - StringField.class, StringField.class, StringField.class }, NAMES); + new Schema(1, "Address", new Field[] { StringField.INSTANCE, StringField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE }, NAMES); } // /** comment type for end of line */ @@ -110,8 +110,8 @@ abstract class CommentsDBAdapter { } private static CommentsDBAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - CommentsDBAdapter oldAdapter, TaskMonitor monitor) throws VersionException, - IOException, CancelledException { + CommentsDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index a608123d2f..155536cf99 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -103,6 +103,8 @@ class DataDB extends CodeUnitDB implements Data { DataType dt; if (rec != null) { // ensure that record provided corresponds to a DataDB record + // since following an undo/redo the record could correspond to + // a different type of code unit (hopefully with a different record schema) if (!rec.hasSameSchema(DataDBAdapter.DATA_SCHEMA)) { return true; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java index 92d51ba2cb..e8bea491aa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +18,9 @@ */ package ghidra.program.database.code; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressKeyIterator; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; @@ -27,10 +29,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Adapter to access the Data table. */ @@ -38,7 +36,7 @@ abstract class DataDBAdapter { static final String DATA_TABLE_NAME = "Data"; - static final Schema DATA_SCHEMA = new Schema(0, "Address", new Class[] { LongField.class }, + static final Schema DATA_SCHEMA = new Schema(0, "Address", new Field[] { LongField.INSTANCE }, new String[] { "Data Type ID" }); static final int DATA_TYPE_ID_COL = 0; @@ -75,8 +73,8 @@ abstract class DataDBAdapter { } private static DataDBAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - DataDBAdapter oldAdapter, TaskMonitor monitor) throws VersionException, IOException, - CancelledException { + DataDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapter.java index e5e66783a7..fa7c2f0eaf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,9 @@ */ package ghidra.program.database.code; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressKeyIterator; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; @@ -24,10 +26,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Adapter that accesses the instruction table. */ @@ -35,8 +33,9 @@ abstract class InstDBAdapter { static final String INSTRUCTION_TABLE_NAME = "Instructions"; - static final Schema INSTRUCTION_SCHEMA = new Schema(1, "Address", new Class[] { IntField.class, - ByteField.class }, new String[] { "Proto ID", "Flags" }); + static final Schema INSTRUCTION_SCHEMA = + new Schema(1, "Address", new Field[] { IntField.INSTANCE, ByteField.INSTANCE }, + new String[] { "Proto ID", "Flags" }); static final int PROTO_ID_COL = 0; static final int FLAGS_COL = 1; @@ -79,8 +78,8 @@ abstract class InstDBAdapter { } private static InstDBAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - InstDBAdapter oldAdapter, TaskMonitor monitor) throws VersionException, IOException, - CancelledException { + InstDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java index 653b732cfd..4e22e5b031 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java @@ -90,7 +90,9 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio return true; } } - // ensure that record provided corresponds to an InstructionDB record + // ensure that record provided corresponds to a DataDB record + // since following an undo/redo the record could correspond to + // a different type of code unit (hopefully with a different record schema) else if (!rec.hasSameSchema(InstDBAdapter.INSTRUCTION_SCHEMA)) { return true; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/PrototypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/PrototypeManager.java index b0b0bc823b..bfd2581534 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/PrototypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/PrototypeManager.java @@ -68,16 +68,15 @@ class PrototypeManager { final static Schema REGISTER_SCHEMA = createRegisterSchema(); private static Schema createPrototypeSchema() { - Schema schema = - new Schema(1, "Keys", new Class[] { BinaryField.class, LongField.class, - BooleanField.class }, new String[] { "Bytes", "Address", "InDelaySlot" }); + Schema schema = new Schema(1, "Keys", + new Field[] { BinaryField.INSTANCE, LongField.INSTANCE, BooleanField.INSTANCE }, + new String[] { "Bytes", "Address", "InDelaySlot" }); return schema; } private static Schema createRegisterSchema() { - Schema schema = - new Schema(1, "Keys", new Class[] { StringField.class }, - new String[] { "Register Context" }); + Schema schema = new Schema(1, "Keys", new Field[] { StringField.INSTANCE }, + new String[] { "Register Context" }); return schema; } @@ -412,8 +411,8 @@ class PrototypeManager { } } - private void loadContextTable(DBHandle dbHandle, int openMode) throws VersionException, - IOException { + private void loadContextTable(DBHandle dbHandle, int openMode) + throws VersionException, IOException { contextTable = dbHandle.getTable(CONTEXT_TABLE_NAME); if (contextTable == null) { contextTable = dbHandle.createTable(CONTEXT_TABLE_NAME, REGISTER_SCHEMA); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java index 7b7e588782..500eee0df7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * @@ -90,29 +88,16 @@ abstract class ArrayDBAdapter { abstract Record createRecord(long dataTypeID, int numberOfElements, int length, long catID) throws IOException; - /** - * @param arrayID - * @return - */ abstract Record getRecord(long arrayID) throws IOException; - /** - * @return - */ abstract RecordIterator getRecords() throws IOException; - /** - * @param dataID - */ abstract boolean removeRecord(long dataID) throws IOException; abstract void updateRecord(Record record) throws IOException; - /** - * - */ abstract void deleteTable(DBHandle handle) throws IOException; - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java index 3ccf6edfed..47243e614d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * @@ -57,42 +55,27 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#createRecord(long, int) - */ @Override public Record createRecord(long dataTypeID, int numberOfElements, int length, long catID) throws IOException { return null; } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#getRecord(long) - */ @Override public Record getRecord(long arrayID) throws IOException { return translateRecord(table.getRecord(arrayID)); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#getRecords() - */ @Override public RecordIterator getRecords() throws IOException { return new TranslatedRecordIterator(table.iterator()); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#removeRecord(long) - */ @Override public boolean removeRecord(long dataID) throws IOException { return false; } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ @Override public void updateRecord(Record record) throws IOException { } @@ -116,43 +99,42 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter { this.it = it; } + @Override public boolean delete() throws IOException { throw new UnsupportedOperationException(); } + @Override public boolean hasNext() throws IOException { return it.hasNext(); } + @Override public boolean hasPrevious() throws IOException { return it.hasPrevious(); } + @Override public Record next() throws IOException { Record rec = it.next(); return translateRecord(rec); } + @Override public Record previous() throws IOException { Record rec = it.previous(); return translateRecord(rec); } } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#deleteTable(ghidra.framework.store.db.DBHandle) - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(ARRAY_TABLE_NAME); } - /* (non-Javadoc) - * @see ghidra.program.database.data.ArrayDBAdapter#getRecordIdsInCategory(long) - */ @Override - long[] getRecordIdsInCategory(long categoryID) throws IOException { - return new long[0]; + Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return Field.EMPTY_ARRAY; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java index 9902077db6..3212f66737 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDBAdapterV1.java @@ -15,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * @@ -38,9 +37,11 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { private Table table; - public static final Schema V1_SCHEMA = new Schema(VERSION, "Array ID", new Class[] { - LongField.class, IntField.class, IntField.class, LongField.class }, new String[] { - "Data Type ID", "Dimension", "Length", "Cat ID" }); + public static final Schema V1_SCHEMA = + new Schema(VERSION, "Array ID", + new Field[] { LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, + LongField.INSTANCE }, + new String[] { "Data Type ID", "Dimension", "Length", "Cat ID" }); /** * Constructor @@ -62,9 +63,6 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { } } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#createRecord(long, int) - */ @Override public Record createRecord(long dataTypeID, int numberOfElements, int length, long catID) throws IOException { @@ -84,52 +82,34 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter { return record; } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#getRecord(long) - */ @Override public Record getRecord(long arrayID) throws IOException { return table.getRecord(arrayID); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#getRecords() - */ @Override public RecordIterator getRecords() throws IOException { return table.iterator(); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#removeRecord(long) - */ @Override public boolean removeRecord(long dataID) throws IOException { return table.deleteRecord(dataID); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ @Override public void updateRecord(Record record) throws IOException { table.putRecord(record); } - /** - * @see ghidra.program.database.data.ArrayDBAdapter#deleteTable(ghidra.framework.store.db.DBHandle) - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(ARRAY_TABLE_NAME); } - /* (non-Javadoc) - * @see ghidra.program.database.data.ArrayDBAdapter#getRecordIdsInCategory(long) - */ @Override - long[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V1_ARRAY_CAT_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java index 1cefae37b1..acfe8d8741 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Database adapter for managing built-in data types. @@ -67,11 +65,12 @@ public abstract class BuiltinDBAdapter { /** * Returns an array containing the data type IDs for the given category ID - * @return an array of the data type IDs; - * empty array if no built-in data types. + * @param categoryID category ID + * @return an array of the data type IDs as LongFields within Field array; + * empty array if no built-in data types found. * @throws IOException if there was a problem accessing the database */ - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; /** * Update the built-ins table with the given record. @@ -83,12 +82,15 @@ public abstract class BuiltinDBAdapter { /** * Remove the record with the given dataID. * @param dataID key + * @return true if record was deleted successfully. * @throws IOException if there was a problem accessing the database */ abstract boolean removeRecord(long dataID) throws IOException; /** * Returns an iterator over all records for built-in data types. + * @return record iterator + * @throws IOException if IO error occurs */ abstract RecordIterator getRecords() throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java index ab0da09b0b..a2010df9de 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/BuiltinDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +18,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation of the adapter for accessing the built-ins table. @@ -33,8 +31,9 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter { static final int V0_BUILT_IN_NAME_COL = 0; static final int V0_BUILT_IN_CLASSNAME_COL = 1; static final int V0_BUILT_IN_CAT_COL = 2; - static final Schema V0_SCHEMA = new Schema(0, "Data Type ID", new Class[] { StringField.class, - StringField.class, LongField.class }, new String[] { "Name", "Class Name", "Category ID" }); + static final Schema V0_SCHEMA = new Schema(0, "Data Type ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE }, + new String[] { "Name", "Class Name", "Category ID" }); private Table table; /** @@ -45,12 +44,12 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter { * for this adapter. * @throws IOException if there is trouble accessing the database. */ - public BuiltinDBAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException { + public BuiltinDBAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - table = - handle.createTable(BUILT_IN_TABLE_NAME, V0_SCHEMA, - new int[] { V0_BUILT_IN_CAT_COL }); + table = handle.createTable(BUILT_IN_TABLE_NAME, V0_SCHEMA, + new int[] { V0_BUILT_IN_CAT_COL }); } else { table = handle.getTable(BUILT_IN_TABLE_NAME); @@ -70,7 +69,7 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_BUILT_IN_CAT_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java index 6b3c3e71da..0da7346a11 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import db.Field; import db.Record; import ghidra.program.database.DBObjectCache; import ghidra.program.database.DatabaseObject; @@ -181,10 +182,10 @@ class CategoryDB extends DatabaseObject implements Category { private CategoryDB[] getCategories(long parentId) { try { - long[] ids = mgr.getCategoryDBAdapter().getRecordIdsWithParent(parentId); + Field[] ids = mgr.getCategoryDBAdapter().getRecordIdsWithParent(parentId); CategoryDB[] cats = new CategoryDB[ids.length]; for (int i = 0; i < cats.length; i++) { - cats[i] = mgr.getCategoryDB(ids[i]); + cats[i] = mgr.getCategoryDB(ids[i].getLongValue()); } return cats; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java index a3343c7f33..f57771ffe4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapter.java @@ -20,8 +20,7 @@ package ghidra.program.database.data; import java.io.IOException; -import db.DBHandle; -import db.Record; +import db.*; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -38,31 +37,35 @@ abstract class CategoryDBAdapter { * Gets the category record for the given ID. * @param categoryID the key into the category table * @return the record for the given ID or null if no record with that id exists. + * @throws IOException if IO error occurs */ abstract Record getRecord(long categoryID) throws IOException; /** * Updates the record in the database - * @param categoryID - * @param parentCategoryID - * @param name - * @throws IOException + * @param categoryID category record key + * @param parentCategoryID parent category record key (-1 for root category) + * @param name category name + * @throws IOException if IO error occurs */ abstract void updateRecord(long categoryID, long parentCategoryID, String name) throws IOException; /** * Returns a list of categoryIDs that have the given parent ID. - * @param categoryID the key into the category table - * @return an array of categoryIDs that have the specified parent + * @param categoryID the key into the catagory table + * @return an array of categoryIDs that have the specified parent. Field array + * returned with LongField key values. + * @throws IOException if IO error occurs */ - abstract long[] getRecordIdsWithParent(long categoryID) throws IOException; + abstract Field[] getRecordIdsWithParent(long categoryID) throws IOException; /** * Creates a new category with the given name and parent ID. * @param name the name of the new category. - * @param categoryID the key into the category table + * @param parentID the parent key into the catagory table * @return a new record for the new category. + * @throws IOException if IO error occurs */ abstract Record createCategory(String name, long parentID) throws IOException; @@ -70,11 +73,14 @@ abstract class CategoryDBAdapter { * Removes the category with the given ID. * @param categoryID the key into the category table * @return true if the a category with that id existed. + * @throws IOException if IO error occurs */ abstract boolean removeCategory(long categoryID) throws IOException; /** * Get the record for the root category. + * @return root category record + * @throws IOException if IO error occurs */ abstract Record getRootRecord() throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java index e4f10452c8..e94ea31390 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +18,18 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.AssertException; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; class CategoryDBAdapterV0 extends CategoryDBAdapter { static final String CATEGORY_TABLE_NAME = "Categories"; static final int V0_CATEGORY_NAME_COL = 0; static final int V0_CATEGORY_PARENT_COL = 1; - static final Schema V0_SCHEMA = new Schema(0, "Category ID", new Class[] { StringField.class, - LongField.class }, new String[] { "Name", "Parent ID" }); + static final Schema V0_SCHEMA = + new Schema(0, "Category ID", new Field[] { StringField.INSTANCE, LongField.INSTANCE }, + new String[] { "Name", "Parent ID" }); private Table table; @@ -42,9 +40,8 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { public CategoryDBAdapterV0(DBHandle handle, int openMode) throws VersionException, IOException { if (openMode == DBConstants.CREATE) { - table = - handle.createTable(CATEGORY_TABLE_NAME, V0_SCHEMA, - new int[] { V0_CATEGORY_PARENT_COL }); + table = handle.createTable(CATEGORY_TABLE_NAME, V0_SCHEMA, + new int[] { V0_CATEGORY_PARENT_COL }); } else { table = handle.getTable(CATEGORY_TABLE_NAME); @@ -58,19 +55,13 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { } } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#getRecord(long) - */ @Override public Record getRecord(long categoryID) throws IOException { return table.getRecord(categoryID); } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#getRecordIdsWithParent(long) - */ @Override - public long[] getRecordIdsWithParent(long categoryID) throws IOException { + public Field[] getRecordIdsWithParent(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_CATEGORY_PARENT_COL); } @@ -87,9 +78,6 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { table.putRecord(record); } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#createCategory(java.lang.String, long) - */ @Override public Record createCategory(String name, long parentID) throws IOException { long key = table.getKey(); @@ -104,29 +92,20 @@ class CategoryDBAdapterV0 extends CategoryDBAdapter { } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#removeCategory(long) - */ @Override public boolean removeCategory(long categoryID) throws IOException { return table.deleteRecord(categoryID); } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#getRootRecord() - */ @Override public Record getRootRecord() throws IOException { - long[] keys = table.findRecords(new LongField(-1), V0_CATEGORY_PARENT_COL); - if (keys.length > 1) { - throw new AssertException("Found " + keys.length + " entries for root category"); + Field[] keys = table.findRecords(new LongField(-1), V0_CATEGORY_PARENT_COL); + if (keys.length != 1) { + throw new IOException("Found " + keys.length + " entries for root category"); } - return getRecord(keys[0]); + return getRecord(keys[0].getLongValue()); } - /** - * @see ghidra.program.database.data.CategoryDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return table.getRecordCount(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java index a9dd4d2fb6..4296c02173 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the Component database table. @@ -52,7 +50,7 @@ abstract class ComponentDBAdapter { */ static ComponentDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) throws VersionException, IOException { - return new ComponentDBAdapterV0(handle, openMode); + return new ComponentDBAdapterV0(handle, openMode == DBConstants.CREATE); } /** @@ -96,8 +94,8 @@ abstract class ComponentDBAdapter { /** * Gets an array with all of the IDs of the defined components within the composite data type indicated. * @param compositeID the ID of the composite data type whose components are desired. - * @return an array of the defined component IDs. + * @return an array of the defined component IDs as LongField values within Field array. * @throws IOException if there is a problem accessing the database. */ - abstract long[] getComponentIdsInComposite(long compositeID) throws IOException; + abstract Field[] getComponentIdsInComposite(long compositeID) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java index 40cbac9ed2..46cb232c02 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ComponentDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for accessing the Component database table. @@ -37,10 +35,11 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { static final int V0_COMPONENT_SIZE_COL = 5; static final int V0_COMPONENT_ORDINAL_COL = 6; - static final Schema V0_COMPONENT_SCHEMA = new Schema(0, "Data Type ID", new Class[] { - LongField.class, IntField.class, LongField.class, StringField.class, StringField.class, - IntField.class, IntField.class }, new String[] { "Parent", "Offset", "Data Type ID", - "Field Name", "Comment", "Component Size", "Ordinal" }); + static final Schema V0_COMPONENT_SCHEMA = new Schema(0, "Data Type ID", + new Field[] { LongField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, + new String[] { "Parent", "Offset", "Data Type ID", "Field Name", "Comment", + "Component Size", "Ordinal" }); private Table componentTable; /** @@ -50,12 +49,12 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public ComponentDBAdapterV0(DBHandle handle, int openMode) throws VersionException, IOException { + public ComponentDBAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { - if (openMode == DBConstants.CREATE) { - componentTable = - handle.createTable(COMPONENT_TABLE_NAME, V0_COMPONENT_SCHEMA, - new int[] { V0_COMPONENT_PARENT_ID_COL }); + if (create) { + componentTable = handle.createTable(COMPONENT_TABLE_NAME, V0_COMPONENT_SCHEMA, + new int[] { V0_COMPONENT_PARENT_ID_COL }); } else { componentTable = handle.getTable(COMPONENT_TABLE_NAME); @@ -64,9 +63,8 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { } int version = componentTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + COMPONENT_TABLE_NAME + - " but got " + componentTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + COMPONENT_TABLE_NAME + + " but got " + componentTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -112,7 +110,7 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter { } @Override - public long[] getComponentIdsInComposite(long compositeID) throws IOException { + public Field[] getComponentIdsInComposite(long compositeID) throws IOException { return componentTable.findRecords(new LongField(compositeID), ComponentDBAdapter.COMPONENT_PARENT_ID_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java index 262c99acff..25c7163dfe 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java @@ -95,7 +95,7 @@ abstract class CompositeDBAdapter { * @param handle handle to prior version of the database. * @return the read only Composite data type table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. - * @throws IOException + * @throws IOException if IO error occurs */ static CompositeDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException, IOException { @@ -194,7 +194,7 @@ abstract class CompositeDBAdapter { /** * Updates the composite data type table with the provided record. * @param record the new record - * @param setLastChangedTime true means change the last change time in the record to the + * @param setLastChangeTime true means change the last change time in the record to the * current time before putting the record in the database. * @throws IOException if the database can't be accessed. */ @@ -218,10 +218,11 @@ abstract class CompositeDBAdapter { /** * Gets all the composite data types that are contained in the category that has the indicated ID. * @param categoryID the category whose composite data types are wanted. - * @return an array of IDs for the composite data types in the category. + * @return an array of IDs as LongField values within Field array for the + * composite data types in the category. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; /** * Gets an array with the IDs of all data types in the composite table that were derived @@ -230,8 +231,15 @@ abstract class CompositeDBAdapter { * @return the array data type IDs. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + abstract Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + /** + * Get composite record whoose sourceID and datatypeID match the specified Universal IDs. + * @param sourceID universal source archive ID + * @param datatypeID universal datatype ID + * @return composite record found or null + * @throws IOException if IO error occurs + */ abstract Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java index bd731bb8b1..2b49b1e4f4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.data; +import java.io.IOException; + +import db.*; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; -import java.io.IOException; - -import db.*; - /** * Version 0 implementation for accessing the Composite database table. */ @@ -38,10 +36,11 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato static final int V0_COMPOSITE_LENGTH_COL = 4; static final int V0_COMPOSITE_NUM_COMPONENTS_COL = 5; - static final Schema V0_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", new Class[] { - StringField.class, StringField.class, BooleanField.class, LongField.class, IntField.class, - IntField.class }, new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", - "Number Of Components" }); + static final Schema V0_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, + LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, + new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", + "Number Of Components" }); private Table compositeTable; @@ -59,9 +58,8 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } int version = compositeTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + " but got " + - compositeTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + + " but got " + compositeTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -98,7 +96,7 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } @@ -109,13 +107,11 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java index 781ae00c0b..d1b9ab548c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Composite database table. @@ -39,9 +37,10 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato static final int V1_COMPOSITE_SOURCE_SYNC_TIME_COL = 8; static final int V1_COMPOSITE_LAST_CHANGE_TIME_COL = 9; - static final Schema V1_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", new Class[] { - StringField.class, StringField.class, BooleanField.class, LongField.class, IntField.class, - IntField.class, LongField.class, LongField.class, LongField.class, LongField.class }, + static final Schema V1_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, + LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", "Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time" }); @@ -62,9 +61,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } int version = compositeTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + " but got " + - compositeTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME + + " but got " + compositeTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -106,13 +104,13 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return compositeTable.findRecords(new LongField(archiveID), V1_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } @@ -120,6 +118,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato /* (non-Javadoc) * @see db.RecordTranslator#translateRecord(db.Record) */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; @@ -148,9 +147,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato @Override Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - long[] keys = - compositeTable.findRecords(new LongField(datatypeID.getValue()), - V1_COMPOSITE_UNIVERSAL_DT_ID_COL); + Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()), + V1_COMPOSITE_UNIVERSAL_DT_ID_COL); for (int i = 0; i < keys.length; i++) { Record record = compositeTable.getRecord(keys[i]); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java index 25758404ee..af757d2b3c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java @@ -54,9 +54,10 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter { static final int V2_COMPOSITE_EXTERNAL_ALIGNMENT_COL = 11; static final Schema V2_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID", - new Class[] { StringField.class, StringField.class, BooleanField.class, LongField.class, - IntField.class, IntField.class, LongField.class, LongField.class, LongField.class, - LongField.class, IntField.class, IntField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE, + LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, + IntField.INSTANCE }, new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", "Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time", "Internal Alignment", "External Alignment" }); @@ -190,20 +191,20 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return compositeTable.findRecords(new LongField(categoryID), CompositeDBAdapter.COMPOSITE_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return compositeTable.findRecords(new LongField(archiveID), V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL); } @Override Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - long[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()), + Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()), V2_COMPOSITE_UNIVERSAL_DT_ID_COL); for (int i = 0; i < keys.length; i++) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index f983f40dc6..0c2f0c9fdc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -1848,9 +1848,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @param parentID the parentData type's ID */ private void removeParameters(long parentID) throws IOException { - long[] paramIDs = paramAdapter.getParameterIdsInFunctionDef(parentID); - for (long paramID : paramIDs) { - deleteDataTypeRecord(paramID); + Field[] paramIDs = paramAdapter.getParameterIdsInFunctionDef(parentID); + for (Field paramID : paramIDs) { + deleteDataTypeRecord(paramID.getLongValue()); } } @@ -1860,9 +1860,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @param parentID the parentData type's ID */ private void removeComponents(long parentID) throws IOException { - long[] componentIDs = componentAdapter.getComponentIdsInComposite(parentID); - for (long componentID : componentIDs) { - deleteDataTypeRecord(componentID); + Field[] componentIDs = componentAdapter.getComponentIdsInComposite(parentID); + for (Field componentID : componentIDs) { + deleteDataTypeRecord(componentID.getLongValue()); } } @@ -1954,7 +1954,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager { lock.acquire(); ArrayList list = new ArrayList<>(); try { - long[] ids = builtinAdapter.getRecordIdsInCategory(categoryID); + Field[] ids = builtinAdapter.getRecordIdsInCategory(categoryID); getDataTypes(ids, list); ids = typedefAdapter.getRecordIdsInCategory(categoryID); @@ -2011,9 +2011,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } } - private void getDataTypes(long[] ids, ArrayList list) { - for (long id : ids) { - DataType dt = getDataType(id); + private void getDataTypes(Field[] ids, ArrayList list) { + for (Field id : ids) { + DataType dt = getDataType(id.getLongValue()); if (dt == null) { throw new AssertException("Could not find data type id: " + id); } @@ -2850,14 +2850,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager { void dataTypeCategoryPathChanged(DataTypeDB dt, CategoryPath oldPath, long oldCatId) { if (!(dt instanceof Array) && !(dt instanceof Pointer)) { try { - for (long arrayId : arrayAdapter.getRecordIdsInCategory(oldCatId)) { - Record rec = arrayAdapter.getRecord(arrayId); - ArrayDB array = (ArrayDB) getDataType(arrayId, rec); + for (Field arrayId : arrayAdapter.getRecordIdsInCategory(oldCatId)) { + long id = arrayId.getLongValue(); + Record rec = arrayAdapter.getRecord(id); + ArrayDB array = (ArrayDB) getDataType(id, rec); array.updatePath(dt); } - for (long ptrId : pointerAdapter.getRecordIdsInCategory(oldCatId)) { - Record rec = pointerAdapter.getRecord(ptrId); - PointerDB ptr = (PointerDB) getDataType(ptrId, rec); + for (Field ptrId : pointerAdapter.getRecordIdsInCategory(oldCatId)) { + long id = ptrId.getLongValue(); + Record rec = pointerAdapter.getRecord(id); + PointerDB ptr = (PointerDB) getDataType(id, rec); ptr.updatePath(dt); } } @@ -3147,9 +3149,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { lock.acquire(); try { settingsCache.clear(); - long[] keys = instanceSettingsAdapter.getInstanceKeys(addrMap.getKey(dataAddr, false)); - for (long key : keys) { - instanceSettingsAdapter.removeInstanceRecord(key); + Field[] keys = instanceSettingsAdapter.getInstanceKeys(addrMap.getKey(dataAddr, false)); + for (Field key : keys) { + instanceSettingsAdapter.removeInstanceRecord(key.getLongValue()); } } catch (IOException e) { @@ -3282,10 +3284,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { } lock.acquire(); try { - long[] keys = instanceSettingsAdapter.getInstanceKeys(addrMap.getKey(dataAddr, false)); + Field[] keys = instanceSettingsAdapter.getInstanceKeys(addrMap.getKey(dataAddr, false)); ArrayList list = new ArrayList<>(); - for (long key : keys) { - Record rec = instanceSettingsAdapter.getInstanceRecord(key); + for (Field key : keys) { + Record rec = instanceSettingsAdapter.getInstanceRecord(key.getLongValue()); list.add(rec.getString(InstanceSettingsDBAdapter.INST_NAME_COL)); } String[] names = new String[list.size()]; @@ -3392,9 +3394,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { private Record getInstanceRecord(long addr, String name) { try { - long[] keys = instanceSettingsAdapter.getInstanceKeys(addr); - for (long key : keys) { - Record rec = instanceSettingsAdapter.getInstanceRecord(key); + Field[] keys = instanceSettingsAdapter.getInstanceKeys(addr); + for (Field key : keys) { + Record rec = instanceSettingsAdapter.getInstanceRecord(key.getLongValue()); if (rec.getString(InstanceSettingsDBAdapter.INST_NAME_COL).equals(name)) { return rec; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index 6fc460b017..29c9c6550b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.math.BigInteger; import java.util.*; +import db.Field; import db.Record; import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; @@ -84,10 +85,10 @@ class EnumDB extends DataTypeDB implements Enum { nameMap = new HashMap<>(); valueMap = new HashMap<>(); - long[] ids = valueAdapter.getValueIdsInEnum(key); + Field[] ids = valueAdapter.getValueIdsInEnum(key); - for (long id : ids) { - Record rec = valueAdapter.getRecord(id); + for (Field id : ids) { + Record rec = valueAdapter.getRecord(id.getLongValue()); String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL); long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL); addToCache(valueName, value); @@ -179,7 +180,9 @@ class EnumDB extends DataTypeDB implements Enum { try { checkIsValid(); initializeIfNeeded(); - return nameMap.keySet().toArray(new String[nameMap.size()]); + String[] names = nameMap.keySet().toArray(new String[nameMap.size()]); + Arrays.sort(names); + return names; } finally { lock.release(); @@ -250,12 +253,12 @@ class EnumDB extends DataTypeDB implements Enum { } bitGroups = null; - long[] ids = valueAdapter.getValueIdsInEnum(key); + Field[] ids = valueAdapter.getValueIdsInEnum(key); - for (long id : ids) { - Record rec = valueAdapter.getRecord(id); + for (Field id : ids) { + Record rec = valueAdapter.getRecord(id.getLongValue()); if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) { - valueAdapter.removeRecord(id); + valueAdapter.removeRecord(id.getLongValue()); break; } } @@ -284,9 +287,9 @@ class EnumDB extends DataTypeDB implements Enum { nameMap = new HashMap<>(); valueMap = new HashMap<>(); - long[] ids = valueAdapter.getValueIdsInEnum(key); - for (long id : ids) { - valueAdapter.removeRecord(id); + Field[] ids = valueAdapter.getValueIdsInEnum(key); + for (Field id : ids) { + valueAdapter.removeRecord(id.getLongValue()); } int oldLength = getLength(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java index e38ccbc187..b01d062d6c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the Enumeration data types tables. @@ -188,7 +186,7 @@ abstract class EnumDBAdapter { * @return an array of IDs for the enumeration data types in the category. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; /** * Gets an array with the IDs of all data types in the enumeration table that were derived @@ -197,8 +195,15 @@ abstract class EnumDBAdapter { * @return the array data type IDs. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + abstract Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + /** + * Get enum record whoose sourceID and datatypeID match the specified Universal IDs. + * @param sourceID universal source archive ID + * @param datatypeID universal datatype ID + * @return enum record found or null + * @throws IOException if IO error occurs + */ abstract Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterNoTable.java index ac5a4b0f70..3a69800511 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterNoTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; - import java.io.IOException; import db.*; +import ghidra.util.UniversalID; /** * Adapter needed for a read-only version of data type manager that is not going @@ -67,13 +65,13 @@ class EnumDBAdapterNoTable extends EnumDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { - return new long[0]; + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return Field.EMPTY_ARRAY; } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java index c8b461ccb4..af396ba490 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.data; +import java.io.IOException; + +import db.*; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; -import java.io.IOException; - -import db.*; - /** * Version 0 implementation for accessing the Enumeration database table. */ @@ -38,9 +36,10 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { static final int V0_ENUM_CAT_COL = 2; static final int V0_ENUM_SIZE_COL = 3; - static final Schema V0_ENUM_SCHEMA = new Schema(VERSION, "Enum ID", new Class[] { - StringField.class, StringField.class, LongField.class, ByteField.class }, new String[] { - "Name", "Comment", "Category ID", "Size" }); + static final Schema V0_ENUM_SCHEMA = new Schema( + VERSION, "Enum ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE, + LongField.INSTANCE, ByteField.INSTANCE }, + new String[] { "Name", "Comment", "Category ID", "Size" }); private Table enumTable; @@ -58,9 +57,8 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { } int version = enumTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + " but got " + - enumTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + + " but got " + enumTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -102,18 +100,16 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return enumTable.findRecords(new LongField(categoryID), V0_ENUM_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; @@ -135,16 +131,4 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator { return null; } -// private void testVersion(Table table, int expectedVersion, String name) -// throws DatabaseVersionException { -// -// if (table == null) { -// throw new DatabaseVersionException(name+ " not found"); -// } -// int versionNumber = table.getSchema().getVersion(); -// if (versionNumber != expectedVersion) { -// throw new DatabaseVersionException( -// name+": Expected Version "+expectedVersion+ ", got " + versionNumber); -// } -// } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java index 2ac353b88a..898af88a58 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.Date; import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Enumeration database table. @@ -40,11 +38,12 @@ class EnumDBAdapterV1 extends EnumDBAdapter { static final int V1_ENUM_SOURCE_SYNC_TIME_COL = 6; static final int V1_ENUM_LAST_CHANGE_TIME_COL = 7; - static final Schema V1_ENUM_SCHEMA = new Schema(VERSION, "Enum ID", new Class[] { - StringField.class, StringField.class, LongField.class, ByteField.class, LongField.class, - LongField.class, LongField.class, LongField.class }, new String[] { "Name", "Comment", - "Category ID", "Size", "Source Archive ID", "Source Data Type ID", "Source Sync Time", - "Last Change Time" }); + static final Schema V1_ENUM_SCHEMA = new Schema(VERSION, "Enum ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE }, + new String[] { "Name", "Comment", "Category ID", "Size", "Source Archive ID", + "Source Data Type ID", "Source Sync Time", "Last Change Time" }); private Table enumTable; @@ -58,9 +57,8 @@ class EnumDBAdapterV1 extends EnumDBAdapter { public EnumDBAdapterV1(DBHandle handle, boolean create) throws VersionException, IOException { if (create) { - enumTable = - handle.createTable(ENUM_TABLE_NAME, V1_ENUM_SCHEMA, new int[] { V1_ENUM_CAT_COL, - V1_ENUM_UNIVERSAL_DT_ID_COL }); + enumTable = handle.createTable(ENUM_TABLE_NAME, V1_ENUM_SCHEMA, + new int[] { V1_ENUM_CAT_COL, V1_ENUM_UNIVERSAL_DT_ID_COL }); } else { enumTable = handle.getTable(ENUM_TABLE_NAME); @@ -69,9 +67,8 @@ class EnumDBAdapterV1 extends EnumDBAdapter { } int version = enumTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + " but got " + - enumTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + ENUM_TABLE_NAME + + " but got " + enumTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -128,19 +125,19 @@ class EnumDBAdapterV1 extends EnumDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return enumTable.findRecords(new LongField(categoryID), V1_ENUM_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return enumTable.findRecords(new LongField(archiveID), V1_ENUM_SOURCE_ARCHIVE_ID_COL); } @Override Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - long[] keys = - enumTable.findRecords(new LongField(datatypeID.getValue()), V1_ENUM_UNIVERSAL_DT_ID_COL); + Field[] keys = enumTable.findRecords(new LongField(datatypeID.getValue()), + V1_ENUM_UNIVERSAL_DT_ID_COL); for (int i = 0; i < keys.length; i++) { Record record = enumTable.getRecord(keys[i]); @@ -151,16 +148,4 @@ class EnumDBAdapterV1 extends EnumDBAdapter { return null; } -// private void testVersion(Table table, int expectedVersion, String name) -// throws DatabaseVersionException { -// -// if (table == null) { -// throw new DatabaseVersionException(name+ " not found"); -// } -// int versionNumber = table.getSchema().getVersion(); -// if (versionNumber != expectedVersion) { -// throw new DatabaseVersionException( -// name+": Expected Version "+expectedVersion+ ", got " + versionNumber); -// } -// } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java index b00c7085f9..092e62bcf8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +21,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the Enumeration data type values tables. @@ -63,7 +61,7 @@ abstract class EnumValueDBAdapter { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { throw e; } - EnumValueDBAdapter adapter = findReadOnlyAdapter(handle); + EnumValueDBAdapter adapter = new EnumValueDBAdapterNoTable(handle); if (openMode == DBConstants.UPGRADE) { adapter = upgrade(handle, adapter); } @@ -71,16 +69,6 @@ abstract class EnumValueDBAdapter { } } - /** - * Tries to get a read only adapter for the database whose handle is passed to this method. - * @param handle handle to prior version of the database. - * @return the read only Enumeration Data Type Values table adapter - * @throws VersionException if a read only adapter can't be obtained for the database handle's version. - */ - static EnumValueDBAdapter findReadOnlyAdapter(DBHandle handle) { - return new EnumValueDBAdapterNoTable(handle); - } - /** * Upgrades the Enumeration Data Type Values table from the oldAdapter's version to the current version. * @param handle handle to the database whose table is to be upgraded to a newer version. @@ -95,8 +83,21 @@ abstract class EnumValueDBAdapter { return new EnumValueDBAdapterV0(handle, true); } + /** + * Create new enum value record corresponding to specified enum datatype ID + * @param enumID enum datatype ID + * @param name value name + * @param value numeric value + * @throws IOException if IO error occurs + */ abstract void createRecord(long enumID, String name, long value) throws IOException; + /** + * Get enum value record which corresponds to specified value record ID + * @param valueID value record ID + * @return value record or null + * @throws IOException if IO error occurs + */ abstract Record getRecord(long valueID) throws IOException; /** @@ -113,6 +114,12 @@ abstract class EnumValueDBAdapter { */ abstract void updateRecord(Record record) throws IOException; - abstract long[] getValueIdsInEnum(long enumID) throws IOException; + /** + * Get enum value record IDs which correspond to specified enum datatype ID + * @param enumID enum datatype ID + * @return enum value record IDs as LongField values within Field array + * @throws IOException if IO error occurs + */ + abstract Field[] getValueIdsInEnum(long enumID) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java index 1ee142cb6b..e4cb5851b4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,9 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; -import db.DBHandle; -import db.Record; +import db.*; /** * Adapter needed for a read-only version of data type manager that is not going @@ -32,14 +28,14 @@ class EnumValueDBAdapterNoTable extends EnumValueDBAdapter { /** * Gets a pre-table version of the adapter for the enumeration data type values database table. * @param handle handle to the database which doesn't contain the table. - * @throws VersionException if the the table's version does not match the expected version - * for this adapter. */ public EnumValueDBAdapterNoTable(DBHandle handle) { + // no table needed } @Override public void createRecord(long enumID, String name, long value) throws IOException { + throw new UnsupportedOperationException(); } @Override @@ -54,11 +50,12 @@ class EnumValueDBAdapterNoTable extends EnumValueDBAdapter { @Override public void removeRecord(long valueID) throws IOException { + throw new UnsupportedOperationException(); } @Override - public long[] getValueIdsInEnum(long enumID) throws IOException { - return new long[0]; + public Field[] getValueIdsInEnum(long enumID) throws IOException { + return Field.EMPTY_ARRAY; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java index 6c6d73681f..a55f899e7c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for the enumeration tables adapter. @@ -34,9 +32,9 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { static final int V0_ENUMVAL_VALUE_COL = 1; static final int V0_ENUMVAL_ID_COL = 2; - static final Schema V0_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID", new Class[] { - StringField.class, LongField.class, LongField.class }, new String[] { "Name", "Value", - "Enum ID" }); + static final Schema V0_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID", + new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Name", "Value", "Enum ID" }); private Table valueTable; @@ -46,14 +44,14 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. + * @throws IOException if IO error occurs */ - public EnumValueDBAdapterV0(DBHandle handle, boolean create) throws VersionException, - IOException { + public EnumValueDBAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - valueTable = - handle.createTable(ENUM_VALUE_TABLE_NAME, V0_ENUM_VALUE_SCHEMA, - new int[] { V0_ENUMVAL_ID_COL }); + valueTable = handle.createTable(ENUM_VALUE_TABLE_NAME, V0_ENUM_VALUE_SCHEMA, + new int[] { V0_ENUMVAL_ID_COL }); } else { valueTable = handle.getTable(ENUM_VALUE_TABLE_NAME); @@ -62,9 +60,8 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { } int version = valueTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + - " but got " + valueTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + + " but got " + valueTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -98,20 +95,7 @@ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { } @Override - public long[] getValueIdsInEnum(long enumID) throws IOException { + public Field[] getValueIdsInEnum(long enumID) throws IOException { return valueTable.findRecords(new LongField(enumID), V0_ENUMVAL_ID_COL); } - -// private void testVersion(Table table, int expectedVersion, String name) -// throws DatabaseVersionException { -// -// if (table == null) { -// throw new DatabaseVersionException(name+ " not found"); -// } -// int versionNumber = table.getSchema().getVersion(); -// if (versionNumber != expectedVersion) { -// throw new DatabaseVersionException( -// name+": Expected Version "+expectedVersion+ ", got " + versionNumber); -// } -// } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java index cd471f5520..c8f772d5aa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import java.util.*; +import db.Field; import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.database.DBObjectCache; @@ -57,9 +58,9 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition { private void loadParameters() { parameters = new ArrayList<>(); try { - long[] ids = paramAdapter.getParameterIdsInFunctionDef(key); - for (long id : ids) { - Record rec = paramAdapter.getRecord(id); + Field[] ids = paramAdapter.getParameterIdsInFunctionDef(key); + for (Field id : ids) { + Record rec = paramAdapter.getRecord(id.getLongValue()); parameters.add(new ParameterDefinitionDB(dataMgr, paramAdapter, this, rec)); } Collections.sort(parameters); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java index 43115dd3aa..ad96a58264 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapter.java @@ -65,8 +65,8 @@ abstract class FunctionDefinitionDBAdapter { * @throws VersionException if the database handle's version doesn't match the expected version. * @throws IOException if there is trouble accessing the database. */ - static FunctionDefinitionDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) - throws VersionException, IOException { + static FunctionDefinitionDBAdapter getAdapter(DBHandle handle, int openMode, + TaskMonitor monitor) throws VersionException, IOException { if (openMode == DBConstants.CREATE) { return new FunctionDefinitionDBAdapterV1(handle, true); } @@ -91,7 +91,8 @@ abstract class FunctionDefinitionDBAdapter { * @return the read only Function Definition data type table adapter * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ - static FunctionDefinitionDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + static FunctionDefinitionDBAdapter findReadOnlyAdapter(DBHandle handle) + throws VersionException { try { return new FunctionDefinitionDBAdapterV0(handle); } @@ -205,7 +206,7 @@ abstract class FunctionDefinitionDBAdapter { * @return an array of IDs for the function definition data types in the category. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; /** * Gets an array with the IDs of all data types in the function definition table that were derived @@ -214,8 +215,15 @@ abstract class FunctionDefinitionDBAdapter { * @return the array data type IDs. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + abstract Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + /** + * Get function definition record whoose sourceID and datatypeID match the specified Universal IDs. + * @param sourceID universal source archive ID + * @param datatypeID universal datatype ID + * @return function definition record found or null + * @throws IOException if IO error occurs + */ abstract Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java index e147877e3b..b0ff7cdd8f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterNoTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.program.database.util.EmptyRecordIterator; -import ghidra.program.model.data.GenericCallingConvention; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.util.EmptyRecordIterator; +import ghidra.program.model.data.GenericCallingConvention; +import ghidra.util.UniversalID; /** * Adapter needed for a read-only version of data type manager that is not going @@ -34,10 +31,9 @@ class FunctionDefinitionDBAdapterNoTable extends FunctionDefinitionDBAdapter { /** * Gets a pre-table version of the adapter for the Function Definition database table. * @param handle handle to the database which doesn't contain the table. - * @throws VersionException if the the table's version does not match the expected version - * for this adapter. */ public FunctionDefinitionDBAdapterNoTable(DBHandle handle) { + // no table required } @Override @@ -70,16 +66,17 @@ class FunctionDefinitionDBAdapterNoTable extends FunctionDefinitionDBAdapter { @Override protected void deleteTable(DBHandle handle) { + // do nothing } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { - return new long[0]; + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return Field.EMPTY_ARRAY; } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java index 8901a03c00..ad90da5798 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +15,19 @@ */ package ghidra.program.database.data; +import java.io.IOException; + +import db.*; import ghidra.program.model.data.*; import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; -import java.io.IOException; - -import db.*; - /** * Version 0 implementation for accessing the Function Signature Definition database table. */ -class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter implements RecordTranslator { +class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter + implements RecordTranslator { static final int VERSION = 0; static final int V0_FUNCTION_DEF_NAME_COL = 0; static final int V0_FUNCTION_DEF_COMMENT_COL = 1; @@ -60,9 +59,8 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter implemen } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + FUNCTION_DEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + FUNCTION_DEF_TABLE_NAME + + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -104,18 +102,16 @@ class FunctionDefinitionDBAdapterV0 extends FunctionDefinitionDBAdapter implemen } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_FUNCTION_DEF_CAT_ID_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java index de3890d27e..5a62ffcd4c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.data; -import ghidra.program.model.data.GenericCallingConvention; -import ghidra.util.Msg; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.Date; import db.*; +import ghidra.program.model.data.GenericCallingConvention; +import ghidra.util.Msg; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Function Signature Definition database table. @@ -40,11 +38,12 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { static final int V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL = 6; static final int V1_FUNCTION_DEF_SOURCE_SYNC_TIME_COL = 7; static final int V1_FUNCTION_DEF_LAST_CHANGE_TIME_COL = 8; - static final Schema V1_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", new Class[] { - StringField.class, StringField.class, LongField.class, LongField.class, ByteField.class, - LongField.class, LongField.class, LongField.class, LongField.class }, new String[] { - "Name", "Comment", "Category ID", "Return Type ID", "Flags", "Source Archive ID", - "Source Data Type ID", "Source Sync Time", "Last Change Time" }); + static final Schema V1_FUN_DEF_SCHEMA = new Schema(VERSION, "Data Type ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Name", "Comment", "Category ID", "Return Type ID", "Flags", + "Source Archive ID", "Source Data Type ID", "Source Sync Time", "Last Change Time" }); private Table table; @@ -55,13 +54,12 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public FunctionDefinitionDBAdapterV1(DBHandle handle, boolean create) throws VersionException, - IOException { + public FunctionDefinitionDBAdapterV1(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - table = - handle.createTable(FUNCTION_DEF_TABLE_NAME, V1_FUN_DEF_SCHEMA, new int[] { - V1_FUNCTION_DEF_CAT_ID_COL, V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL }); + table = handle.createTable(FUNCTION_DEF_TABLE_NAME, V1_FUN_DEF_SCHEMA, + new int[] { V1_FUNCTION_DEF_CAT_ID_COL, V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL }); } else { table = handle.getTable(FUNCTION_DEF_TABLE_NAME); @@ -70,9 +68,8 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + FUNCTION_DEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + + FUNCTION_DEF_TABLE_NAME + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -150,20 +147,19 @@ class FunctionDefinitionDBAdapterV1 extends FunctionDefinitionDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V1_FUNCTION_DEF_CAT_ID_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return table.findRecords(new LongField(archiveID), V1_FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL); } @Override Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - long[] keys = - table.findRecords(new LongField(datatypeID.getValue()), - V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL); + Field[] keys = table.findRecords(new LongField(datatypeID.getValue()), + V1_FUNCTION_DEF_UNIVERSAL_DT_ID_COL); for (int i = 0; i < keys.length; i++) { Record record = table.getRecord(keys[i]); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java index a2a66f94a1..8b0a94074b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the Function Signature Definition Parameters database table. @@ -134,31 +132,31 @@ abstract class FunctionParameterAdapter { abstract protected RecordIterator getRecords() throws IOException; /** - * - * @param handle - * @throws IOException + * Delete underlying database table + * @param handle database handle + * @throws IOException if IO error occurs */ abstract protected void deleteTable(DBHandle handle) throws IOException; /** - * - * @param dataTypeID - * @param parentID - * @param ordinal - * @param name - * @param comment - * @param dtLength - * @return - * @throws IOException + * Create new parameter definition record + * @param dataTypeID parameter datatype ID + * @param parentID parent function definition ID + * @param ordinal parameter ordinal + * @param name parameter name + * @param comment parameter comment + * @param dtLength datatype length if required, else -1 + * @return new record + * @throws IOException if IO error occurs */ abstract Record createRecord(long dataTypeID, long parentID, int ordinal, String name, String comment, int dtLength) throws IOException; /** - * - * @param parameterID - * @return - * @throws IOException + * Get parameter definition record + * @param parameterID parameter record ID + * @return parameter definition record or null + * @throws IOException if IO error occurs */ abstract Record getRecord(long parameterID) throws IOException; @@ -178,11 +176,11 @@ abstract class FunctionParameterAdapter { abstract boolean removeRecord(long parameterID) throws IOException; /** - * - * @param functionDefID - * @return - * @throws IOException + * Get parameter definition IDs for specified function definition + * @param functionDefID function definition ID + * @return parameter definition IDs as LongField values within Field array + * @throws IOException if IO error occurs */ - abstract long[] getParameterIdsInFunctionDef(long functionDefID) throws IOException; + abstract Field[] getParameterIdsInFunctionDef(long functionDefID) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterNoTable.java index 8f6140d50e..9a3dc73792 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterNoTable.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.program.database.util.EmptyRecordIterator; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.util.EmptyRecordIterator; +import ghidra.util.exception.VersionException; /** * Adapter needed for a read-only version of data type manager that is not going @@ -36,6 +34,7 @@ class FunctionParameterAdapterNoTable extends FunctionParameterAdapter { * for this adapter. */ public FunctionParameterAdapterNoTable(DBHandle handle) { + // no table required } @Override @@ -66,11 +65,12 @@ class FunctionParameterAdapterNoTable extends FunctionParameterAdapter { @Override protected void deleteTable(DBHandle handle) throws IOException { + // do nothing } @Override - public long[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { - return new long[0]; + public Field[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { + return Field.EMPTY_ARRAY; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java index 135f0460eb..450da6f5d2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for accessing the Function Definition Parameters database table. @@ -35,8 +33,9 @@ class FunctionParameterAdapterV0 extends FunctionParameterAdapter implements Rec static final int V0_PARAMETER_COMMENT_COL = 3; static final int V0_PARAMETER_ORDINAL_COL = 4; - static final Schema V0_PARAMETER_SCHEMA = new Schema(VERSION, "Parameter ID", new Class[] { - LongField.class, LongField.class, StringField.class, StringField.class, IntField.class }, + static final Schema V0_PARAMETER_SCHEMA = new Schema(VERSION, "Parameter ID", + new Field[] { LongField.INSTANCE, LongField.INSTANCE, StringField.INSTANCE, + StringField.INSTANCE, IntField.INSTANCE }, new String[] { "Parent ID", "Data Type ID", "Name", "Comment", "Ordinal" }); private Table parameterTable; @@ -55,9 +54,8 @@ class FunctionParameterAdapterV0 extends FunctionParameterAdapter implements Rec } int version = parameterTable.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + " but got " + - parameterTable.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + + " but got " + parameterTable.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -110,13 +108,14 @@ class FunctionParameterAdapterV0 extends FunctionParameterAdapter implements Rec } @Override - public long[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { + public Field[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { return parameterTable.findRecords(new LongField(functionDefID), V0_PARAMETER_PARENT_ID_COL); } /* (non-Javadoc) * @see db.RecordTranslator#translateRecord(db.Record) */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java index 5efe96ac88..cc94200881 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionParameterAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Function Definition Parameters database table. @@ -36,10 +34,11 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { static final int V1_PARAMETER_ORDINAL_COL = 4; static final int V1_PARAMETER_DT_LENGTH_COL = 5; - static final Schema V1_PARAMETER_SCHEMA = new Schema(VERSION, "Parameter ID", new Class[] { - LongField.class, LongField.class, StringField.class, StringField.class, IntField.class, - IntField.class }, new String[] { "Parent ID", "Data Type ID", "Name", "Comment", "Ordinal", - "Data Type Length" }); + static final Schema V1_PARAMETER_SCHEMA = new Schema(VERSION, "Parameter ID", + new Field[] { LongField.INSTANCE, LongField.INSTANCE, StringField.INSTANCE, + StringField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE }, + new String[] { "Parent ID", "Data Type ID", "Name", "Comment", "Ordinal", + "Data Type Length" }); private Table table; @@ -50,13 +49,12 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public FunctionParameterAdapterV1(DBHandle handle, boolean create) throws VersionException, - IOException { + public FunctionParameterAdapterV1(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - table = - handle.createTable(PARAMETER_TABLE_NAME, V1_PARAMETER_SCHEMA, - new int[] { V1_PARAMETER_PARENT_ID_COL }); + table = handle.createTable(PARAMETER_TABLE_NAME, V1_PARAMETER_SCHEMA, + new int[] { V1_PARAMETER_PARENT_ID_COL }); } else { table = handle.getTable(PARAMETER_TABLE_NAME); @@ -65,9 +63,8 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + - " but got " + table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + PARAMETER_TABLE_NAME + + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -122,7 +119,7 @@ class FunctionParameterAdapterV1 extends FunctionParameterAdapter { } @Override - public long[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { + public Field[] getParameterIdsInFunctionDef(long functionDefID) throws IOException { return table.findRecords(new LongField(functionDefID), V1_PARAMETER_PARENT_ID_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapter.java index 3c63d8b34f..faef8a3d75 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,7 +140,7 @@ abstract class InstanceSettingsDBAdapter { * Get keys for the instance settings applied at the given address. * @throws IOException if there was a problem accessing the database */ - abstract long[] getInstanceKeys(long addr) throws IOException; + abstract Field[] getInstanceKeys(long addr) throws IOException; /** * Remove the instance record. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapterV0.java index 78e8706ecd..2b33e38150 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/InstanceSettingsDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * @@ -37,9 +35,9 @@ class InstanceSettingsDBAdapterV0 extends InstanceSettingsDBAdapter { static final int V0_INST_BYTE_VALUE_COL = 4; static final Schema V0_INSTANCE_SCHEMA = new Schema(0, "Settings ID", - new Class[] { LongField.class, StringField.class, LongField.class, StringField.class, - BinaryField.class }, new String[] { "Address", "Settings Name", "Long Value", - "String Value", "Byte Value" }); + new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, BinaryField.INSTANCE }, + new String[] { "Address", "Settings Name", "Long Value", "String Value", "Byte Value" }); private Table instanceTable; @@ -47,13 +45,12 @@ class InstanceSettingsDBAdapterV0 extends InstanceSettingsDBAdapter { * Constructor * */ - InstanceSettingsDBAdapterV0(DBHandle handle, boolean create) throws VersionException, - IOException { + InstanceSettingsDBAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - instanceTable = - handle.createTable(INSTANCE_TABLE_NAME, V0_INSTANCE_SCHEMA, - new int[] { V0_INST_ADDR_COL }); + instanceTable = handle.createTable(INSTANCE_TABLE_NAME, V0_INSTANCE_SCHEMA, + new int[] { V0_INST_ADDR_COL }); } else { instanceTable = handle.getTable(INSTANCE_TABLE_NAME); @@ -67,9 +64,6 @@ class InstanceSettingsDBAdapterV0 extends InstanceSettingsDBAdapter { } } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#createInstanceRecord(long, java.lang.String, java.lang.String, long, byte[]) - */ @Override public Record createInstanceRecord(long addr, String name, String strValue, long longValue, byte[] byteValue) throws IOException { @@ -84,72 +78,47 @@ class InstanceSettingsDBAdapterV0 extends InstanceSettingsDBAdapter { return record; } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#getInstanceKeys(long) - */ @Override - public long[] getInstanceKeys(long addr) throws IOException { + public Field[] getInstanceKeys(long addr) throws IOException { return instanceTable.findRecords(new LongField(addr), V0_INST_ADDR_COL); } - /** - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#removeInstanceRecords(long) - */ @Override public boolean removeInstanceRecord(long settingsID) throws IOException { return instanceTable.deleteRecord(settingsID); } - /** - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#getInstanceRecord(long) - */ @Override public Record getInstanceRecord(long settingsID) throws IOException { return instanceTable.getRecord(settingsID); } - /** - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#updateInstanceRecord(ghidra.framework.store.db.Record) - */ @Override public void updateInstanceRecord(Record record) throws IOException { instanceTable.putRecord(record); } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#getRecords(long, long) - */ @Override public RecordIterator getRecords(long start, long end) throws IOException { - return instanceTable.indexIterator(V0_INST_ADDR_COL, new LongField(start), new LongField( - end), true); + return instanceTable.indexIterator(V0_INST_ADDR_COL, new LongField(start), + new LongField(end), true); } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return instanceTable.iterator(); } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return instanceTable.getRecordCount(); } - /* - * @see ghidra.program.database.data.InstanceSettingsDBAdapter#delete(long, long, ghidra.util.task.TaskMonitor) - */ @Override void delete(long start, long end, TaskMonitor monitor) throws CancelledException, IOException { - DBLongIterator it = - instanceTable.indexKeyIterator(V0_INST_ADDR_COL, new LongField(start), new LongField( - end), true); + DBFieldIterator it = instanceTable.indexKeyIterator(V0_INST_ADDR_COL, new LongField(start), + new LongField(end), true); while (it.hasNext()) { if (monitor.isCancelled()) { throw new CancelledException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java index ed429f615b..4e60ee23c8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildAdapter.java @@ -67,32 +67,12 @@ abstract class ParentChildAdapter { abstract boolean needsInitializing(); - /** - * Get the version number for this adapter - */ - abstract int getVersion(); - - /** - * @param parentID - * @param childID - */ abstract void createRecord(long parentID, long childID) throws IOException; - /** - * @param parentID - * @param childID - */ abstract void removeRecord(long parentID, long childID) throws IOException; - /** - * @param childID - * @return - */ abstract long[] getParentIds(long childID) throws IOException; - /** - * @param dataTypeID - */ abstract void removeAllRecordsForParent(long parentID) throws IOException; abstract void removeAllRecordsForChild(long childID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java index 28c7ec830d..e3ce350572 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterNoTable.java @@ -28,50 +28,30 @@ class ParentChildDBAdapterNoTable extends ParentChildAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public ParentChildDBAdapterNoTable(DBHandle handle) { + ParentChildDBAdapterNoTable(DBHandle handle) { + // no table required } - /** - * @see ghidra.program.database.data.ParentChildAdapter#getVersion() - */ - @Override - public int getVersion() { - return -1; - } - - /** - * @see ghidra.program.database.data.ParentChildAdapter#createRecord(long, long) - */ @Override void createRecord(long parentID, long childID) throws IOException { + throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.data.ParentChildAdapter#removeRecord(long, long) - */ @Override void removeRecord(long parentID, long childID) throws IOException { + throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.data.ParentChildAdapter#getParentIds(long) - */ @Override long[] getParentIds(long childID) throws IOException { return new long[0]; } - /** - * @see ghidra.program.database.data.ParentChildAdapter#needsInitializing() - */ @Override boolean needsInitializing() { return false; } - /** - * @see ghidra.program.database.data.ParentChildAdapter#removeAllRecordsForParent(long) - */ @Override void removeAllRecordsForParent(long parentID) throws IOException { // stub diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java index 630387d911..d2caa9d7ef 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParentChildDBAdapterV0.java @@ -26,18 +26,14 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { private static final int PARENT_COL = 0; private static final int CHILD_COL = 1; - static final Schema V0_SCHEMA = new Schema(0, "KEY", - new Class[] { LongField.class, LongField.class }, new String[] { "Parent ID", "Child ID" }); + static final Schema V0_SCHEMA = + new Schema(0, "KEY", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Parent ID", "Child ID" }); private Table table; private boolean needsInitializing = false; - /** - * @param handle - * @param b - */ - public ParentChildDBAdapterV0(DBHandle handle, boolean create) - throws VersionException, IOException { + ParentChildDBAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException { if (create) { table = handle.createTable(TABLE_NAME, V0_SCHEMA, new int[] { PARENT_COL, CHILD_COL }); @@ -54,17 +50,6 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { } } - /** - * @see ghidra.program.database.data.ParentChildAdapter#getVersion() - */ - @Override - public int getVersion() { - return VERSION; - } - - /* (non-Javadoc) - * @see ghidra.program.database.data.EnumDBAdapter#createEnumRecord(java.lang.String, java.lang.String, long, byte) - */ @Override public void createRecord(long parentID, long childID) throws IOException { long key = table.getKey(); @@ -74,14 +59,11 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { table.putRecord(record); } - /** - * @see ghidra.program.database.data.ParentChildAdapter#removeRecord(long, long) - */ @Override void removeRecord(long parentID, long childID) throws IOException { - long[] ids = table.findRecords(new LongField(childID), CHILD_COL); - for (long id : ids) { + Field[] ids = table.findRecords(new LongField(childID), CHILD_COL); + for (Field id : ids) { Record rec = table.getRecord(id); if (rec.getLongValue(PARENT_COL) == parentID) { table.deleteRecord(id); @@ -90,12 +72,9 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { } } - /** - * @see ghidra.program.database.data.ParentChildAdapter#getParentIds(long) - */ @Override long[] getParentIds(long childID) throws IOException { - long[] ids = table.findRecords(new LongField(childID), CHILD_COL); + Field[] ids = table.findRecords(new LongField(childID), CHILD_COL); long[] parentIds = new long[ids.length]; for (int i = 0; i < ids.length; i++) { Record rec = table.getRecord(ids[i]); @@ -108,29 +87,23 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter { needsInitializing = true; } - /** - * @see ghidra.program.database.data.ParentChildAdapter#needsInitializing() - */ @Override boolean needsInitializing() { return needsInitializing; } - /** - * @see ghidra.program.database.data.ParentChildAdapter#removeAllRecordsForParent(long) - */ @Override void removeAllRecordsForParent(long parentID) throws IOException { - long[] ids = table.findRecords(new LongField(parentID), PARENT_COL); - for (long id : ids) { + Field[] ids = table.findRecords(new LongField(parentID), PARENT_COL); + for (Field id : ids) { table.deleteRecord(id); } } @Override void removeAllRecordsForChild(long childID) throws IOException { - long[] ids = table.findRecords(new LongField(childID), CHILD_COL); - for (long id : ids) { + Field[] ids = table.findRecords(new LongField(childID), CHILD_COL); + for (Field id : ids) { table.deleteRecord(id); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java index 01f4724266..804e89d7f9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the Pointer database table for Pointer data types. @@ -31,9 +29,9 @@ import db.*; abstract class PointerDBAdapter { static final String POINTER_TABLE_NAME = "Pointers"; - static final Schema SCHEMA = new Schema(PointerDBAdapterV2.VERSION, "Pointer ID", new Class[] { - LongField.class, LongField.class, ByteField.class }, new String[] { "Data Type ID", - "Category ID", "Length" }); + static final Schema SCHEMA = new Schema(PointerDBAdapterV2.VERSION, "Pointer ID", + new Field[] { LongField.INSTANCE, LongField.INSTANCE, ByteField.INSTANCE }, + new String[] { "Data Type ID", "Category ID", "Length" }); static final int PTR_DT_ID_COL = 0; static final int PTR_CATEGORY_COL = 1; @@ -114,6 +112,7 @@ abstract class PointerDBAdapter { /** * Get the record with the given pointerID. * @param pointerID database key + * @return requested pointer record or null if not found * @throws IOException if there was a problem accessing the database */ abstract Record getRecord(long pointerID) throws IOException; @@ -130,11 +129,19 @@ abstract class PointerDBAdapter { /** * Update the record in the table. + * @param record pointer record to be updated * @throws IOException if there was a problem accessing the database */ abstract void updateRecord(Record record) throws IOException; - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + /** + * Gets all the pointer data types that are contained in the category that + * have the indicated ID. + * @param categoryID the category whose pointer data types are wanted. + * @return an array of IDs for the pointer data types in the category. + * @throws IOException if the database can't be accessed. + */ + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; Record translateRecord(Record rec) { return rec; @@ -147,23 +154,28 @@ abstract class PointerDBAdapter { this.it = it; } + @Override public boolean delete() throws IOException { throw new UnsupportedOperationException(); } + @Override public boolean hasNext() throws IOException { return it.hasNext(); } + @Override public boolean hasPrevious() throws IOException { return it.hasPrevious(); } + @Override public Record next() throws IOException { Record rec = it.next(); return translateRecord(rec); } + @Override public Record previous() throws IOException { Record rec = it.previous(); return translateRecord(rec); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java index 157155dc75..1db1964e7c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for the accessing the pointer database table. @@ -38,10 +36,6 @@ class PointerDBAdapterV0 extends PointerDBAdapter { private Table table; - /** - * Constructor - * - */ PointerDBAdapterV0(DBHandle handle) throws VersionException { table = handle.getTable(POINTER_TABLE_NAME); @@ -54,53 +48,34 @@ class PointerDBAdapterV0 extends PointerDBAdapter { } } - /* - * (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#createRecord(long, long) - */ @Override Record createRecord(long dataTypeID, long categoryID, int length) throws IOException { throw new UnsupportedOperationException("Cannot update Version 0"); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#getRecord(long) - */ @Override Record getRecord(long pointerID) throws IOException { return translateRecord(table.getRecord(pointerID)); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return new TranslatedRecordIterator(table.iterator()); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#removeRecord(long) - */ @Override boolean removeRecord(long pointerID) throws IOException { throw new UnsupportedOperationException("Cannot update Version 0"); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ @Override void updateRecord(Record record) throws IOException { throw new UnsupportedOperationException("Cannot update Version 0"); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecordIdsInCategory(long) - */ @Override - long[] getRecordIdsInCategory(long categoryID) throws IOException { - return new long[0]; + Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return Field.EMPTY_ARRAY; } @Override @@ -114,9 +89,6 @@ class PointerDBAdapterV0 extends PointerDBAdapter { return rec; } - /** - * @see ghidra.program.database.data.PointerDBAdapter#deleteTable() - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(POINTER_TABLE_NAME); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java index dab013480e..6d390999e8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 1 adapter for the Pointer table. @@ -30,8 +28,9 @@ class PointerDBAdapterV1 extends PointerDBAdapter { static final int OLD_PTR_DT_ID_COL = 0; static final int OLD_PTR_CATEGORY_COL = 1; - static final Schema V1_SCHEMA = new Schema(VERSION, "Pointer ID", new Class[] { - LongField.class, LongField.class }, new String[] { "Data Type ID", "Category ID" }); + static final Schema V1_SCHEMA = + new Schema(VERSION, "Pointer ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Data Type ID", "Category ID" }); private Table table; @@ -66,49 +65,31 @@ class PointerDBAdapterV1 extends PointerDBAdapter { throw new UnsupportedOperationException(); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecord(long) - */ @Override Record getRecord(long pointerID) throws IOException { return translateRecord(table.getRecord(pointerID)); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return new TranslatedRecordIterator(table.iterator()); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#removeRecord(long) - */ @Override boolean removeRecord(long pointerID) throws IOException { throw new UnsupportedOperationException(); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ @Override void updateRecord(Record record) throws IOException { throw new UnsupportedOperationException(); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecordIdsInCategory(long) - */ @Override - long[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), OLD_PTR_CATEGORY_COL); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#deleteTable() - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(POINTER_TABLE_NAME); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java index eab240d3a3..923bbc7c7f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; class PointerDBAdapterV2 extends PointerDBAdapter { final static int VERSION = 2; @@ -47,9 +45,6 @@ class PointerDBAdapterV2 extends PointerDBAdapter { } } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#createRecord(long, int) - */ @Override Record createRecord(long dataTypeID, long categoryID, int length) throws IOException { long tableKey = table.getKey(); @@ -63,49 +58,31 @@ class PointerDBAdapterV2 extends PointerDBAdapter { return record; } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecord(long) - */ @Override Record getRecord(long pointerID) throws IOException { return table.getRecord(pointerID); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return table.iterator(); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#removeRecord(long) - */ @Override boolean removeRecord(long pointerID) throws IOException { return table.deleteRecord(pointerID); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ @Override void updateRecord(Record record) throws IOException { table.putRecord(record); } - /* (non-Javadoc) - * @see ghidra.program.database.data.PointerDBAdapter#getRecordIdsInCategory(long) - */ @Override - long[] getRecordIdsInCategory(long categoryID) throws IOException { + Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), PTR_CATEGORY_COL); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#deleteTable() - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(POINTER_TABLE_NAME); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapter.java index 7ab799d34c..1b48095341 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the default settings and instance settings database tables. @@ -48,6 +46,7 @@ abstract class SettingsDBAdapter { /** * Returns number of settings records + * @return total settings record count */ abstract int getRecordCount(); @@ -58,16 +57,20 @@ abstract class SettingsDBAdapter { * @param strValue string value; null if setting is not String * @param longValue long value; -1 if setting is not a long * @param byteValue byte array value; null if setting is not a byte array + * @return new record * @throws IOException if there was a problem accessing the database */ abstract Record createSettingsRecord(long dataTypeID, String name, String strValue, long longValue, byte[] byteValue) throws IOException; /** - * Get keys for the default settings for the given data type ID. + * Get settings record keys for the default settings corresponding to the + * specified data type ID. + * @param dataTypeID datatype ID + * @return settings record keys returned as LongFields within Field array * @throws IOException if there was a problem accessing the database */ - abstract long[] getSettingsKeys(long dataTypeID) throws IOException; + abstract Field[] getSettingsKeys(long dataTypeID) throws IOException; /** * Remove the default settings record. @@ -80,6 +83,7 @@ abstract class SettingsDBAdapter { /** * Get the default settings record. * @param settingsID key for the record + * @return record corresponding to settingsID or null * @throws IOException if there was a problem accessing the database */ abstract Record getSettingsRecord(long settingsID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV0.java index e6a89ed9dd..635bb9f608 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV0.java @@ -15,18 +15,13 @@ */ package ghidra.program.database.data; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** - * - * To change the template for this generated type comment go to - * {@literal Window>Preferences>Java>Code Generation>Code and Comments} - * - * + * Version 0 implementation for the accessing the data type settings database table. */ class SettingsDBAdapterV0 extends SettingsDBAdapter { @@ -38,21 +33,17 @@ class SettingsDBAdapterV0 extends SettingsDBAdapter { static final int V0_SETTINGS_BYTE_VALUE_COL = 4; static final Schema V0_SETTINGS_SCHEMA = new Schema(0, "DT Settings ID", - new Class[] { LongField.class, StringField.class, LongField.class, StringField.class, - BinaryField.class }, new String[] { "Data Type ID", "Settings Name", "Long Value", - "String Value", "Byte Value" }); + new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, BinaryField.INSTANCE }, + new String[] { "Data Type ID", "Settings Name", "Long Value", "String Value", + "Byte Value" }); private Table settingsTable; - /** - * Constructor - * - */ SettingsDBAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException { if (create) { - settingsTable = - handle.createTable(SETTINGS_TABLE_NAME, V0_SETTINGS_SCHEMA, - new int[] { V0_SETTINGS_DT_ID_COL }); + settingsTable = handle.createTable(SETTINGS_TABLE_NAME, V0_SETTINGS_SCHEMA, + new int[] { V0_SETTINGS_DT_ID_COL }); } else { settingsTable = handle.getTable(SETTINGS_TABLE_NAME); @@ -66,9 +57,6 @@ class SettingsDBAdapterV0 extends SettingsDBAdapter { } } - /** - * @see ghidra.program.database.data.SettingsDBAdapter#createSettingsRecord(long, java.lang.String, java.lang.String, long, byte[]) - */ @Override public Record createSettingsRecord(long dataTypeID, String name, String strValue, long longValue, byte[] byteValue) throws IOException { @@ -83,41 +71,26 @@ class SettingsDBAdapterV0 extends SettingsDBAdapter { return record; } - /** - * @see ghidra.program.database.data.SettingsDBAdapter#getSettingsKeys(long) - */ @Override - public long[] getSettingsKeys(long dataTypeID) throws IOException { + public Field[] getSettingsKeys(long dataTypeID) throws IOException { return settingsTable.findRecords(new LongField(dataTypeID), V0_SETTINGS_DT_ID_COL); } - /** - * @see ghidra.program.database.data.SettingsDBAdapter#removeSettingsRecord(long) - */ @Override public boolean removeSettingsRecord(long settingsID) throws IOException { return settingsTable.deleteRecord(settingsID); } - /** - * @see ghidra.program.database.data.SettingsDBAdapter#getSettingsRecord(long) - */ @Override public Record getSettingsRecord(long settingsID) throws IOException { return settingsTable.getRecord(settingsID); } - /** - * @see ghidra.program.database.data.SettingsDBAdapter#updateSettingsRecord(ghidra.framework.store.db.Record) - */ @Override public void updateSettingsRecord(Record record) throws IOException { settingsTable.putRecord(record); } - /* - * @see ghidra.program.database.data.SettingsDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return settingsTable.getRecordCount(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java index 468f611c24..d4f8bdb18f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java @@ -15,17 +15,15 @@ */ package ghidra.program.database.data; +import java.io.IOException; +import java.util.*; + +import db.Field; +import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeComponent; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import db.Record; - /** * Database implementation for settings. * @@ -73,9 +71,7 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#getLong(java.lang.String) - */ + @Override public Long getLong(String name) { SettingsDB settingsDB = getSettingsDB(name); if (settingsDB != null) { @@ -87,9 +83,7 @@ class SettingsDBManager implements Settings { return null; } - /** - * @see ghidra.docking.settings.Settings#getString(java.lang.String) - */ + @Override public String getString(String name) { SettingsDB settingsDB = getSettingsDB(name); if (settingsDB != null) { @@ -101,9 +95,7 @@ class SettingsDBManager implements Settings { return null; } - /** - * @see ghidra.docking.settings.Settings#getByteArray(java.lang.String) - */ + @Override public byte[] getByteArray(String name) { SettingsDB settingsDB = getSettingsDB(name); if (settingsDB != null) { @@ -115,9 +107,7 @@ class SettingsDBManager implements Settings { return null; } - /** - * @see ghidra.docking.settings.Settings#getValue(java.lang.String) - */ + @Override public Object getValue(String name) { SettingsDB settingsDB = getSettingsDB(name); if (settingsDB != null) { @@ -129,9 +119,7 @@ class SettingsDBManager implements Settings { return null; } - /** - * @see ghidra.docking.settings.Settings#setLong(java.lang.String, long) - */ + @Override public void setLong(String name, long value) { try { if (updateSettingsRecord(name, null, value, null)) { @@ -144,9 +132,7 @@ class SettingsDBManager implements Settings { } - /** - * @see ghidra.docking.settings.Settings#setString(java.lang.String, java.lang.String) - */ + @Override public void setString(String name, String value) { try { @@ -159,9 +145,7 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#setByteArray(java.lang.String, byte[]) - */ + @Override public void setByteArray(String name, byte[] value) { try { if (updateSettingsRecord(name, null, -1, value)) { @@ -173,9 +157,7 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#setValue(java.lang.String, java.lang.Object) - */ + @Override public void setValue(String name, Object value) { if (value instanceof Long) { setLong(name, ((Long) value).longValue()); @@ -191,18 +173,16 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#clearSetting(java.lang.String) - */ + @Override public void clearSetting(String name) { try { - long[] keys = adapter.getSettingsKeys(dataTypeID); + Field[] keys = adapter.getSettingsKeys(dataTypeID); for (int i = 0; i < keys.length; i++) { - Record rec = adapter.getSettingsRecord(keys[i]); + Record rec = adapter.getSettingsRecord(keys[i].getLongValue()); String settingsName = rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL); if (settingsName.equals(name)) { - adapter.removeSettingsRecord(keys[i]); + adapter.removeSettingsRecord(keys[i].getLongValue()); settingsChanged(); return; } @@ -213,14 +193,12 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#clearAllSettings() - */ + @Override public void clearAllSettings() { try { - long[] keys = adapter.getSettingsKeys(dataTypeID); + Field[] keys = adapter.getSettingsKeys(dataTypeID); for (int i = 0; i < keys.length; i++) { - adapter.removeSettingsRecord(keys[i]); + adapter.removeSettingsRecord(keys[i].getLongValue()); } settingsChanged(); } @@ -229,15 +207,13 @@ class SettingsDBManager implements Settings { } } - /** - * @see ghidra.docking.settings.Settings#getNames() - */ + @Override public String[] getNames() { List list = new ArrayList(); try { - long[] keys = adapter.getSettingsKeys(dataTypeID); + Field[] keys = adapter.getSettingsKeys(dataTypeID); for (int i = 0; i < keys.length; i++) { - Record rec = adapter.getSettingsRecord(keys[i]); + Record rec = adapter.getSettingsRecord(keys[i].getLongValue()); String name = rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL); if (!list.contains(name)) { list.add(name); @@ -252,9 +228,7 @@ class SettingsDBManager implements Settings { return new String[0]; } - /** - * @see ghidra.docking.settings.Settings#isEmpty() - */ + @Override public boolean isEmpty() { try { return adapter.getSettingsKeys(dataTypeID).length == 0; @@ -274,7 +248,15 @@ class SettingsDBManager implements Settings { } /** - * Compare values and return true if old and new values are different. + * Following settings value change compare old and new values and return + * true if old and new values are different. + * @param oldStrValue old settings string value + * @param newStrValue new settings string value + * @param oldByteValue old settings byte array value + * @param newByteValue new settings byte array value + * @param oldLongValue old settings long value + * @param newLongValue new settings long value + * @return true true if value change detected else false */ static boolean valuesChanged(String oldStrValue, String newStrValue, byte[] oldByteValue, byte[] newByteValue, long oldLongValue, long newLongValue) { @@ -295,9 +277,9 @@ class SettingsDBManager implements Settings { private Record getRecord(String name) { try { - long[] keys = adapter.getSettingsKeys(dataTypeID); + Field[] keys = adapter.getSettingsKeys(dataTypeID); for (int i = 0; i < keys.length; i++) { - Record rec = adapter.getSettingsRecord(keys[i]); + Record rec = adapter.getSettingsRecord(keys[i].getLongValue()); if (rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL).equals(name)) { return rec; } @@ -333,9 +315,8 @@ class SettingsDBManager implements Settings { byte[] recByteValue = record.getBinaryData(SettingsDBAdapter.SETTINGS_BYTE_VALUE_COL); long recLongValue = record.getLongValue(SettingsDBAdapter.SETTINGS_LONG_VALUE_COL); - wasChanged = - valuesChanged(recStrValue, strValue, recByteValue, byteValue, recLongValue, - longValue); + wasChanged = valuesChanged(recStrValue, strValue, recByteValue, byteValue, recLongValue, + longValue); if (wasChanged) { record.setString(SettingsDBAdapter.SETTINGS_STRING_VALUE_COL, strValue); record.setLongValue(SettingsDBAdapter.SETTINGS_LONG_VALUE_COL, longValue); @@ -346,9 +327,7 @@ class SettingsDBManager implements Settings { return wasChanged; } - /** - * @see ghidra.docking.settings.Settings#getDefaultSettings() - */ + @Override public Settings getDefaultSettings() { // This settings object already represents the default settings return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java index 01aa69cd2e..b128c7e16a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveAdapterV0.java @@ -36,8 +36,8 @@ class SourceArchiveAdapterV0 extends SourceArchiveAdapter { static final int V0_ARCHIVE_ID_DIRTY_FLAG_COL = 4; static final Schema V0_SCHEMA = new Schema(VERSION, "Archive ID", - new Class[] { StringField.class, StringField.class, ByteField.class, LongField.class, - BooleanField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, ByteField.INSTANCE, + LongField.INSTANCE, BooleanField.INSTANCE }, new String[] { "Domain File ID", "Name", "Type", "Last Sync Time", "Dirty Flag" }); private Table table; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index da90c05cec..9c87e4427a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import java.util.*; +import db.Field; import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.database.DBObjectCache; @@ -65,9 +66,9 @@ class StructureDB extends CompositeDB implements Structure { components = new ArrayList<>(); try { - long[] ids = componentAdapter.getComponentIdsInComposite(key); - for (long id : ids) { - Record rec = componentAdapter.getRecord(id); + Field[] ids = componentAdapter.getComponentIdsInComposite(key); + for (Field id : ids) { + Record rec = componentAdapter.getRecord(id.getLongValue()); DataTypeComponentDB component = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); if (component.isFlexibleArrayComponent()) { @@ -83,6 +84,7 @@ class StructureDB extends CompositeDB implements Structure { } Collections.sort(components, componentComparator); + structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); numComponents = record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java index f0d4b6f16a..35e07e9a9a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter to access the database table for typedef data types. @@ -177,7 +175,7 @@ abstract class TypedefDBAdapter { * @return an array of IDs for the type definition data types in the category. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsInCategory(long categoryID) throws IOException; + abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; /** * Gets an array with the IDs of all data types in the type definition table that were derived @@ -186,8 +184,15 @@ abstract class TypedefDBAdapter { * @return the array data type IDs. * @throws IOException if the database can't be accessed. */ - abstract long[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + abstract Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException; + /** + * Get typedef record whoose sourceID and datatypeID match the specified Universal IDs. + * @param sourceID universal source archive ID + * @param datatypeID universal datatype ID + * @return typedef record found or null + * @throws IOException if IO error occurs + */ abstract Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java index 7f4323c0c8..d848ec7186 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.data; +import java.io.IOException; + +import db.*; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeManager; import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; import ghidra.util.exception.VersionException; -import java.io.IOException; - -import db.*; - /** * Version 0 implementation for accessing the Typedef database table. */ @@ -55,9 +53,8 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + " but got " + - table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -98,18 +95,16 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V0_TYPEDEF_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { - return new long[0]; + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return Field.EMPTY_ARRAY; } - /* (non-Javadoc) - * @see db.RecordTranslator#translateRecord(db.Record) - */ + @Override public Record translateRecord(Record oldRec) { if (oldRec == null) { return null; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java index b79e777168..eac7c7c2e3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.data; -import ghidra.util.UniversalID; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.Date; import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Typedef database table. @@ -36,10 +34,11 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { static final int V1_TYPEDEF_UNIVERSAL_DT_ID_COL = 4; static final int V1_TYPEDEF_SOURCE_SYNC_TIME_COL = 5; static final int V1_TYPEDEF_LAST_CHANGE_TIME_COL = 6; - static final Schema V1_SCHEMA = new Schema(VERSION, "Typedef ID", new Class[] { - LongField.class, StringField.class, LongField.class, LongField.class, LongField.class, - LongField.class, LongField.class }, new String[] { "Data Type ID", "Name", "Category ID", - "Source Archive ID", "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); + static final Schema V1_SCHEMA = new Schema(VERSION, "Typedef ID", + new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Data Type ID", "Name", "Category ID", "Source Archive ID", + "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); private Table table; /** @@ -49,12 +48,12 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public TypedefDBAdapterV1(DBHandle handle, boolean create) throws VersionException, IOException { + public TypedefDBAdapterV1(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - table = - handle.createTable(TYPEDEF_TABLE_NAME, V1_SCHEMA, new int[] { V1_TYPEDEF_CAT_COL, - V1_TYPEDEF_UNIVERSAL_DT_ID_COL }); + table = handle.createTable(TYPEDEF_TABLE_NAME, V1_SCHEMA, + new int[] { V1_TYPEDEF_CAT_COL, V1_TYPEDEF_UNIVERSAL_DT_ID_COL }); } else { table = handle.getTable(TYPEDEF_TABLE_NAME); @@ -63,9 +62,8 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { } int version = table.getSchema().getVersion(); if (version != VERSION) { - String msg = - "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); + String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + + " but got " + table.getSchema().getVersion(); if (version < VERSION) { throw new VersionException(msg, VersionException.OLDER_VERSION, true); } @@ -126,18 +124,18 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { } @Override - public long[] getRecordIdsInCategory(long categoryID) throws IOException { + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { return table.findRecords(new LongField(categoryID), V1_TYPEDEF_CAT_COL); } @Override - long[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { return table.findRecords(new LongField(archiveID), V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL); } @Override Record getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { - long[] keys = + Field[] keys = table.findRecords(new LongField(datatypeID.getValue()), V1_TYPEDEF_UNIVERSAL_DT_ID_COL); for (int i = 0; i < keys.length; i++) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index 1ec4fa3f91..ef18ce0e59 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -18,6 +18,7 @@ package ghidra.program.database.data; import java.io.IOException; import java.util.*; +import db.Field; import db.Record; import ghidra.docking.settings.Settings; import ghidra.program.database.DBObjectCache; @@ -55,9 +56,9 @@ class UnionDB extends CompositeDB implements Union { components = new ArrayList<>(); try { - long[] ids = componentAdapter.getComponentIdsInComposite(key); - for (long id : ids) { - Record rec = componentAdapter.getRecord(id); + Field[] ids = componentAdapter.getComponentIdsInComposite(key); + for (Field id : ids) { + Record rec = componentAdapter.getRecord(id.getLongValue()); components.add(new DataTypeComponentDB(dataMgr, componentAdapter, this, rec)); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtNameAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtNameAdapter.java index 397efa0842..017e6f60dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtNameAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtNameAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +15,20 @@ */ package ghidra.program.database.external; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; class OldExtNameAdapter { final static String EXT_NAME_TABLE_NAME = "External Program Names"; - static final Schema EXT_NAME_SCHEMA = new Schema(0, "Key", new Class[] { StringField.class, - StringField.class }, new String[] { "External Name", "External Pathname" }); + static final Schema EXT_NAME_SCHEMA = + new Schema(0, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE }, + new String[] { "External Name", "External Pathname" }); static final int EXT_NAME_COL = 0; static final int EXT_PATHNAME_COL = 1; @@ -66,8 +65,8 @@ class OldExtNameAdapter { return nameTable.getRecordCount(); } - private void moveTable(DBHandle handle, TaskMonitor monitor) throws IOException, - CancelledException { + private void moveTable(DBHandle handle, TaskMonitor monitor) + throws IOException, CancelledException { DBHandle tmpHandle = handle.getScratchPad(); Table newRefTable = tmpHandle.createTable(EXT_NAME_TABLE_NAME, EXT_NAME_SCHEMA); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtRefAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtRefAdapter.java index da915d0bbc..5f06340b3c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtRefAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/external/OldExtRefAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +15,22 @@ */ package ghidra.program.database.external; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; class OldExtRefAdapter { static final String EXT_REF_TABLE_NAME = "External References"; - static final Schema EXT_REF_SCHEMA = new Schema(0, "Key", new Class[] { LongField.class, - ShortField.class, BooleanField.class, LongField.class, StringField.class, LongField.class, - BooleanField.class }, new String[] { "From Address", "Op Index", "User Defined", - "External Name ID", "Label", "External To", "External To Exists" }); + static final Schema EXT_REF_SCHEMA = new Schema(0, "Key", + new Field[] { LongField.INSTANCE, ShortField.INSTANCE, BooleanField.INSTANCE, + LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, BooleanField.INSTANCE }, + new String[] { "From Address", "Op Index", "User Defined", "External Name ID", "Label", + "External To", "External To Exists" }); static final int FROM_ADDR_COL = 0; static final int OP_INDEX_COL = 1; @@ -73,8 +72,8 @@ class OldExtRefAdapter { return refTable.getRecordCount(); } - private void moveTable(DBHandle handle, TaskMonitor monitor) throws IOException, - CancelledException { + private void moveTable(DBHandle handle, TaskMonitor monitor) + throws IOException, CancelledException { DBHandle tmpHandle = handle.getScratchPad(); Table newRefTable = tmpHandle.createTable(EXT_REF_TABLE_NAME, EXT_REF_SCHEMA); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java index 75aad3eab6..17095df4a1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/CallingConventionDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.function; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.VersionException; /** * Version 0 implementation for the calling conventions tables adapter. @@ -34,8 +32,8 @@ class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { // Key field is the Calling convention ID, which is a Byte field. static final int V0_CALLING_CONVENTION_NAME_COL = 0; - static final Schema V0_CALLING_CONVENTION_SCHEMA = new Schema(0, ByteField.class, "ID", - new Class[] { StringField.class }, new String[] { "Name" }); + static final Schema V0_CALLING_CONVENTION_SCHEMA = new Schema(0, ByteField.INSTANCE, "ID", + new Field[] { StringField.INSTANCE }, new String[] { "Name" }); private Table callingConventionTable; @@ -43,14 +41,13 @@ class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { * Constructor * */ - public CallingConventionDBAdapterV0(DBHandle handle, boolean create) throws VersionException, - IOException { + public CallingConventionDBAdapterV0(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { // No additional indexed fields. - callingConventionTable = - handle.createTable(CALLING_CONVENTION_TABLE_NAME, V0_CALLING_CONVENTION_SCHEMA, - new int[] {}); + callingConventionTable = handle.createTable(CALLING_CONVENTION_TABLE_NAME, + V0_CALLING_CONVENTION_SCHEMA, new int[] {}); } else { callingConventionTable = handle.getTable(CALLING_CONVENTION_TABLE_NAME); @@ -63,9 +60,6 @@ class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { } } - /* (non-Javadoc) - * @see ghidra.program.database.function.CallingConventionDBAdapter#createCallingConventionRecord(java.lang.String) - */ @Override public Record createCallingConventionRecord(String name) throws IOException { byte key = getFirstAvailableKey(); @@ -94,9 +88,6 @@ class CallingConventionDBAdapterV0 extends CallingConventionDBAdapter { return key; } - /* (non-Javadoc) - * @see ghidra.program.database.function.CallingConventionDBAdapter#getCallingConventionRecord(byte) - */ @Override public Record getCallingConventionRecord(byte callingConventionID) throws IOException { return callingConventionTable.getRecord(new ByteField(callingConventionID)); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapter.java index 6ebf6cd44b..303a11e087 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.function; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressMap; import ghidra.program.model.symbol.SourceType; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Database adapter for functions. */ @@ -51,11 +49,11 @@ abstract class FunctionAdapter { static final int FUNCTION_SIGNATURE_SOURCE_SHIFT = 4; // bit shift for flag storage of "signature SourceType" - final static Schema FUNCTION_SCHEMA = - new Schema(CURRENT_VERSION, "ID", new Class[] { LongField.class, IntField.class, - IntField.class, IntField.class, ByteField.class, ByteField.class, StringField.class }, - new String[] { "Return DataType ID", "StackPurge", "StackReturnOffset", - "StackLocalSize", "Flags", "Calling Convention ID", "Return Storage" }); + final static Schema FUNCTION_SCHEMA = new Schema(CURRENT_VERSION, "ID", + new Field[] { LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, + ByteField.INSTANCE, ByteField.INSTANCE, StringField.INSTANCE }, + new String[] { "Return DataType ID", "StackPurge", "StackReturnOffset", "StackLocalSize", + "Flags", "Calling Convention ID", "Return Storage" }); protected AddressMap addrMap; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagAdapterV0.java index c364c75d80..8622a74278 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagAdapterV0.java @@ -30,18 +30,17 @@ class FunctionTagAdapterV0 extends FunctionTagAdapter implements DBListener { static final int V0_TAG_NAME_COL = 0; static final int V0_COMMENT_COL = 1; - final static Schema V0_SCHEMA = - new Schema(CURRENT_VERSION, "ID", new Class[] { StringField.class, StringField.class }, - new String[] { "Tag", "Comment" }); + final static Schema V0_SCHEMA = new Schema(CURRENT_VERSION, "ID", + new Field[] { StringField.INSTANCE, StringField.INSTANCE }, + new String[] { "Tag", "Comment" }); private Table table; // lazy creation, null if empty private final DBHandle dbHandle; - FunctionTagAdapterV0(DBHandle dbHandle, boolean create) - throws VersionException { + FunctionTagAdapterV0(DBHandle dbHandle, boolean create) throws VersionException { this.dbHandle = dbHandle; - + // This deserves an explanation: // // Both function tag tables are transient, meaning they're created only when necessary, @@ -62,10 +61,8 @@ class FunctionTagAdapterV0 extends FunctionTagAdapter implements DBListener { } } - } - @Override Record getRecord(String tag) throws IOException { if (table == null) { @@ -84,8 +81,7 @@ class FunctionTagAdapterV0 extends FunctionTagAdapter implements DBListener { } @Override - Record createTagRecord(String tag, String comment) - throws IOException { + Record createTagRecord(String tag, String comment) throws IOException { // See if there is already a record for this tag name. If so, // just return that one. @@ -136,10 +132,10 @@ class FunctionTagAdapterV0 extends FunctionTagAdapter implements DBListener { void updateRecord(Record record) throws IOException { getTable().putRecord(record); } - + private Table getTable() throws IOException { if (table == null) { - table = dbHandle.createTable(TABLE_NAME, V0_SCHEMA); + table = dbHandle.createTable(TABLE_NAME, V0_SCHEMA); } return table; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagMappingAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagMappingAdapterV0.java index 0b5ece9e93..9bb36f4dda 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagMappingAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionTagMappingAdapterV0.java @@ -34,14 +34,14 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D public static final int V0_FUNCTION_ID_COL = 0; public static final int V0_TAG_ID_COL = 1; - final static Schema SCHEMA = new Schema(CURRENT_VERSION, "ID", - new Class[] { LongField.class, LongField.class }, new String[] { "Function ID", "Tag ID" }); + final static Schema SCHEMA = + new Schema(CURRENT_VERSION, "ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Function ID", "Tag ID" }); private Table table; // lazy creation, null if empty private final DBHandle dbHandle; - FunctionTagMappingAdapterV0(DBHandle dbHandle, boolean create) - throws VersionException { + FunctionTagMappingAdapterV0(DBHandle dbHandle, boolean create) throws VersionException { this.dbHandle = dbHandle; @@ -72,7 +72,7 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D @Override Record getRecord(long functionID, long tagID) throws IOException { - + if (table == null) { return null; } @@ -93,8 +93,7 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D } @Override - Record createFunctionTagRecord(long functionID, long tagID) - throws IOException { + Record createFunctionTagRecord(long functionID, long tagID) throws IOException { Table t = getTable(); Record rec = SCHEMA.createRecord(t.getKey()); @@ -106,8 +105,7 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D } @Override - boolean removeFunctionTagRecord(long functionID, long tagID) - throws IOException { + boolean removeFunctionTagRecord(long functionID, long tagID) throws IOException { Record record = getRecord(functionID, tagID); if (record != null) { @@ -123,7 +121,7 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D if (table == null) { return; } - + // Tag ID is not an indexed column in the mapping table, so we just have to iterate // over all records. This operation is only done when deleting a tag (ie: not often) // so it won't be indexed. @@ -169,7 +167,7 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D } return false; } - + private Table getTable() throws IOException { if (table == null) { table = dbHandle.createTable(TABLE_NAME, SCHEMA, new int[] { V0_FUNCTION_ID_COL }); @@ -194,5 +192,5 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D public void tableAdded(DBHandle dbh, Table table) { // do nothing } - + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/ThunkFunctionAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/ThunkFunctionAdapter.java index 36b5ed455e..c25198afe4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/ThunkFunctionAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/ThunkFunctionAdapter.java @@ -31,7 +31,7 @@ abstract class ThunkFunctionAdapter { static final int LINKED_FUNCTION_ID_COL = 0; final static Schema THUNK_FUNCTION_SCHEMA = new Schema(CURRENT_VERSION, "ID", - new Class[] { LongField.class }, new String[] { "Linked Function ID" }); + new Field[] { LongField.INSTANCE }, new String[] { "Linked Function ID" }); protected AddressMap addrMap; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java index 1a7c813685..6304f736f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressIndexPrimaryKeyIterator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,24 +15,23 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; - import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; import db.*; +import ghidra.program.model.address.*; /** * Long iterator over indexed addresses. The longs are primary keys returned ordered and restrained * by the address field they contain */ -public class AddressIndexPrimaryKeyIterator implements DBLongIterator { +public class AddressIndexPrimaryKeyIterator implements DBFieldIterator { private Table table; private List keyRangeList; - private DBLongIterator it; + private DBFieldIterator it; private int keyRangeIndex = -1; private int indexCol; @@ -113,16 +111,14 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { if (atStart) { keyRangeIndex = 0; KeyRange keyRange = keyRangeList.get(keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), true); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), true); } else { keyRangeIndex = keyRangeList.size() - 1; KeyRange keyRange = keyRangeList.get(keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), false); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), false); } } @@ -169,15 +165,13 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { keyRangeIndex = -keyRangeIndex - 1; if (keyRangeIndex == 0) { KeyRange keyRange = keyRangeList.get(keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), true); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), true); } else { KeyRange keyRange = keyRangeList.get(--keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), false); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), false); } } else { @@ -185,15 +179,12 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { KeyRange keyRange = keyRangeList.get(keyRangeIndex); long startKey = absolute ? addrMap.getAbsoluteEncoding(start, false) : addrMap.getKey(start, false); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), new LongField(startKey), before); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), new LongField(startKey), before); } } - /** - * @see db.DBLongIterator#hasNext() - */ + @Override public boolean hasNext() throws IOException { if (it == null) { return false; @@ -201,9 +192,8 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { else if (!it.hasNext()) { while (keyRangeIndex < (keyRangeList.size() - 1)) { KeyRange keyRange = keyRangeList.get(++keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), true); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), true); if (it.hasPrevious()) { it.previous(); } @@ -216,9 +206,7 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { return true; } - /** - * @see db.DBLongIterator#hasPrevious() - */ + @Override public boolean hasPrevious() throws IOException { if (it == null) { return false; @@ -226,9 +214,8 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { else if (!it.hasPrevious()) { while (keyRangeIndex > 0) { KeyRange keyRange = keyRangeList.get(--keyRangeIndex); - it = - table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), new LongField( - keyRange.maxKey), false); + it = table.indexKeyIterator(indexCol, new LongField(keyRange.minKey), + new LongField(keyRange.maxKey), false); if (it.hasNext()) { it.next(); } @@ -241,29 +228,23 @@ public class AddressIndexPrimaryKeyIterator implements DBLongIterator { return true; } - /** - * @see db.DBLongIterator#next() - */ - public long next() throws IOException { + @Override + public Field next() throws IOException { if (hasNext()) { return it.next(); } throw new NoSuchElementException(); } - /** - * @see db.DBLongIterator#previous() - */ - public long previous() throws IOException { + @Override + public Field previous() throws IOException { if (hasPrevious()) { return it.previous(); } throw new NoSuchElementException(); } - /** - * @see db.DBLongIterator#delete() - */ + @Override public boolean delete() throws IOException { if (it != null) { return it.delete(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV0.java index a344970462..479b1ed7f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV0.java @@ -15,22 +15,22 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.ArrayList; import java.util.List; import db.*; +import ghidra.program.model.address.*; +import ghidra.util.exception.VersionException; /** * Adapter version 0 (the first real adapter) */ class AddressMapDBAdapterV0 extends AddressMapDBAdapter { - final Schema SCHEMA = new Schema(0, "Key", new Class[] { StringField.class, IntField.class, - ShortField.class }, new String[] { "Space Name", "Segment", "Not Used" }); + final Schema SCHEMA = new Schema(0, "Key", + new Field[] { StringField.INSTANCE, IntField.INSTANCE, ShortField.INSTANCE }, + new String[] { "Space Name", "Segment", "Not Used" }); final int SPACE_NAME_COL = 0; final int SEGMENT_COL = 1; @@ -40,8 +40,8 @@ class AddressMapDBAdapterV0 extends AddressMapDBAdapter { private AddressFactory factory; private Address[] addresses; - AddressMapDBAdapterV0(DBHandle handle, AddressFactory factory) throws VersionException, - IOException { + AddressMapDBAdapterV0(DBHandle handle, AddressFactory factory) + throws VersionException, IOException { this.handle = handle; this.factory = factory; table = handle.getTable(TABLE_NAME); @@ -65,9 +65,8 @@ class AddressMapDBAdapterV0 extends AddressMapDBAdapter { int segment = rec.getIntValue(SEGMENT_COL); AddressSpace space = factory.getAddressSpace(spaceName); if (space == null) { - GenericAddressSpace sp = - new GenericAddressSpace("Deleted_" + spaceName, 32, AddressSpace.TYPE_UNKNOWN, - deletedID++); + GenericAddressSpace sp = new GenericAddressSpace("Deleted_" + spaceName, 32, + AddressSpace.TYPE_UNKNOWN, deletedID++); sp.setShowSpaceName(true); space = sp; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV1.java index b3cefc96cc..4a6402eba5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapterV1.java @@ -15,22 +15,22 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.*; -import ghidra.util.exception.VersionException; - import java.io.IOException; import java.util.ArrayList; import java.util.List; import db.*; +import ghidra.program.model.address.*; +import ghidra.util.exception.VersionException; /** * Adapter version 0 (the first real adapter) */ class AddressMapDBAdapterV1 extends AddressMapDBAdapter { - final Schema SCHEMA = new Schema(CURRENT_VERSION, "Key", new Class[] { StringField.class, - IntField.class, BooleanField.class }, new String[] { "Space Name", "Segment", "Deleted" }); + final Schema SCHEMA = new Schema(CURRENT_VERSION, "Key", + new Field[] { StringField.INSTANCE, IntField.INSTANCE, BooleanField.INSTANCE }, + new String[] { "Space Name", "Segment", "Deleted" }); final int SPACE_NAME_COL = 0; final int SEGMENT_COL = 1; @@ -75,9 +75,8 @@ class AddressMapDBAdapterV1 extends AddressMapDBAdapter { if (segment != 0) { spaceName += "_" + segment; } - GenericAddressSpace sp = - new GenericAddressSpace(deletedName, 32, AddressSpace.TYPE_DELETED, - (int) rec.getKey()); + GenericAddressSpace sp = new GenericAddressSpace(deletedName, 32, + AddressSpace.TYPE_DELETED, (int) rec.getKey()); sp.setShowSpaceName(true); space = sp; segment = 0; @@ -157,8 +156,8 @@ class AddressMapDBAdapterV1 extends AddressMapDBAdapter { Address[] newAddrs = new Address[addresses.length + 1]; System.arraycopy(addresses, 0, newAddrs, 0, addresses.length); - newAddrs[addresses.length] = - addr.getAddressSpace().getAddressInThisSpaceOnly(normalizedOffset & ~AddressMapDB.ADDR_OFFSET_MASK); + newAddrs[addresses.length] = addr.getAddressSpace().getAddressInThisSpaceOnly( + normalizedOffset & ~AddressMapDB.ADDR_OFFSET_MASK); addresses = newAddrs; return addresses; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressRecordDeleter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressRecordDeleter.java index ed4030da5a..ad94e417c1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressRecordDeleter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressRecordDeleter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.map; -import ghidra.program.database.util.RecordFilter; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.KeyRange; - import java.io.IOException; import java.util.Iterator; import java.util.List; import db.*; +import ghidra.program.database.util.RecordFilter; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.KeyRange; /** * Static methods to delete records from a table. Handles subtle issues with image base causing @@ -76,10 +74,10 @@ public class AddressRecordDeleter { Address end, RecordFilter filter) throws IOException { boolean success = false; - DBLongIterator iter = + DBFieldIterator iter = new AddressIndexPrimaryKeyIterator(table, colIx, addrMap, start, end, true); while (iter.hasNext()) { - long next = iter.next(); + Field next = iter.next(); if (filter != null) { Record record = table.getRecord(next); if (!filter.matches(record)) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java index a230f51320..d9482941bd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java @@ -37,8 +37,8 @@ class FileBytesAdapterV0 extends FileBytesAdapter { public static final int V0_LAYERED_BUF_IDS_COL = 4; static final Schema SCHEMA = new Schema(VERSION, "Key", - new Class[] { StringField.class, LongField.class, LongField.class, BinaryField.class, - BinaryField.class }, + new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + BinaryField.INSTANCE, BinaryField.INSTANCE }, new String[] { "Filename", "Offset", "Size", "Chain Buffer IDs", "Layered Chain Buffer IDs" }); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java index fa5afc1f6e..1d6817f761 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java @@ -58,14 +58,14 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { public static final byte V3_SUB_TYPE_FILE_BYTES = 4; static Schema V3_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key", - new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class, - LongField.class, LongField.class, IntField.class }, + new Field[] { StringField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE, + ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE }, new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", "Length", "Segment" }); static Schema V3_SUB_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key", - new Class[] { LongField.class, ByteField.class, LongField.class, LongField.class, - IntField.class, LongField.class }, + new Field[] { LongField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE }, new String[] { "Parent ID", "Type", "Length", "Starting Offset", "Source ID", "Source Address/Offset" }); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/FragmentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/FragmentDB.java index c2ec38fec2..98e964ce2d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/FragmentDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/FragmentDB.java @@ -18,6 +18,7 @@ package ghidra.program.database.module; import java.io.IOException; import java.util.Iterator; +import db.Field; import db.Record; import ghidra.program.database.DBObjectCache; import ghidra.program.database.DatabaseObject; @@ -74,26 +75,17 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { return false; } - /** - * @see ghidra.program.model.listing.Group#contains(ghidra.program.model.listing.CodeUnit) - */ @Override public boolean contains(CodeUnit codeUnit) { return contains(codeUnit.getMinAddress()); } - /** - * @see ghidra.program.model.listing.ProgramFragment#getCodeUnits() - */ @Override public CodeUnitIterator getCodeUnits() { checkIsValid(); return moduleMgr.getCodeUnits(this); } - /** - * @see ghidra.program.model.listing.Group#getComment() - */ @Override public String getComment() { lock.acquire(); @@ -106,9 +98,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.listing.Group#getName() - */ @Override public String getName() { lock.acquire(); @@ -121,15 +110,12 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.listing.Group#getNumParents() - */ @Override public int getNumParents() { lock.acquire(); try { checkIsValid(); - long[] keys = adapter.getParentChildKeys(-key, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(-key, TreeManager.CHILD_ID_COL); return keys.length; } catch (IOException e) { @@ -141,25 +127,16 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { return 0; } - /** - * @see ghidra.program.model.listing.Group#getParentNames() - */ @Override public String[] getParentNames() { return moduleMgr.getParentNames(-key); } - /** - * @see ghidra.program.model.listing.Group#getParents() - */ @Override public ProgramModule[] getParents() { return moduleMgr.getParents(-key); } - /** - * @see ghidra.program.model.listing.ProgramFragment#move(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override public void move(Address min, Address max) throws NotFoundException { lock.acquire(); @@ -172,9 +149,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.listing.Group#setComment(java.lang.String) - */ @Override public void setComment(String comment) { lock.acquire(); @@ -198,9 +172,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.listing.Group#setName(java.lang.String) - */ @Override public void setName(String name) throws DuplicateNameException { lock.acquire(); @@ -230,17 +201,11 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.listing.Group#getTreeName() - */ @Override public String getTreeName() { return moduleMgr.getTreeName(); } - /** - * @see ghidra.program.model.address.AddressSetView#contains(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override public boolean contains(Address start, Address end) { lock.acquire(); @@ -253,9 +218,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#contains(ghidra.program.model.address.Address) - */ @Override public boolean contains(Address addr) { lock.acquire(); @@ -268,9 +230,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#contains(ghidra.program.model.address.AddressSetView) - */ @Override public boolean contains(AddressSetView rangeSet) { lock.acquire(); @@ -283,9 +242,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#equals(ghidra.program.model.address.AddressSetView) - */ @Override public boolean hasSameAddresses(AddressSetView view) { lock.acquire(); @@ -298,10 +254,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /* - * (non-Javadoc) - * @see ghidra.program.model.address.AddressSetView#getAddresses(boolean) - */ @Override public AddressIterator getAddresses(boolean forward) { lock.acquire(); @@ -314,10 +266,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /* - * (non-Javadoc) - * @see ghidra.program.model.address.AddressSetView#getAddresses(ghidra.program.model.address.Address, boolean) - */ @Override public AddressIterator getAddresses(Address start, boolean forward) { lock.acquire(); @@ -330,9 +278,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#getAddressRanges() - */ @Override public AddressRangeIterator getAddressRanges() { lock.acquire(); @@ -350,10 +295,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { return getAddressRanges(); } - /** - * - * @see ghidra.program.model.address.AddressSetView#getAddressRanges(boolean) - */ @Override public AddressRangeIterator getAddressRanges(boolean atStart) { lock.acquire(); @@ -366,9 +307,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#getMaxAddress() - */ @Override public Address getMaxAddress() { lock.acquire(); @@ -381,9 +319,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#getMinAddress() - */ @Override public Address getMinAddress() { lock.acquire(); @@ -396,9 +331,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#getNumAddresses() - */ @Override public long getNumAddresses() { lock.acquire(); @@ -411,9 +343,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#getNumAddressRanges() - */ @Override public int getNumAddressRanges() { lock.acquire(); @@ -426,9 +355,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#intersect(ghidra.program.model.address.AddressSetView) - */ @Override public AddressSet intersect(AddressSetView view) { lock.acquire(); @@ -441,9 +367,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#intersectRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override public AddressSet intersectRange(Address start, Address end) { lock.acquire(); @@ -456,9 +379,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#intersects(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override public boolean intersects(Address start, Address end) { lock.acquire(); @@ -471,9 +391,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#intersects(ghidra.program.model.address.AddressSetView) - */ @Override public boolean intersects(AddressSetView set) { lock.acquire(); @@ -486,9 +403,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#isEmpty() - */ @Override public boolean isEmpty() { lock.acquire(); @@ -501,9 +415,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#subtract(ghidra.program.model.address.AddressSetView) - */ @Override public AddressSet subtract(AddressSetView set) { lock.acquire(); @@ -516,9 +427,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#union(ghidra.program.model.address.AddressSetView) - */ @Override public AddressSet union(AddressSetView set) { lock.acquire(); @@ -531,9 +439,6 @@ class FragmentDB extends DatabaseObject implements ProgramFragment { } } - /** - * @see ghidra.program.model.address.AddressSetView#xor(ghidra.program.model.address.AddressSetView) - */ @Override public AddressSet xor(AddressSetView set) { lock.acquire(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapter.java index 788fdcda07..2eaceb2d0c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,11 @@ */ package ghidra.program.database.module; -import ghidra.util.exception.DuplicateNameException; - import java.io.IOException; +import db.Field; import db.Record; +import ghidra.util.exception.DuplicateNameException; /** * Adapter to access the module, fragment, and parent/child database tables. @@ -46,8 +45,8 @@ interface GroupDBAdapter { * @throws DuplicateNameException if a module or fragment already exists * having the given name */ - Record createModule(long parentModuleID, String name) throws IOException, - DuplicateNameException; + Record createModule(long parentModuleID, String name) + throws IOException, DuplicateNameException; /** * Get the record for the module with the given key. @@ -80,8 +79,8 @@ interface GroupDBAdapter { * @throws DuplicateNameException if a module or fragment already exists * having the given name */ - Record createFragment(long parentModuleID, String name) throws IOException, - DuplicateNameException; + Record createFragment(long parentModuleID, String name) + throws IOException, DuplicateNameException; /** * Get the record for the fragment with the given key. @@ -123,7 +122,7 @@ interface GroupDBAdapter { * @return zero-length array if no records were found * @throws IOException if there was a problem accessing the database */ - long[] getParentChildKeys(long ID, int indexedCol) throws IOException; + Field[] getParentChildKeys(long ID, int indexedCol) throws IOException; /** * Get the Parent/Child record with the given key. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapterV0.java index fbbc39fa6c..4c478060d4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/GroupDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.module; -import ghidra.util.exception.*; - import java.io.IOException; import db.*; +import ghidra.util.exception.*; /** * @@ -45,11 +43,9 @@ class GroupDBAdapterV0 implements GroupDBAdapter { testVersion(parentChildTable, 0, parentChildTableName); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#createModule(java.lang.String) - */ - public Record createModule(long parentModuleID, String name) throws IOException, - DuplicateNameException { + @Override + public Record createModule(long parentModuleID, String name) + throws IOException, DuplicateNameException { if (getModuleRecord(name) != null || getFragmentRecord(name) != null) { throw new DuplicateNameException(name + " already exists"); @@ -67,11 +63,9 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return record; } - /** - * @see ghidra.program.database.module.GroupDBAdapter#createFragment(java.lang.String) - */ - public Record createFragment(long parentModuleID, String name) throws IOException, - DuplicateNameException { + @Override + public Record createFragment(long parentModuleID, String name) + throws IOException, DuplicateNameException { if (getFragmentRecord(name) != null || getModuleRecord(name) != null) { throw new DuplicateNameException(name + " already exists"); @@ -94,16 +88,12 @@ class GroupDBAdapterV0 implements GroupDBAdapter { } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getFragmentRecord(long) - */ + @Override public Record getFragmentRecord(long key) throws IOException { return fragmentTable.getRecord(key); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getModuleRecord(long) - */ + @Override public Record getModuleRecord(long key) throws IOException { return moduleTable.getRecord(key); } @@ -112,8 +102,9 @@ class GroupDBAdapterV0 implements GroupDBAdapter { * @see ghidra.program.database.module.GroupDBAdapter#getParentChildRecord(long, long) * @param childID negative value if child is a fragment */ + @Override public Record getParentChildRecord(long parentID, long childID) throws IOException { - long[] keys = + Field[] keys = parentChildTable.findRecords(new LongField(parentID), TreeManager.PARENT_ID_COL); for (int i = 0; i < keys.length; i++) { Record pcRec = parentChildTable.getRecord(keys[i]); @@ -124,9 +115,7 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return null; } - /** - * @see ghidra.program.database.module.GroupDBAdapter#addParentChildRecord(long, long) - */ + @Override public Record addParentChildRecord(long moduleID, long childID) throws IOException { Record pcRec = TreeManager.PARENT_CHILD_SCHEMA.createRecord(parentChildTable.getKey()); @@ -136,25 +125,19 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return pcRec; } - /** - * @see ghidra.program.database.module.GroupDBAdapter#removeParentChildRecord(long) - */ + @Override public boolean removeParentChildRecord(long key) throws IOException { return parentChildTable.deleteRecord(key); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getParentChildRecords(long) - */ - public long[] getParentChildKeys(long parentID, int indexedCol) throws IOException { + @Override + public Field[] getParentChildKeys(long parentID, int indexedCol) throws IOException { return parentChildTable.findRecords(new LongField(parentID), indexedCol); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getFragmentRecord(java.lang.String) - */ + @Override public Record getFragmentRecord(String name) throws IOException { - long[] keys = + Field[] keys = fragmentTable.findRecords(new StringField(name), TreeManager.FRAGMENT_NAME_COL); if (keys.length == 0) { return null; @@ -165,11 +148,9 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return fragmentTable.getRecord(keys[0]); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getModuleRecord(java.lang.String) - */ + @Override public Record getModuleRecord(String name) throws IOException { - long[] keys = moduleTable.findRecords(new StringField(name), TreeManager.MODULE_NAME_COL); + Field[] keys = moduleTable.findRecords(new StringField(name), TreeManager.MODULE_NAME_COL); if (keys.length == 0) { return null; } @@ -179,37 +160,27 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return moduleTable.getRecord(keys[0]); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#getParentChildRecord(long) - */ + @Override public Record getParentChildRecord(long key) throws IOException { return parentChildTable.getRecord(key); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ + @Override public void updateModuleRecord(Record record) throws IOException { moduleTable.putRecord(record); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#updateFragmentRecord(ghidra.framework.store.db.Record) - */ + @Override public void updateFragmentRecord(Record record) throws IOException { fragmentTable.putRecord(record); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#updateParentChildRecord(ghidra.framework.store.db.Record) - */ + @Override public void updateParentChildRecord(Record record) throws IOException { parentChildTable.putRecord(record); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#createRootModule() - */ + @Override public Record createRootModule(String name) throws IOException { Record record = TreeManager.MODULE_SCHEMA.createRecord(0); record.setString(TreeManager.MODULE_NAME_COL, name); @@ -217,29 +188,26 @@ class GroupDBAdapterV0 implements GroupDBAdapter { return record; } - /** - * @see ghidra.program.database.module.GroupDBAdapter#removeFragmentRecord(long) - */ + @Override public boolean removeFragmentRecord(long childID) throws IOException { return fragmentTable.deleteRecord(childID); } - /** - * @see ghidra.program.database.module.GroupDBAdapter#removeModuleRecord(long) - */ + @Override public boolean removeModuleRecord(long childID) throws IOException { return moduleTable.deleteRecord(childID); } - private void testVersion(Table table, int expectedVersion, String name) throws VersionException { + private void testVersion(Table table, int expectedVersion, String name) + throws VersionException { if (table == null) { throw new VersionException(name + " not found"); } int versionNumber = table.getSchema().getVersion(); if (versionNumber != expectedVersion) { - throw new VersionException(name + ": Expected Version " + expectedVersion + ", got " + - versionNumber); + throw new VersionException( + name + ": Expected Version " + expectedVersion + ", got " + versionNumber); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleDB.java index 67a80350a8..ce73ae2dc7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleDB.java @@ -18,6 +18,7 @@ package ghidra.program.database.module; import java.io.IOException; import java.util.*; +import db.Field; import db.Record; import ghidra.program.database.DBObjectCache; import ghidra.program.database.DatabaseObject; @@ -65,7 +66,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { record = rec; childCount = 0; try { - long[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); + Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); childCount = keys.length; } catch (IOException e) { @@ -80,9 +81,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return false; } - /** - * @see ghidra.program.model.listing.ProgramModule#add(ghidra.program.model.listing.ProgramFragment) - */ + @Override public void add(ProgramFragment fragment) throws DuplicateGroupException { lock.acquire(); try { @@ -92,8 +91,8 @@ class ModuleDB extends DatabaseObject implements ProgramModule { // add a row to the parent/child table Record parentChildRecord = adapter.getParentChildRecord(key, -fragID); if (parentChildRecord != null) { - throw new DuplicateGroupException(frag.getName() + " already exists a child of " + - getName()); + throw new DuplicateGroupException( + frag.getName() + " already exists a child of " + getName()); } Record pcRec = adapter.addParentChildRecord(key, -fragID); @@ -109,10 +108,9 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#add(ghidra.program.model.listing.ProgramModule) - */ - public void add(ProgramModule module) throws CircularDependencyException, DuplicateGroupException { + @Override + public void add(ProgramModule module) + throws CircularDependencyException, DuplicateGroupException { lock.acquire(); try { @@ -122,12 +120,12 @@ class ModuleDB extends DatabaseObject implements ProgramModule { Record parentChildRecord = adapter.getParentChildRecord(key, moduleID); if (parentChildRecord != null) { - throw new DuplicateGroupException(module.getName() + " already exists a child of " + - getName()); + throw new DuplicateGroupException( + module.getName() + " already exists a child of " + getName()); } if (moduleMgr.isDescendant(key, moduleID)) { - throw new CircularDependencyException(getName() + " is already a descendant of " + - module.getName()); + throw new CircularDependencyException( + getName() + " is already a descendant of " + module.getName()); } Record pcRec = adapter.addParentChildRecord(key, moduleID); @@ -143,9 +141,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#contains(ghidra.program.model.listing.ProgramFragment) - */ + @Override public boolean contains(ProgramFragment fragment) { if (!(fragment instanceof FragmentDB)) { return false; @@ -157,9 +153,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return contains(-frag.getKey()); } - /** - * @see ghidra.program.model.listing.ProgramModule#contains(ghidra.program.model.listing.ProgramModule) - */ + @Override public boolean contains(ProgramModule module) { if (!(module instanceof ModuleDB)) { return false; @@ -171,9 +165,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return contains(moduleDB.getKey()); } - /** - * @see ghidra.program.model.listing.ProgramModule#createFragment(java.lang.String) - */ + @Override public ProgramFragment createFragment(String fragmentName) throws DuplicateNameException { lock.acquire(); @@ -196,9 +188,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return null; } - /** - * @see ghidra.program.model.listing.ProgramModule#createModule(java.lang.String) - */ + @Override public ProgramModule createModule(String moduleName) throws DuplicateNameException { lock.acquire(); @@ -221,9 +211,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return null; } - /** - * @see ghidra.program.model.listing.ProgramModule#getChildren() - */ + @Override public Group[] getChildren() { lock.acquire(); try { @@ -252,9 +240,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return new Group[0]; } - /** - * @see ghidra.program.model.listing.Group#getComment() - */ + @Override public String getComment() { lock.acquire(); try { @@ -266,9 +252,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#getFirstAddress() - */ + @Override public Address getFirstAddress() { lock.acquire(); try { @@ -280,9 +264,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#getIndex(java.lang.String) - */ + @Override public int getIndex(String name) { lock.acquire(); try { @@ -312,9 +294,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return -1; } - /** - * @see ghidra.program.model.listing.ProgramModule#getLastAddress() - */ + @Override public Address getLastAddress() { lock.acquire(); try { @@ -326,9 +306,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#getMaxAddress() - */ + @Override public Address getMaxAddress() { lock.acquire(); try { @@ -340,9 +318,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#getMinAddress() - */ + @Override public Address getMinAddress() { lock.acquire(); try { @@ -354,9 +330,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#getAddressSet() - */ + @Override public AddressSetView getAddressSet() { AddressSet set = new AddressSet(); Group[] children = getChildren(); @@ -372,9 +346,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return set; } - /** - * @see ghidra.program.model.listing.ProgramModule#getNumChildren() - */ + @Override public int getNumChildren() { lock.acquire(); try { @@ -386,9 +358,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#isDescendant(ghidra.program.model.listing.ProgramFragment) - */ + @Override public boolean isDescendant(ProgramFragment fragment) { if (!(fragment instanceof FragmentDB)) { return false; @@ -403,9 +373,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return false; } - /** - * @see ghidra.program.model.listing.ProgramModule#isDescendant(ghidra.program.model.listing.ProgramModule) - */ + @Override public boolean isDescendant(ProgramModule module) { if (!(module instanceof ModuleDB)) { return false; @@ -420,9 +388,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return false; } - /** - * @see ghidra.program.model.listing.ProgramModule#moveChild(java.lang.String, int) - */ + @Override public void moveChild(String name, int index) throws NotFoundException { lock.acquire(); try { @@ -473,9 +439,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.ProgramModule#removeChild(java.lang.String) - */ + @Override public boolean removeChild(String name) throws NotEmptyException { lock.acquire(); @@ -492,7 +456,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { // check for module record return removeModuleRecord(name); } - long[] keys = adapter.getParentChildKeys(-childID, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(-childID, TreeManager.CHILD_ID_COL); if (keys.length == 1) { FragmentDB frag = moduleMgr.getFragmentDB(childID); if (!frag.isEmpty()) { @@ -525,8 +489,8 @@ class ModuleDB extends DatabaseObject implements ProgramModule { if (pcRec == null) { return false; } - long[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); if (keys.length == 1) { ProgramModule module = moduleMgr.getModuleDB(childID); if (module.getNumChildren() > 0) { @@ -538,9 +502,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return removeChild(childID, pcRec, false, deleteChild); } - /** - * @see ghidra.program.model.listing.ProgramModule#reparent(java.lang.String, ghidra.program.model.listing.ProgramModule) - */ + @Override public void reparent(String name, ProgramModule oldParent) throws NotFoundException { Group group = null; @@ -582,9 +544,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.Group#contains(ghidra.program.model.listing.CodeUnit) - */ + @Override public boolean contains(CodeUnit codeUnit) { FragmentDB frag = moduleMgr.getFragment(codeUnit); if (frag != null) { @@ -593,9 +553,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return false; } - /** - * @see ghidra.program.model.listing.Group#getName() - */ + @Override public String getName() { lock.acquire(); try { @@ -607,14 +565,12 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.Group#getNumParents() - */ + @Override public int getNumParents() { lock.acquire(); try { checkIsValid(); - long[] keys = adapter.getParentChildKeys(key, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(key, TreeManager.CHILD_ID_COL); return keys.length; } catch (IOException e) { @@ -626,30 +582,22 @@ class ModuleDB extends DatabaseObject implements ProgramModule { return 0; } - /** - * @see ghidra.program.model.listing.Group#getParentNames() - */ + @Override public String[] getParentNames() { return moduleMgr.getParentNames(key); } - /** - * @see ghidra.program.model.listing.Group#getParents() - */ + @Override public ProgramModule[] getParents() { return moduleMgr.getParents(key); } - /** - * @see ghidra.program.model.listing.Group#getTreeName() - */ + @Override public String getTreeName() { return moduleMgr.getTreeName(); } - /** - * @see ghidra.program.model.listing.Group#setComment(java.lang.String) - */ + @Override public void setComment(String comment) { lock.acquire(); try { @@ -671,9 +619,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } } - /** - * @see ghidra.program.model.listing.Group#setName(java.lang.String) - */ + @Override public void setName(String name) throws DuplicateNameException { lock.acquire(); try { @@ -754,11 +700,11 @@ class ModuleDB extends DatabaseObject implements ProgramModule { * Get sorted list based on child order column. */ private List getParentChildRecords() throws IOException { - long[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); + Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); List list = new ArrayList(); Comparator c = new ParentChildRecordComparator(); for (int i = 0; i < keys.length; i++) { - Record rec = adapter.getParentChildRecord(keys[i]); + Record rec = adapter.getParentChildRecord(keys[i].getLongValue()); int index = Collections.binarySearch(list, rec, c); if (index < 0) { index = -index - 1; @@ -921,7 +867,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { checkIsValid(); childCount = 0; try { - long[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); + Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL); childCount = keys.length; } catch (IOException e) { @@ -931,6 +877,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule { private class ParentChildRecordComparator implements Comparator { + @Override public int compare(Record r1, Record r2) { int index1 = r1.getIntValue(TreeManager.ORDER_COL); int index2 = r2.getIntValue(TreeManager.ORDER_COL); @@ -945,23 +892,17 @@ class ModuleDB extends DatabaseObject implements ProgramModule { } - /** - * @see ghidra.program.model.listing.ProgramModule#getVersionTag() - */ + @Override public Object getVersionTag() { return moduleMgr.getVersionTag(); } - /* (non-Javadoc) - * @see ghidra.program.model.listing.Module#getModificationNumber() - */ + @Override public long getModificationNumber() { return moduleMgr.getModificationNumber(); } - /* (non-Javadoc) - * @see ghidra.program.model.listing.Module#getTreeID() - */ + @Override public long getTreeID() { return moduleMgr.getTreeID(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleManager.java index 3d68a879f6..cb21367ac2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/ModuleManager.java @@ -72,7 +72,7 @@ class ModuleManager { nameSet = new HashSet<>(); errHandler = treeMgr.getErrorHandler(); fragMap = new AddressRangeMapDB(handle, addrMap, lock, - TreeManager.getFragAddressTableName(treeID), errHandler, LongField.class, true); + TreeManager.getFragAddressTableName(treeID), errHandler, LongField.INSTANCE, true); if (createTables) { createDBTables(handle); } @@ -97,7 +97,7 @@ class ModuleManager { String mapName = TreeManager.getFragAddressTableName(treeID); AddressRangeMapDB map = new AddressRangeMapDB(handle, addrMap.getOldAddressMap(), - treeMgr.getLock(), mapName, errHandler, LongField.class, true); + treeMgr.getLock(), mapName, errHandler, LongField.INSTANCE, true); if (map.isEmpty()) { return; } @@ -113,7 +113,7 @@ class ModuleManager { int count = 0; AddressRangeMapDB tmpMap = new AddressRangeMapDB(tmpDb, addrMap, - new Lock("Tmp Upgrade"), mapName, errHandler, LongField.class, false); + new Lock("Tmp Upgrade"), mapName, errHandler, LongField.INSTANCE, false); AddressRangeIterator iter = map.getAddressRanges(); while (iter.hasNext()) { @@ -135,7 +135,7 @@ class ModuleManager { // Copy ranges into new map map = new AddressRangeMapDB(handle, addrMap, treeMgr.getLock(), mapName, errHandler, - LongField.class, true); + LongField.INSTANCE, true); iter = tmpMap.getAddressRanges(); while (iter.hasNext()) { monitor.checkCanceled(); @@ -457,22 +457,22 @@ class ModuleManager { } /** - * Return true if ID is a descendant of moduleID. + * Return true if specified id is a descendant of moduleID. */ - boolean isDescendant(long ID, long moduleID) throws IOException { + boolean isDescendant(long id, long moduleID) throws IOException { - long[] keys = adapter.getParentChildKeys(moduleID, TreeManager.PARENT_ID_COL); + Field[] keys = adapter.getParentChildKeys(moduleID, TreeManager.PARENT_ID_COL); if (keys.length == 0) { return false; } - for (long key : keys) { - Record parentChildRecord = adapter.getParentChildRecord(key); + for (Field key : keys) { + Record parentChildRecord = adapter.getParentChildRecord(key.getLongValue()); long childID = parentChildRecord.getLongValue(TreeManager.CHILD_ID_COL); - if (childID == ID) { + if (childID == id) { return true; } - if (isDescendant(ID, childID)) { + if (isDescendant(id, childID)) { return true; } } @@ -645,10 +645,10 @@ class ModuleManager { String[] getParentNames(long childID) { lock.acquire(); try { - long[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); String[] names = new String[keys.length]; for (int i = 0; i < keys.length; i++) { - Record parentChildRecord = adapter.getParentChildRecord(keys[i]); + Record parentChildRecord = adapter.getParentChildRecord(keys[i].getLongValue()); Record mrec = adapter.getModuleRecord( parentChildRecord.getLongValue(TreeManager.PARENT_ID_COL)); names[i] = mrec.getString(TreeManager.MODULE_NAME_COL); @@ -668,10 +668,10 @@ class ModuleManager { ProgramModule[] getParents(long childID) { lock.acquire(); try { - long[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); + Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL); ProgramModule[] modules = new ProgramModule[keys.length]; for (int i = 0; i < keys.length; i++) { - Record parentChildRecord = adapter.getParentChildRecord(keys[i]); + Record parentChildRecord = adapter.getParentChildRecord(keys[i].getLongValue()); Record mrec = adapter.getModuleRecord( parentChildRecord.getLongValue(TreeManager.PARENT_ID_COL)); modules[i] = getModuleDB(mrec); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeDBAdapterV0.java index 9940c422bb..4533f0e41a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.module; -import ghidra.util.exception.AssertException; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.VersionException; /** * @@ -51,9 +49,7 @@ class TreeDBAdapterV0 implements TreeDBAdapter { } } - /** - * @see ghidra.program.database.module.TreeDBAdapter#createRecord(long, java.lang.String, int) - */ + @Override public Record createRecord(String name) throws IOException { Record record = TreeManager.TREE_SCHEMA.createRecord(treeTable.getKey()); record.setString(TreeManager.TREE_NAME_COL, name); @@ -62,9 +58,7 @@ class TreeDBAdapterV0 implements TreeDBAdapter { return record; } - /** - * @see ghidra.program.database.module.TreeDBAdapter#deleteRecord(long) - */ + @Override public boolean deleteRecord(long treeID) throws IOException { if (treeTable.deleteRecord(treeID)) { @@ -76,18 +70,14 @@ class TreeDBAdapterV0 implements TreeDBAdapter { return false; } - /** - * @see ghidra.program.database.module.TreeDBAdapter#getRecord(long) - */ + @Override public Record getRecord(long treeID) throws IOException { return treeTable.getRecord(treeID); } - /** - * @see ghidra.program.database.module.TreeDBAdapter#getRecord(java.lang.String) - */ + @Override public Record getRecord(String name) throws IOException { - long[] keys = treeTable.findRecords(new StringField(name), TreeManager.TREE_NAME_COL); + Field[] keys = treeTable.findRecords(new StringField(name), TreeManager.TREE_NAME_COL); if (keys.length == 0) { return null; } @@ -97,17 +87,12 @@ class TreeDBAdapterV0 implements TreeDBAdapter { return treeTable.getRecord(keys[0]); } - /** - * @see ghidra.program.database.module.TreeDBAdapter#getRecords() - */ + @Override public RecordIterator getRecords() throws IOException { return treeTable.iterator(); } - /** - * - * @see ghidra.program.database.module.TreeDBAdapter#updateRecord(ghidra.framework.store.db.Record) - */ + @Override public void updateRecord(Record record) throws IOException { treeTable.putRecord(record); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java index e46fcc4175..97159a8b8b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/module/TreeManager.java @@ -78,23 +78,23 @@ public class TreeManager implements ManagerDB { static final Schema PARENT_CHILD_SCHEMA = createParentChildSchema(); private static Schema createTreeSchema() { - return new Schema(0, "Key", new Class[] { StringField.class, LongField.class }, + return new Schema(0, "Key", new Field[] { StringField.INSTANCE, LongField.INSTANCE }, new String[] { "Name", "Modification Number" }); } private static Schema createModuleSchema() { - return new Schema(0, "Key", new Class[] { StringField.class, StringField.class }, + return new Schema(0, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE }, new String[] { "Name", "Comments" }); } private static Schema createFragmentSchema() { - return new Schema(0, "Key", new Class[] { StringField.class, StringField.class }, + return new Schema(0, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE }, new String[] { "Name", "Comments" }); } private static Schema createParentChildSchema() { return new Schema(0, "Key", - new Class[] { LongField.class, LongField.class, IntField.class }, + new Field[] { LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE }, new String[] { "Parent ID", "Child ID", "Child Index" }); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDBAdapterV1.java index 138990f010..2d8d26fca4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * @@ -41,9 +39,10 @@ class OldFunctionDBAdapterV1 extends OldFunctionDBAdapter { static final int V1_REPEATABLE_COMMENT_COL = 5; final static Schema V1_FUNCTIONS_SCHEMA = new Schema(SCHEMA_VERSION, "Entry Point", - new Class[] { LongField.class, IntField.class, IntField.class, IntField.class, - IntField.class, StringField.class }, new String[] { "Return DataType ID", "StackDepth", - "StackParamOffset", "StackReturnOffset", "StackLocalSize", "RepeatableComment" }); + new Field[] { LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, + IntField.INSTANCE, StringField.INSTANCE }, + new String[] { "Return DataType ID", "StackDepth", "StackParamOffset", "StackReturnOffset", + "StackLocalSize", "RepeatableComment" }); protected Table table; OldFunctionDBAdapterV1(DBHandle dbHandle, AddressMap addrMap) throws VersionException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDataDB.java index 364337b8f7..ae8ebd2234 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldFunctionDataDB.java @@ -15,6 +15,11 @@ */ package ghidra.program.database.oldfunction; +import java.io.IOException; +import java.util.*; + +import db.Field; +import db.Record; import ghidra.program.database.ProgramDB; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; @@ -27,11 +32,6 @@ import ghidra.util.Msg; import ghidra.util.StringUtilities; import ghidra.util.exception.InvalidInputException; -import java.io.IOException; -import java.util.*; - -import db.Record; - /** * */ @@ -50,8 +50,8 @@ class OldFunctionDataDB { private OldStackFrameDB frame; private List regParams; - OldFunctionDataDB(OldFunctionManager functionManager, AddressMap addrMap, - Record functionRecord, AddressSetView body) { + OldFunctionDataDB(OldFunctionManager functionManager, AddressMap addrMap, Record functionRecord, + AddressSetView body) { this.functionManager = functionManager; this.addrMap = addrMap; @@ -200,9 +200,9 @@ class OldFunctionDataDB { return; regParams = new ArrayList(); try { - long[] keys = registerAdapter.getRegisterVariableKeys(functionRecord.getKey()); + Field[] keys = registerAdapter.getRegisterVariableKeys(functionRecord.getKey()); for (int i = 0; i < keys.length; i++) { - Record varRec = registerAdapter.getRegisterVariableRecord(keys[i]); + Record varRec = registerAdapter.getRegisterVariableRecord(keys[i].getLongValue()); regParams.add(getRegisterParameter(varRec, i)); } // TODO Does register variable list need to be sorted? @@ -266,10 +266,9 @@ class OldFunctionDataDB { try { Variable[] stackParams = frame.getParameters(); for (int i = 0; i < stackParams.length; i++) { - parms[ordinal++] = - new OldFunctionParameter(stackParams[i].getName(), ordinal, - stackParams[i].getDataType(), stackParams[i].getVariableStorage(), program, - SourceType.USER_DEFINED); + parms[ordinal++] = new OldFunctionParameter(stackParams[i].getName(), ordinal, + stackParams[i].getDataType(), stackParams[i].getVariableStorage(), program, + SourceType.USER_DEFINED); } } catch (InvalidInputException e) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapter.java index daa39ab6af..090330f5d7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * Database adapter for register variables. @@ -60,9 +58,9 @@ abstract class OldRegisterVariableDBAdapter { /** * Get all register variable keys which correspond to a function. * @param functionKey - * @return array of register variable keys. - * @throws IOException + * @return array of register variable keys as LongField values within Field array. + * @throws IOException if IO error occurs */ - abstract long[] getRegisterVariableKeys(long functionKey) throws IOException; + abstract Field[] getRegisterVariableKeys(long functionKey) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapterV0.java index 54957d5a17..c340ac848e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldRegisterVariableDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * @@ -39,9 +37,9 @@ class OldRegisterVariableDBAdapterV0 extends OldRegisterVariableDBAdapter { static final String REG_PARMS_TABLE_NAME = "Register Parameters"; static final Schema V0_REG_PARAMS_SCHEMA = new Schema(SCHEMA_VERSION, "Key", - new Class[] { LongField.class, StringField.class, LongField.class, StringField.class, - StringField.class }, new String[] { "Function ID", "Register", "DataType ID", "Name", - "Comment" }); + new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE }, + new String[] { "Function ID", "Register", "DataType ID", "Name", "Comment" }); private Table table; @@ -52,34 +50,22 @@ class OldRegisterVariableDBAdapterV0 extends OldRegisterVariableDBAdapter { } } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getRegisterVariableRecord(long) - */ @Override public Record getRegisterVariableRecord(long key) throws IOException { return table.getRecord(key); } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getRegisterVariableKeys(long) - */ @Override - public long[] getRegisterVariableKeys(long functionKey) throws IOException { + public Field[] getRegisterVariableKeys(long functionKey) throws IOException { return table.findRecords(new LongField(functionKey), OldStackVariableDBAdapter.STACK_VAR_FUNCTION_KEY_COL); } - /** - * @see ghidra.program.database.function.RegisterVariableDBAdapter#deleteTable(db.DBHandle) - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(REG_PARMS_TABLE_NAME); } - /** - * @see ghidra.program.database.function.RegisterVariableDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return table.getRecordCount(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackFrameDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackFrameDB.java index 299fda631f..b40c6991f4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackFrameDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackFrameDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,11 @@ */ package ghidra.program.database.oldfunction; +import java.io.IOException; +import java.util.*; + +import db.Field; +import db.Record; import ghidra.program.model.address.AddressOutOfBoundsException; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.*; @@ -23,11 +27,6 @@ import ghidra.program.model.symbol.SourceType; import ghidra.util.Msg; import ghidra.util.exception.InvalidInputException; -import java.io.IOException; -import java.util.*; - -import db.Record; - /** * */ @@ -89,9 +88,9 @@ class OldStackFrameDB implements StackFrame { return; try { variables = new ArrayList(); - long[] keys = adapter.getStackVariableKeys(function.getKey()); + Field[] keys = adapter.getStackVariableKeys(function.getKey()); for (int i = 0; i < keys.length; i++) { - Record varRec = adapter.getStackVariableRecord(keys[i]); + Record varRec = adapter.getStackVariableRecord(keys[i].getLongValue()); variables.add(getStackVariable(varRec)); } Collections.sort(variables, StackVariableComparator.get()); @@ -124,13 +123,11 @@ class OldStackFrameDB implements StackFrame { throw new RuntimeException(e); // unexpected } catch (AddressOutOfBoundsException e) { - Msg.error(this, - "Invalid stack variable '" + name + "' in function at " + function.getEntryPoint() + - ": " + e.getMessage()); + Msg.error(this, "Invalid stack variable '" + name + "' in function at " + + function.getEntryPoint() + ": " + e.getMessage()); try { - var = - new LocalVariableImpl(name, 0, dataType, VariableStorage.BAD_STORAGE, - functionManager.getProgram()); + var = new LocalVariableImpl(name, 0, dataType, VariableStorage.BAD_STORAGE, + functionManager.getProgram()); } catch (InvalidInputException e1) { throw new RuntimeException(e); // unexpected diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapter.java index 2df78d7bcc..7b382f33d6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * Database adapter for stack variables. @@ -56,23 +54,26 @@ abstract class OldStackVariableDBAdapter { } /** - * + * Delete associated database table + * @param handle database handle + * @throws IOException if IO error occurs */ abstract void deleteTable(DBHandle handle) throws IOException; /** * Get a stack variable record. - * @param key - * @return Record + * @param key stack variable record + * @return Record record or null + * @throws IOException if IO error occurs */ abstract Record getStackVariableRecord(long key) throws IOException; /** * Get all stack variable keys which correspond to a function. - * @param functionKey - * @return array of stack variable keys. - * @throws IOException + * @param functionKey parent function ID + * @return array of stack variable keys as LongField values within Field array. + * @throws IOException if IO error occurs */ - abstract long[] getStackVariableKeys(long functionKey) throws IOException; + abstract Field[] getStackVariableKeys(long functionKey) throws IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV0.java index 97b2f5f891..e44a918a80 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * @@ -40,8 +38,9 @@ class OldStackVariableDBAdapterV0 extends OldStackVariableDBAdapter { static final int V0_STACK_VAR_NAME_COL = 3; static final int V0_STACK_VAR_COMMENT_COL = 4; - static final Schema V0_STACK_VARS_SCHEMA = new Schema(SCHEMA_VERSION, "Key", new Class[] { - LongField.class, IntField.class, LongField.class, StringField.class, StringField.class }, + static final Schema V0_STACK_VARS_SCHEMA = new Schema(SCHEMA_VERSION, "Key", + new Field[] { LongField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE }, new String[] { "Function ID", "Offset", "DataType ID", "Name", "Comment" }); private Table table; @@ -58,19 +57,13 @@ class OldStackVariableDBAdapterV0 extends OldStackVariableDBAdapter { } } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getStackVariableRecord(long) - */ @Override public Record getStackVariableRecord(long key) throws IOException { return translateRecord(table.getRecord(key)); } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getStackVariableKeys(long) - */ @Override - public long[] getStackVariableKeys(long functionKey) throws IOException { + public Field[] getStackVariableKeys(long functionKey) throws IOException { return table.findRecords(new LongField(functionKey), V0_STACK_VAR_FUNCTION_KEY_COL); } @@ -94,9 +87,6 @@ class OldStackVariableDBAdapterV0 extends OldStackVariableDBAdapter { return rec; } - /** - * @see ghidra.program.database.data.PointerDBAdapter#deleteTable() - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(STACK_VARS_TABLE_NAME); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV1.java index 1f4e0f762c..5d8e0dd74e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/oldfunction/OldStackVariableDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +15,11 @@ */ package ghidra.program.database.oldfunction; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.VersionException; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.VersionException; /** * @@ -41,11 +39,12 @@ class OldStackVariableDBAdapterV1 extends OldStackVariableDBAdapter { static final int V1_STACK_VAR_COMMENT_COL = 4; static final int V1_STACK_VAR_DT_LENGTH_COL = 5; - static final Schema V1_STACK_VARS_SCHEMA = new Schema(SCHEMA_VERSION, "Key", new Class[] { - LongField.class, IntField.class, LongField.class, StringField.class, StringField.class, - IntField.class }, + static final Schema V1_STACK_VARS_SCHEMA = new Schema(SCHEMA_VERSION, "Key", + new Field[] { LongField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE, + StringField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE }, - new String[] { "Function ID", "Offset", "DataType ID", "Name", "Comment", "DataType Length" }); + new String[] { "Function ID", "Offset", "DataType ID", "Name", "Comment", + "DataType Length" }); private Table table; @@ -64,25 +63,16 @@ class OldStackVariableDBAdapterV1 extends OldStackVariableDBAdapter { } } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getStackVariableRecord(long) - */ @Override public Record getStackVariableRecord(long key) throws IOException { return table.getRecord(key); } - /** - * @see ghidra.program.database.function.FunctionDBAdapter#getStackVariableKeys(long) - */ @Override - public long[] getStackVariableKeys(long functionKey) throws IOException { + public Field[] getStackVariableKeys(long functionKey) throws IOException { return table.findRecords(new LongField(functionKey), V1_STACK_VAR_FUNCTION_KEY_COL); } - /** - * @see ghidra.program.database.data.PointerDBAdapter#deleteTable() - */ @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(STACK_VARS_TABLE_NAME); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java index 2f1914fd76..0157a3ba7d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java @@ -68,8 +68,8 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { static { - PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.class, "Name", - new Class[] { ByteField.class, StringField.class, IntField.class }, + PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.INSTANCE, + "Name", new Field[] { ByteField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE }, new String[] { "Type", "Object Class", "Version" }); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java index 90c0fb6402..cc54bf74b7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,10 @@ */ package ghidra.program.database.properties; +import java.io.IOException; + +import db.*; +import db.util.ErrorHandler; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.util.IntPropertyMap; @@ -24,11 +27,6 @@ import ghidra.util.exception.*; import ghidra.util.prop.PropertyVisitor; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; -import db.util.ErrorHandler; - /** * Property manager that deals with properties that are of * int type and stored with a database table. @@ -58,6 +56,7 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { /** * @see ghidra.program.model.util.IntPropertyMap#add(ghidra.program.model.address.Address, int) */ + @Override public void add(Address addr, int value) { lock.acquire(); try { @@ -66,7 +65,7 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { long key = addrMap.getKey(addr, true); if (propertyTable == null) { - createTable(IntField.class); + createTable(IntField.INSTANCE); } else { oldValue = (Integer) cache.get(key); @@ -96,6 +95,7 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { /** * @see ghidra.program.model.util.IntPropertyMap#getInt(ghidra.program.model.address.Address) */ + @Override public int getInt(Address addr) throws NoValueException { if (propertyTable == null) { throw NO_VALUE_EXCEPTION; @@ -130,6 +130,7 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { /** * @see ghidra.program.model.util.PropertyMap#getObject(ghidra.program.model.address.Address) */ + @Override public Object getObject(Address addr) { try { return new Integer(getInt(addr)); @@ -142,6 +143,7 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { /** * @see ghidra.program.model.util.PropertyMap#applyValue(ghidra.util.prop.PropertyVisitor, ghidra.program.model.address.Address) */ + @Override public void applyValue(PropertyVisitor visitor, Address addr) { try { visitor.visit(getInt(addr)); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java index e411e5b4f6..bddf4a8566 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,10 @@ */ package ghidra.program.database.properties; +import java.io.IOException; + +import db.*; +import db.util.ErrorHandler; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.util.LongPropertyMap; @@ -24,11 +27,6 @@ import ghidra.util.exception.*; import ghidra.util.prop.PropertyVisitor; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; -import db.util.ErrorHandler; - /** * Property manager that deals with properties that are of * long type and stored with a database table. @@ -58,13 +56,14 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap /** * @see ghidra.program.model.util.LongPropertyMap#add(ghidra.program.model.address.Address, long) */ + @Override public void add(Address addr, long value) { Long oldValue = null; lock.acquire(); try { long key = addrMap.getKey(addr, true); if (propertyTable == null) { - createTable(LongField.class); + createTable(LongField.INSTANCE); } else { oldValue = (Long) cache.get(key); @@ -93,6 +92,7 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap /** * @see ghidra.program.model.util.LongPropertyMap#getLong(ghidra.program.model.address.Address) */ + @Override public long getLong(Address addr) throws NoValueException { if (propertyTable == null) { throw NO_VALUE_EXCEPTION; @@ -127,6 +127,7 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap /** * @see ghidra.program.model.util.PropertyMap#getObject(ghidra.program.model.address.Address) */ + @Override public Object getObject(Address addr) { try { return new Long(getLong(addr)); @@ -139,6 +140,7 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap /** * @see ghidra.program.model.util.PropertyMap#applyValue(ghidra.util.prop.PropertyVisitor, ghidra.program.model.address.Address) */ + @Override public void applyValue(PropertyVisitor visitor, Address addr) { throw new NotYetImplementedException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java index 6f5e1b4b79..b12735f93a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java @@ -42,7 +42,7 @@ public abstract class PropertyMapDB implements PropertyMap { protected static final String[] SCHEMA_FIELD_NAMES = new String[] { "Value" }; protected static final String[] NO_SCHEMA_FIELD_NAMES = new String[0]; - protected static final Class[] NO_SCHEMA_FIELD_CLASSES = new Class[0]; + protected static final Field[] NO_SCHEMA_FIELDS = new Field[0]; protected static final int PROPERTY_VALUE_COL = 0; @@ -92,8 +92,8 @@ public abstract class PropertyMapDB implements PropertyMap { } } - void checkMapVersion(int openMode, TaskMonitor monitor) throws VersionException, - CancelledException, IOException { + void checkMapVersion(int openMode, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { if (propertyTable != null && addrMap.isUpgraded()) { if (openMode == DBConstants.UPGRADE) { upgradeTable(monitor); @@ -175,15 +175,15 @@ public abstract class PropertyMapDB implements PropertyMap { * is null. * @throws IOException */ - protected void createTable(Class valueFieldClass) throws IOException { - if (valueFieldClass != null) { + protected void createTable(Field valueField) throws IOException { + if (valueField != null) { // Create default table schema with a value column and an long Address key - Class[] classes = new Class[] { valueFieldClass }; - schema = new Schema(0, "Address", classes, SCHEMA_FIELD_NAMES); + Field[] fields = new Field[] { valueField }; + schema = new Schema(0, "Address", fields, SCHEMA_FIELD_NAMES); } else { // Table contains only a long Address key - schema = new Schema(0, "Address", NO_SCHEMA_FIELD_CLASSES, NO_SCHEMA_FIELD_NAMES); + schema = new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES); } propertyTable = dbHandle.createTable(getTableName(), schema); } @@ -412,9 +412,8 @@ public abstract class PropertyMapDB implements PropertyMap { return null; } try { - AddressKeyIterator iter = - new AddressKeyIterator(propertyTable, addrMap, - addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false); + AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, + addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false); return addrMap.decodeAddress(iter.previous()); } catch (NoSuchElementException e) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java index 2eefabb775..5975fa017e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +15,18 @@ */ package ghidra.program.database.properties; -import ghidra.program.database.map.AddressMap; -import ghidra.program.model.address.Address; -import ghidra.program.model.util.StringPropertyMap; -import ghidra.program.util.ChangeManager; -import ghidra.util.exception.*; -import ghidra.util.prop.PropertyVisitor; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; import db.util.ErrorHandler; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.address.Address; +import ghidra.program.model.util.StringPropertyMap; +import ghidra.program.util.ChangeManager; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.prop.PropertyVisitor; +import ghidra.util.task.TaskMonitor; /** * Property manager that deals with properties that are of @@ -58,6 +57,7 @@ public class StringPropertyMapDB extends PropertyMapDB implements StringProperty /** * @see ghidra.program.model.util.StringPropertyMap#add(ghidra.program.model.address.Address, java.lang.String) */ + @Override public void add(Address addr, String value) { lock.acquire(); try { @@ -65,7 +65,7 @@ public class StringPropertyMapDB extends PropertyMapDB implements StringProperty String oldValue = null; if (propertyTable == null) { - createTable(StringField.class); + createTable(StringField.INSTANCE); } else { oldValue = (String) cache.get(key); @@ -94,6 +94,7 @@ public class StringPropertyMapDB extends PropertyMapDB implements StringProperty /** * @see ghidra.program.model.util.StringPropertyMap#getString(ghidra.program.model.address.Address) */ + @Override public String getString(Address addr) { if (propertyTable == null) { return null; @@ -132,6 +133,7 @@ public class StringPropertyMapDB extends PropertyMapDB implements StringProperty /** * @see ghidra.program.model.util.PropertyMap#getObject(ghidra.program.model.address.Address) */ + @Override public Object getObject(Address addr) { return getString(addr); } @@ -139,6 +141,7 @@ public class StringPropertyMapDB extends PropertyMapDB implements StringProperty /** * @see ghidra.program.model.util.PropertyMap#applyValue(ghidra.util.prop.PropertyVisitor, ghidra.program.model.address.Address) */ + @Override public void applyValue(PropertyVisitor visitor, Address addr) { String str = getString(addr); if (str != null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/BigRefListV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/BigRefListV0.java index a210e27441..0853a06646 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/BigRefListV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/BigRefListV0.java @@ -41,10 +41,10 @@ class BigRefListV0 extends RefList { private static final String BASE_TABLE_NAME = "BigRefList_"; - private static final Schema BIG_REFS_SCHEMA = new Schema(1, "RefID", new Class[] { - LongField.class, ByteField.class, ByteField.class, ByteField.class, LongField.class, - LongField.class }, new String[] { "Address", "Flags", "Type", "OpIndex", "SymbolID", - "Offset" }); + private static final Schema BIG_REFS_SCHEMA = new Schema(1, "RefID", + new Field[] { LongField.INSTANCE, ByteField.INSTANCE, ByteField.INSTANCE, + ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Address", "Flags", "Type", "OpIndex", "SymbolID", "Offset" }); private static int ADDRESS_COL = 0; private static int FLAGS_COL = 1; @@ -71,9 +71,8 @@ class BigRefListV0 extends RefList { DBObjectCache cache, boolean isFrom) throws IOException { super(addrMap.getKey(address, true), address, adapter, addrMap, program, cache, isFrom); record = ToAdapter.TO_REFS_SCHEMA.createRecord(key); - table = - program.getDBHandle().createTable(BASE_TABLE_NAME + Long.toHexString(key), - BIG_REFS_SCHEMA, new int[] { ADDRESS_COL }); + table = program.getDBHandle().createTable(BASE_TABLE_NAME + Long.toHexString(key), + BIG_REFS_SCHEMA, new int[] { ADDRESS_COL }); } /** @@ -95,8 +94,8 @@ class BigRefListV0 extends RefList { String tableName = BASE_TABLE_NAME + Long.toHexString(rec.getKey()); table = program.getDBHandle().getTable(tableName); if (table == null) { - throw new IOException("BigRefList table not found for " + address + " (" + tableName + - ")"); + throw new IOException( + "BigRefList table not found for " + address + " (" + tableName + ")"); } if (!isFrom) { refLevel = rec.getByteValue(ToAdapter.REF_LEVEL_COL); @@ -114,9 +113,6 @@ class BigRefListV0 extends RefList { return false; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#addRef(ghidra.program.model.address.Address, ghidra.program.model.address.Address, ghidra.program.model.symbol.RefType, boolean, int, long, boolean) - */ @Override void addRef(Address fromAddr, Address toAddr, RefType refType, int opIndex, long symbolID, boolean isPrimary, SourceType source, boolean isOffset, boolean isShift, @@ -165,8 +161,8 @@ class BigRefListV0 extends RefList { } appendRef(ref.getFromAddress(), ref.getToAddress(), ref.getOperandIndex(), - ref.getReferenceType(), ref.getSource(), isPrimary, symbolID, isOffset, - isShifted, offsetOrShift); + ref.getReferenceType(), ref.getSource(), isPrimary, symbolID, isOffset, isShifted, + offsetOrShift); } updateRecord(); @@ -239,9 +235,6 @@ class BigRefListV0 extends RefList { symbolID); } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getAllRefs() - */ @Override synchronized Reference[] getAllRefs() throws IOException { Reference[] refs = new Reference[getNumRefs()]; @@ -252,17 +245,11 @@ class BigRefListV0 extends RefList { return refs; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getNumRefs() - */ @Override int getNumRefs() { return table.getRecordCount(); } - /* - * @see ghidra.program.database.references.RefList#hasReference(int) - */ @Override boolean hasReference(int opIndex) throws IOException { if (!isFrom) { @@ -278,9 +265,6 @@ class BigRefListV0 extends RefList { return false; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getPrimaryRef() - */ @Override synchronized Reference getPrimaryRef(int opIndex) throws IOException { if (!isFrom) { @@ -300,13 +284,10 @@ class BigRefListV0 extends RefList { return null; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getRef(ghidra.program.model.address.Address, int) - */ @Override synchronized ReferenceDB getRef(Address refAddress, int opIndex) throws IOException { LongField addrField = new LongField(addrMap.getKey(refAddress, false)); - for (long id : table.findRecords(addrField, ADDRESS_COL)) { + for (Field id : table.findRecords(addrField, ADDRESS_COL)) { Record rec = table.getRecord(id); if (rec.getByteValue(OPINDEX_COL) == (byte) opIndex) { return getRef(rec); @@ -315,33 +296,21 @@ class BigRefListV0 extends RefList { return null; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getRefs() - */ @Override synchronized ReferenceIterator getRefs() throws IOException { return new RefIterator(); } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#isEmpty() - */ @Override boolean isEmpty() { return table == null || table.getRecordCount() == 0; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#getReferenceLevel() - */ @Override byte getReferenceLevel() { return refLevel; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#removeAll() - */ @Override synchronized void removeAll() throws IOException { table.deleteAll(); @@ -352,13 +321,10 @@ class BigRefListV0 extends RefList { setInvalid(); } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#removeRef(ghidra.program.model.symbol.MemReference) - */ @Override synchronized boolean removeRef(Address deleteAddr, int opIndex) throws IOException { LongField addrField = new LongField(addrMap.getKey(deleteAddr, false)); - for (long id : table.findRecords(addrField, ADDRESS_COL)) { + for (Field id : table.findRecords(addrField, ADDRESS_COL)) { Record rec = table.getRecord(id); if (rec.getByteValue(OPINDEX_COL) == (byte) opIndex) { table.deleteRecord(id); @@ -397,15 +363,12 @@ class BigRefListV0 extends RefList { return maxLevel; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#setPrimary(ghidra.program.model.symbol.MemReference, boolean) - */ @Override synchronized boolean setPrimary(Reference ref, boolean isPrimary) throws IOException { int opIndex = ref.getOperandIndex(); Address changeAddr = isFrom ? ref.getToAddress() : ref.getFromAddress(); LongField addrField = new LongField(addrMap.getKey(changeAddr, false)); - for (long id : table.findRecords(addrField, ADDRESS_COL)) { + for (Field id : table.findRecords(addrField, ADDRESS_COL)) { Record rec = table.getRecord(id); if (rec.getByteValue(OPINDEX_COL) == (byte) opIndex) { RefListFlagsV0 flags = new RefListFlagsV0(rec.getByteValue(FLAGS_COL)); @@ -421,16 +384,13 @@ class BigRefListV0 extends RefList { return false; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#setSymbolID(ghidra.program.model.symbol.MemReference, long) - */ @Override synchronized boolean setSymbolID(Reference ref, long symbolID) throws IOException { boolean hasSymbolID = symbolID >= 0; int opIndex = ref.getOperandIndex(); Address changeAddr = isFrom ? ref.getToAddress() : ref.getFromAddress(); LongField addrField = new LongField(addrMap.getKey(changeAddr, false)); - for (long id : table.findRecords(addrField, ADDRESS_COL)) { + for (Field id : table.findRecords(addrField, ADDRESS_COL)) { Record rec = table.getRecord(id); if (rec.getByteValue(OPINDEX_COL) == (byte) opIndex) { RefListFlagsV0 flags = new RefListFlagsV0(rec.getByteValue(FLAGS_COL)); @@ -447,9 +407,6 @@ class BigRefListV0 extends RefList { return false; } - /* (non-Javadoc) - * @see ghidra.program.database.references.RefList#updateRefType(ghidra.program.model.address.Address, int, ghidra.program.model.symbol.RefType) - */ @Override synchronized void updateRefType(Address changeAddr, int opIndex, RefType refType) throws IOException { @@ -459,7 +416,7 @@ class BigRefListV0 extends RefList { updateRefLevel = (newLevel != refLevel); } LongField addrField = new LongField(addrMap.getKey(changeAddr, false)); - for (long id : table.findRecords(addrField, ADDRESS_COL)) { + for (Field id : table.findRecords(addrField, ADDRESS_COL)) { Record rec = table.getRecord(id); if (rec.getByteValue(OPINDEX_COL) == (byte) opIndex) { if (refType.getValue() == rec.getByteValue(TYPE_COL)) { @@ -515,9 +472,6 @@ class BigRefListV0 extends RefList { recIter = table.iterator(); } - /* (non-Javadoc) - * @see ghidra.program.model.symbol.MemReferenceIterator#hasNext() - */ @Override public boolean hasNext() { try { @@ -529,9 +483,6 @@ class BigRefListV0 extends RefList { return false; } - /* (non-Javadoc) - * @see ghidra.program.model.symbol.MemReferenceIterator#next() - */ @Override public Reference next() { try { @@ -545,9 +496,6 @@ class BigRefListV0 extends RefList { return null; } - /** - * @see java.util.Iterator#remove() - */ @Override public void remove() { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/FromAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/FromAdapter.java index 6fad83fdab..909bdde2f7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/FromAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/FromAdapter.java @@ -32,15 +32,16 @@ abstract class FromAdapter implements RecordAdapter { static final String FROM_REFS_TABLE_NAME = "FROM REFS"; - static final Schema FROM_REFS_SCHEMA = new Schema(0, "From Address", new Class[] { - IntField.class, BinaryField.class }, new String[] { "Number of Refs", "Ref Data" }); + static final Schema FROM_REFS_SCHEMA = + new Schema(0, "From Address", new Field[] { IntField.INSTANCE, BinaryField.INSTANCE }, + new String[] { "Number of Refs", "Ref Data" }); static final int REF_COUNT_COL = 0; static final int REF_DATA_COL = 1; static FromAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap, - ErrorHandler errHandler, TaskMonitor monitor) throws VersionException, - CancelledException, IOException { + ErrorHandler errHandler, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { if (openMode == DBConstants.CREATE) { return new FromAdapterV0(dbHandle, true, addrMap, errHandler); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OldStackRefDBAdpater.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OldStackRefDBAdpater.java index 0529d3aa1a..f59a7e6e61 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OldStackRefDBAdpater.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/OldStackRefDBAdpater.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.references; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Adapter for the stack references table in the database. @@ -31,9 +29,10 @@ class OldStackRefDBAdpater { static final String STACK_REF_TABLE_NAME = "Stack References"; - static final Schema STACK_REF_SCHEMA = new Schema(0, "Key", new Class[] { LongField.class, - ShortField.class, BooleanField.class, ShortField.class }, new String[] { "From Address", - "Op Index", "User Defined", "Stack Offset" }); + static final Schema STACK_REF_SCHEMA = new Schema(0, "Key", + new Field[] { LongField.INSTANCE, ShortField.INSTANCE, BooleanField.INSTANCE, + ShortField.INSTANCE }, + new String[] { "From Address", "Op Index", "User Defined", "Stack Offset" }); static final int FROM_ADDR_COL = 0; static final int OP_INDEX_COL = 1; @@ -72,8 +71,8 @@ class OldStackRefDBAdpater { return refTable.getRecordCount(); } - private void moveTable(DBHandle handle, TaskMonitor monitor) throws IOException, - CancelledException { + private void moveTable(DBHandle handle, TaskMonitor monitor) + throws IOException, CancelledException { DBHandle tmpHandle = handle.getScratchPad(); Table newRefTable = tmpHandle.createTable(STACK_REF_TABLE_NAME, STACK_REF_SCHEMA); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ToAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ToAdapter.java index 1fbd28958d..f13671a7be 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ToAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/references/ToAdapter.java @@ -38,17 +38,17 @@ abstract class ToAdapter implements RecordAdapter { static final String TO_REFS_TABLE_NAME = "TO REFS"; static final int CURRENT_VERSION = 1; - static final Schema TO_REFS_SCHEMA = new Schema(CURRENT_VERSION, "To Address", new Class[] { - IntField.class, BinaryField.class, ByteField.class }, new String[] { "Number of Refs", - "Ref Data", "Ref Level" }); + static final Schema TO_REFS_SCHEMA = new Schema(CURRENT_VERSION, "To Address", + new Field[] { IntField.INSTANCE, BinaryField.INSTANCE, ByteField.INSTANCE }, + new String[] { "Number of Refs", "Ref Data", "Ref Level" }); static final int REF_COUNT_COL = 0; static final int REF_DATA_COL = 1; static final int REF_LEVEL_COL = 2; static ToAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap, - ErrorHandler errHandler, TaskMonitor monitor) throws VersionException, - CancelledException, IOException { + ErrorHandler errHandler, TaskMonitor monitor) + throws VersionException, CancelledException, IOException { if (openMode == DBConstants.CREATE) { return new ToAdapterV1(dbHandle, true, addrMap, errHandler); @@ -91,8 +91,8 @@ abstract class ToAdapter implements RecordAdapter { } private static ToAdapter upgrade(DBHandle dbHandle, ToAdapter oldAdapter, AddressMap addrMap, - ErrorHandler errHandler, TaskMonitor monitor) throws VersionException, IOException, - CancelledException { + ErrorHandler errHandler, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); DBHandle tmpHandle = new DBHandle(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/DatabaseRangeMapAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/DatabaseRangeMapAdapter.java index 517df1bf9a..e6bb350571 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/DatabaseRangeMapAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/DatabaseRangeMapAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,8 @@ */ package ghidra.program.database.register; +import db.*; +import db.util.ErrorHandler; import ghidra.program.database.map.AddressMap; import ghidra.program.database.util.AddressRangeMapDB; import ghidra.program.model.address.*; @@ -26,14 +27,12 @@ import ghidra.program.util.RangeMapAdapter; import ghidra.util.Lock; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import db.*; -import db.util.ErrorHandler; public class DatabaseRangeMapAdapter implements RangeMapAdapter { static final String NAME_PREFIX = "Register_"; - static final String CONTEXT_TABLE_PREFIX = AddressRangeMapDB.RANGE_MAP_TABLE_PREFIX + - NAME_PREFIX; + static final String CONTEXT_TABLE_PREFIX = + AddressRangeMapDB.RANGE_MAP_TABLE_PREFIX + NAME_PREFIX; private String mapName; private ErrorHandler errorHandler; @@ -46,9 +45,8 @@ public class DatabaseRangeMapAdapter implements RangeMapAdapter { this.dbh = dbHandle; this.errorHandler = errorHandler; mapName = NAME_PREFIX + register.getName(); - rangeMap = - new AddressRangeMapDB(dbHandle, addrMap, lock, mapName, errorHandler, - BinaryField.class, false); + rangeMap = new AddressRangeMapDB(dbHandle, addrMap, lock, mapName, errorHandler, + BinaryField.INSTANCE, false); addressMap = addrMap; } @@ -152,9 +150,8 @@ public class DatabaseRangeMapAdapter implements RangeMapAdapter { while (AddressRangeMapDB.exists(dbh, tempName)) { tempName = "TEMP_MAP" + (++retry); } - tempMap = - new AddressRangeMapDB(dbh, addressMap, new Lock("Test"), tempName, errorHandler, - BinaryField.class, false); + tempMap = new AddressRangeMapDB(dbh, addressMap, new Lock("Test"), tempName, + errorHandler, BinaryField.INSTANCE, false); // Translate range map data into tempMap monitor.initialize(rangeMap.getRecordCount()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java index 0cdd516b43..03ae2f5960 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/register/OldProgramContextDB.java @@ -87,9 +87,8 @@ public class OldProgramContextDB implements ProgramContext, DefaultProgramContex baseContextRegister = language.getContextBaseRegister(); if (baseContextRegister == null) { - baseContextRegister = - new Register("DEFAULT_CONTEXT", "DEFAULT_CONTEXT", - addrMap.getAddressFactory().getRegisterSpace().getAddress(0x0), 4, true, 0); + baseContextRegister = new Register("DEFAULT_CONTEXT", "DEFAULT_CONTEXT", + addrMap.getAddressFactory().getRegisterSpace().getAddress(0x0), 4, true, 0); } defaultDisassemblyContext = new RegisterValue(baseContextRegister); @@ -298,9 +297,8 @@ public class OldProgramContextDB implements ProgramContext, DefaultProgramContex RegisterValueStore store = defaultRegisterValueMap.get(baseRegister); if (store == null) { RangeMapAdapter adapter = new InMemoryRangeMapAdapter(); - store = - new RegisterValueStore(registerValue.getRegister().getBaseRegister(), adapter, - false); + store = new RegisterValueStore(registerValue.getRegister().getBaseRegister(), adapter, + false); defaultRegisterValueMap.put(baseRegister, store); } store.setValue(start, end, registerValue); @@ -373,9 +371,8 @@ public class OldProgramContextDB implements ProgramContext, DefaultProgramContex private AddressRangeMapDB createMap(int offset) { lock.acquire(); try { - AddressRangeMapDB map = - new AddressRangeMapDB(dbHandle, addrMap, lock, "ProgContext" + offset, errHandler, - ByteField.class, false); + AddressRangeMapDB map = new AddressRangeMapDB(dbHandle, addrMap, lock, + "ProgContext" + offset, errHandler, ByteField.INSTANCE, false); valueMaps.put(offset, map); return map; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationDBAdapter.java index ed61e8aefb..a3c8c2940b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/reloc/RelocationDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.reloc; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - abstract class RelocationDBAdapter { final static int TYPE_COL = 0; @@ -35,13 +33,10 @@ abstract class RelocationDBAdapter { final static String TABLE_NAME = "Relocations"; - //@formatter:off - final static Schema SCHEMA = new Schema(RelocationDBAdapterV4.VERSION, "Address", - new Class[] { - IntField.class, BinaryField.class, BinaryField.class, StringField.class }, - new String[] { - "Type", "Values", "Bytes", "Symbol Name" }); - //@formatter:on + final static Schema SCHEMA = new Schema( + RelocationDBAdapterV4.VERSION, "Address", new Field[] { IntField.INSTANCE, + BinaryField.INSTANCE, BinaryField.INSTANCE, StringField.INSTANCE }, + new String[] { "Type", "Values", "Bytes", "Symbol Name" }); static RelocationDBAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap, TaskMonitor monitor) throws VersionException, IOException { @@ -93,8 +88,8 @@ abstract class RelocationDBAdapter { } private static RelocationDBAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - RelocationDBAdapter oldAdapter, TaskMonitor monitor) throws VersionException, - IOException { + RelocationDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapter.java index d3fdcf2e5b..b5e8b92bf3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +15,12 @@ */ package ghidra.program.database.symbol; -import ghidra.util.exception.NotFoundException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.util.exception.NotFoundException; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * @@ -34,8 +32,9 @@ abstract class EquateDBAdapter { final static String EQUATES_TABLE_NAME = "Equates"; - static final Schema EQUATES_SCHEMA = new Schema(0, "Key", new Class[] { StringField.class, - LongField.class }, new String[] { "Equate Name", "Equate Value" }); + static final Schema EQUATES_SCHEMA = + new Schema(0, "Key", new Field[] { StringField.INSTANCE, LongField.INSTANCE }, + new String[] { "Equate Name", "Equate Value" }); final static int NAME_COL = 0; final static int VALUE_COL = 1; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapterV0.java index 5e0b131fc8..652311c814 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateDBAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +15,10 @@ */ package ghidra.program.database.symbol; -import ghidra.util.exception.*; - import java.io.IOException; import db.*; +import ghidra.util.exception.*; /** * Implementation for Version 0 of the adapter that accesses the @@ -74,20 +72,20 @@ class EquateDBAdapterV0 extends EquateDBAdapter { */ @Override long getRecordKey(String name) throws IOException, NotFoundException { - long[] keys = equateTable.findRecords(new StringField(name), NAME_COL); + Field[] keys = equateTable.findRecords(new StringField(name), NAME_COL); if (keys.length == 0) { throw new NotFoundException("Equate named " + name + " was not found"); } if (keys.length > 1) { - throw new AssertException("Expected one record for " + name + " but found " + - keys.length); + throw new AssertException( + "Expected one record for " + name + " but found " + keys.length); } - return keys[0]; + return keys[0].getLongValue(); } @Override boolean hasRecord(String name) throws IOException { - long[] keys = equateTable.findRecords(new StringField(name), NAME_COL); + Field[] keys = equateTable.findRecords(new StringField(name), NAME_COL); return keys.length > 0; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java index 8dd3c86f42..9d3e47300f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateManager.java @@ -142,9 +142,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { if (refAddr == AddressMap.INVALID_ADDRESS_KEY) { return null; } - long[] keys = refAdapter.getRecordKeysForAddr(refAddr); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForAddr(refAddr); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); if (ref.getOpIndex() == opIndex) { EquateDB equate = getEquateDB(ref.getEquateID()); if (equate.getValue() == scalarValue) { @@ -171,9 +171,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { if (refAddr == AddressMap.INVALID_ADDRESS_KEY) { return ret; } - long[] keys = refAdapter.getRecordKeysForAddr(refAddr); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForAddr(refAddr); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); if (ref.getOpIndex() == opIndex) { ret.add(getEquateDB(ref.getEquateID())); } @@ -197,9 +197,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { if (refAddr == AddressMap.INVALID_ADDRESS_KEY) { return ret; } - long[] keys = refAdapter.getRecordKeysForAddr(refAddr); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForAddr(refAddr); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); ret.add(getEquateDB(ref.getEquateID())); } } @@ -322,9 +322,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { throw new CancelledException(); } Address addr = iter.next(); - long[] keys = refAdapter.getRecordKeysForAddr(addrMap.getKey(addr, false)); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForAddr(addrMap.getKey(addr, false)); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); list.add(ref); } } @@ -416,9 +416,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { long addr = addrMap.getKey(address, true); // first remove reference for address and opIndex - long[] keys = refAdapter.getRecordKeysForAddr(addr); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForAddr(addr); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); if (dynamicHash != 0) { if (ref.getDynamicHashValue() == dynamicHash) { removeRef(equateDB, ref); @@ -442,10 +442,10 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { } EquateRefDB[] getReferences(long equateID) throws IOException { - long[] keys = refAdapter.getRecordKeysForEquateID(equateID); + Field[] keys = refAdapter.getRecordKeysForEquateID(equateID); EquateRefDB[] refs = new EquateRefDB[keys.length]; for (int i = 0; i < keys.length; i++) { - refs[i] = getEquateRefDB(keys[i]); + refs[i] = getEquateRefDB(keys[i].getLongValue()); } return refs; } @@ -456,9 +456,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { void removeReference(EquateDB equateDB, Address refAddr, short opIndex) throws IOException { - long[] keys = refAdapter.getRecordKeysForEquateID(equateDB.getKey()); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForEquateID(equateDB.getKey()); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); if (ref.getOpIndex() == opIndex && ref.getAddress().equals(refAddr)) { removeRef(equateDB, ref); break; @@ -468,9 +468,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { void removeReference(EquateDB equateDB, long dynamicHash, Address refAddr) throws IOException { - long[] keys = refAdapter.getRecordKeysForEquateID(equateDB.getKey()); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForEquateID(equateDB.getKey()); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); if (ref.getDynamicHashValue() == dynamicHash && ref.getAddress().equals(refAddr)) { removeRef(equateDB, ref); break; @@ -566,9 +566,9 @@ public class EquateManager implements EquateTable, ErrorHandler, ManagerDB { private void removeReferences(long equateID) throws IOException { EquateDB equateDB = getEquateDB(equateID); - long[] keys = refAdapter.getRecordKeysForEquateID(equateID); - for (long key : keys) { - EquateRefDB ref = getEquateRefDB(key); + Field[] keys = refAdapter.getRecordKeysForEquateID(equateID); + for (Field key : keys) { + EquateRefDB ref = getEquateRefDB(key.getLongValue()); removeRef(equateDB, ref); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapter.java index 4fdf3fc463..8811371b53 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,9 @@ */ package ghidra.program.database.symbol; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; @@ -23,10 +25,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Adapter to access records in the equate references table. * @@ -36,9 +34,10 @@ abstract class EquateRefDBAdapter { static final String EQUATE_REFS_TABLE_NAME = "Equate References"; - static final Schema REFS_SCHEMA = new Schema(1, "Key", new Class[] { LongField.class, - LongField.class, ShortField.class, LongField.class }, new String[] { "Equate ID", - "Equate Reference", "Operand Index", "Varnode Hash" }); + static final Schema REFS_SCHEMA = new Schema(1, "Key", + new Field[] { LongField.INSTANCE, LongField.INSTANCE, ShortField.INSTANCE, + LongField.INSTANCE }, + new String[] { "Equate ID", "Equate Reference", "Operand Index", "Varnode Hash" }); static final int EQUATE_ID_COL = 0; static final int ADDR_COL = 1; @@ -84,8 +83,8 @@ abstract class EquateRefDBAdapter { } private static EquateRefDBAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - EquateRefDBAdapter oldAdapter, TaskMonitor monitor) throws VersionException, - IOException { + EquateRefDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); @@ -156,7 +155,7 @@ abstract class EquateRefDBAdapter { * @param addr the address to find equates for. * @throws IOException if there was a problem accessing the database */ - abstract long[] getRecordKeysForAddr(long addr) throws IOException; + abstract Field[] getRecordKeysForAddr(long addr) throws IOException; /** * Update the table with the given record. @@ -169,7 +168,7 @@ abstract class EquateRefDBAdapter { * Get the records that have the given equateID. * @throws IOException if there was a problem accessing the database */ - abstract long[] getRecordKeysForEquateID(long equateID) throws IOException; + abstract Field[] getRecordKeysForEquateID(long equateID) throws IOException; /** * Get an iterator over the addresses. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV0.java index 2ce54725ad..02fe7644ef 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV0.java @@ -75,7 +75,7 @@ class EquateRefDBAdapterV0 extends EquateRefDBAdapter { * @see ghidra.program.database.symbol.EquateRefDBAdapter#getRecordKeysFrom(long) */ @Override - long[] getRecordKeysForAddr(long addr) throws IOException { + Field[] getRecordKeysForAddr(long addr) throws IOException { return refTable.findRecords(new LongField(addr), ADDR_COL); } @@ -91,7 +91,7 @@ class EquateRefDBAdapterV0 extends EquateRefDBAdapter { * @see ghidra.program.database.symbol.EquateRefDBAdapter#getRecordsForEquateID(long) */ @Override - long[] getRecordKeysForEquateID(long equateID) throws IOException { + Field[] getRecordKeysForEquateID(long equateID) throws IOException { return refTable.findRecords(new LongField(equateID), EQUATE_ID_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV1.java index 199326e363..aef81c502e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/EquateRefDBAdapterV1.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,9 @@ */ package ghidra.program.database.symbol; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.AddressIndexKeyIterator; import ghidra.program.database.map.AddressMap; import ghidra.program.database.util.DatabaseTableUtils; @@ -25,10 +27,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Implementation for Version 0 of the equate references table. * @@ -43,13 +41,12 @@ class EquateRefDBAdapterV1 extends EquateRefDBAdapter { * Constructor * */ - EquateRefDBAdapterV1(DBHandle handle, AddressMap addrMap, boolean create) throws IOException, - VersionException { + EquateRefDBAdapterV1(DBHandle handle, AddressMap addrMap, boolean create) + throws IOException, VersionException { this.addrMap = addrMap; if (create) { - refTable = - handle.createTable(EQUATE_REFS_TABLE_NAME, REFS_SCHEMA, new int[] { EQUATE_ID_COL, - ADDR_COL }); + refTable = handle.createTable(EQUATE_REFS_TABLE_NAME, REFS_SCHEMA, + new int[] { EQUATE_ID_COL, ADDR_COL }); } else { refTable = handle.getTable(EQUATE_REFS_TABLE_NAME); @@ -93,7 +90,7 @@ class EquateRefDBAdapterV1 extends EquateRefDBAdapter { * @see ghidra.program.database.symbol.EquateRefDBAdapter#getRecordKeysFrom(long) */ @Override - long[] getRecordKeysForAddr(long addr) throws IOException { + Field[] getRecordKeysForAddr(long addr) throws IOException { return refTable.findRecords(new LongField(addr), ADDR_COL); } @@ -109,7 +106,7 @@ class EquateRefDBAdapterV1 extends EquateRefDBAdapter { * @see ghidra.program.database.symbol.EquateRefDBAdapter#getRecordsForEquateID(long) */ @Override - long[] getRecordKeysForEquateID(long equateID) throws IOException { + Field[] getRecordKeysForEquateID(long equateID) throws IOException { return refTable.findRecords(new LongField(equateID), EQUATE_ID_COL); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapter.java index 2e7a96f31d..c16da7da9c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +15,16 @@ */ package ghidra.program.database.symbol; +import java.io.IOException; +import java.util.Set; + +import db.*; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.Set; - -import db.*; - /** * Adapter for the Label History table. */ @@ -34,9 +32,10 @@ abstract class LabelHistoryAdapter { static final String LABEL_HISTORY_TABLE_NAME = "Label History"; - static final Schema LABEL_HISTORY_SCHEMA = new Schema(0, "Key", new Class[] { LongField.class, - ByteField.class, StringField.class, StringField.class, LongField.class }, new String[] { - "Address", "Action", "Labels", "User", "Date" }); + static final Schema LABEL_HISTORY_SCHEMA = new Schema(0, "Key", + new Field[] { LongField.INSTANCE, ByteField.INSTANCE, StringField.INSTANCE, + StringField.INSTANCE, LongField.INSTANCE }, + new String[] { "Address", "Action", "Labels", "User", "Date" }); static final int HISTORY_ADDR_COL = 0; static final int HISTORY_ACTION_COL = 1; @@ -139,7 +138,7 @@ abstract class LabelHistoryAdapter { * @throws IOException if there was a problem accessing the database */ abstract void deleteAddressRange(Address startAddr, Address endAddr, AddressMap addrMap, - Set
doNotDeleteSet, TaskMonitor monitor) throws CancelledException, - IOException; + Set
doNotDeleteSet, TaskMonitor monitor) + throws CancelledException, IOException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapterV0.java index c3ec5fc263..21a6a3cb61 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LabelHistoryAdapterV0.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,11 @@ */ package ghidra.program.database.symbol; +import java.io.IOException; +import java.util.Date; +import java.util.Set; + +import db.*; import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressRecordDeleter; import ghidra.program.database.util.DatabaseTableUtils; @@ -26,12 +30,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.Date; -import java.util.Set; - -import db.*; - /** * Version 0 of the Label History adapter. */ @@ -50,9 +48,8 @@ class LabelHistoryAdapterV0 extends LabelHistoryAdapter { LabelHistoryAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException { if (create) { - table = - handle.createTable(LABEL_HISTORY_TABLE_NAME, LABEL_HISTORY_SCHEMA, - new int[] { HISTORY_ADDR_COL }); + table = handle.createTable(LABEL_HISTORY_TABLE_NAME, LABEL_HISTORY_SCHEMA, + new int[] { HISTORY_ADDR_COL }); } else { table = handle.getTable(LABEL_HISTORY_TABLE_NAME); @@ -67,8 +64,8 @@ class LabelHistoryAdapterV0 extends LabelHistoryAdapter { } static LabelHistoryAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - LabelHistoryAdapter oldAdapter, TaskMonitor monitor) throws VersionException, - IOException, CancelledException { + LabelHistoryAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); @@ -159,8 +156,8 @@ class LabelHistoryAdapterV0 extends LabelHistoryAdapter { */ @Override void moveAddress(long oldAddr, long newAddr) throws IOException { - long[] keys = table.findRecords(new LongField(oldAddr), HISTORY_ADDR_COL); - for (long key : keys) { + Field[] keys = table.findRecords(new LongField(oldAddr), HISTORY_ADDR_COL); + for (Field key : keys) { Record rec = table.getRecord(key); rec.setLongValue(HISTORY_ADDR_COL, newAddr); table.putRecord(rec); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java index 8b484a630d..23ea929584 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceManager.java @@ -68,7 +68,7 @@ public class NamespaceManager implements ManagerDB { } namespaceMap = new AddressRangeMapDB(handle, addrMap, lock, NAMESPACE_MAP_NAME, errHandler, - LongField.class, true); + LongField.INSTANCE, true); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapter.java deleted file mode 100644 index 0246e5a558..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapter.java +++ /dev/null @@ -1,109 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.symbol; - -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - -import java.io.IOException; - -import db.*; - -abstract class OldVariableStorageDBAdapter { - - static final String VARIABLE_STORAGE_TABLE_NAME = "VariableStorage"; - - static final Schema VARIABLE_STORAGE_SCHEMA = new Schema(1, "Key", new Class[] { - LongField.class, LongField.class, IntField.class }, new String[] { "Address", - "NamespaceID", "SymCount" }); - - static final int STORAGE_ADDR_COL = 0; - static final int NAMESPACE_ID_COL = 1; - static final int SYMBOL_COUNT_COL = 2; - - boolean upgradeOldVariableAddressesRequired = false; // if true, must upgrade register and stack symbol addresses - - /** - * Gets a new VariableDatabaseAdapter - * @param dbHandle the database handle. - * @param openMode the openmode - * @param monitor the progress monitor. - * @throws VersionException if the database table does not match the adapter. - * @throws CancelledException if the user cancels an upgrade. - * @throws IOException if a database io error occurs. - */ - static OldVariableStorageDBAdapter getAdapter(DBHandle dbHandle, int openMode, - TaskMonitor monitor) throws VersionException, IOException, CancelledException { - - if (openMode == DBConstants.CREATE) { - return new OldVariableStorageDBAdapterV1(dbHandle, true); - } - - try { - OldVariableStorageDBAdapter adapter = - new OldVariableStorageDBAdapterV1(dbHandle, false); - return adapter; - } - catch (VersionException e) { - if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { - throw e; - } - OldVariableStorageDBAdapter adapter = findReadOnlyAdapter(dbHandle); - if (openMode == DBConstants.UPGRADE) { - if (adapter == null) { - adapter = new OldVariableStorageDBAdapterV1(dbHandle, true); - adapter.upgradeOldVariableAddressesRequired = true; - } - else { - adapter = OldVariableStorageDBAdapterV1.upgrade(dbHandle, adapter, monitor); - } - } - else if (adapter == null) { - throw e; // upgrade required - read-only mode not supported - } - return adapter; - } - } - - static void deleteTable(DBHandle dbHandle) throws IOException { - dbHandle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); - } - - private static OldVariableStorageDBAdapter findReadOnlyAdapter(DBHandle dbHandle) - throws VersionException { - if (dbHandle.getTable(VARIABLE_STORAGE_TABLE_NAME) == null) { - return null; - } - return new OldVariableStorageDBAdapterV0(dbHandle); - } - - abstract void updateRecord(Record record) throws IOException; - - abstract Record getRecord(long key) throws IOException; - - abstract Record[] getRecordsForNamespace(long longValue) throws IOException; - - abstract long getNextStorageID(); - - abstract void deleteRecord(long key) throws IOException; - - abstract RecordIterator getRecords() throws IOException; - - abstract int getRecordCount(); - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0.java deleted file mode 100644 index 428cf89801..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0.java +++ /dev/null @@ -1,101 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.symbol; - -import ghidra.util.exception.VersionException; - -import java.io.IOException; - -import db.*; - -public class OldVariableStorageDBAdapterV0 extends OldVariableStorageDBAdapter { - - private static final int TABLE_VERSION = 0; - private Table variableStorageTable; - - OldVariableStorageDBAdapterV0(DBHandle handle) throws VersionException { - variableStorageTable = handle.getTable(VARIABLE_STORAGE_TABLE_NAME); - if (variableStorageTable == null || - variableStorageTable.getSchema().getVersion() != TABLE_VERSION) { - throw new VersionException(); - } - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getNextStorageID() - */ - @Override - long getNextStorageID() { - throw new UnsupportedOperationException(); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#deleteRecord(long) - */ - @Override - void deleteRecord(long key) throws IOException { - throw new UnsupportedOperationException(); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecord(long) - */ - @Override - Record getRecord(long key) throws IOException { - return variableStorageTable.getRecord(key); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordsForNamespace(long) - */ - @Override - Record[] getRecordsForNamespace(long namespaceID) throws IOException { - long[] keys = - variableStorageTable.findRecords(new LongField(namespaceID), NAMESPACE_ID_COL); - Record[] records = new Record[keys.length]; - for (int i = 0; i < keys.length; i++) { - records[i] = variableStorageTable.getRecord(keys[i]); - } - return records; - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#updateRecord(db.Record) - */ - @Override - void updateRecord(Record record) throws IOException { - throw new UnsupportedOperationException(); - } - - /** - * @throws IOException - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecords() - */ - @Override - RecordIterator getRecords() throws IOException { - return variableStorageTable.iterator(); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordCount() - */ - @Override - int getRecordCount() { - return variableStorageTable.getRecordCount(); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0V1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0V1.java new file mode 100644 index 0000000000..319bc5934d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV0V1.java @@ -0,0 +1,74 @@ +/* ### + * 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.symbol; + +import java.io.IOException; + +import db.*; + +/** + * OldVariableStorageDBAdapterV0V1 provide legacy variable storage + * table support where each variable storage record was namespace-specific and + * provided storage address only. In a later revision this was deemed inadequate + * since size information and support for storage binding was needed. + */ +class OldVariableStorageDBAdapterV0V1 { + + static final String VARIABLE_STORAGE_TABLE_NAME = "VariableStorage"; + + static final Schema VARIABLE_STORAGE_SCHEMA = new Schema(1, "Key", + new Field[] { LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE }, + new String[] { "Address", "NamespaceID", "SymCount" }); + + static final int STORAGE_ADDR_COL = 0; + static final int NAMESPACE_ID_COL = 1; + static final int SYMBOL_COUNT_COL = 2; + + private Table variableStorageTable; + + /** + * Construction legacy variable storage adapter. The old variable storage + * table must exist (see {@link #VARIABLE_STORAGE_TABLE_NAME}). + * @param handle database handle + * @throws IOException if VariableStorage table is missing or invalid schema version detected + */ + OldVariableStorageDBAdapterV0V1(DBHandle handle) throws IOException { + + variableStorageTable = handle.getTable(VARIABLE_STORAGE_TABLE_NAME); + if (variableStorageTable == null) { + throw new IOException("No such table: " + VARIABLE_STORAGE_TABLE_NAME); + } + int version = variableStorageTable.getSchema().getVersion(); + if (version != 0 && version != 1) { + throw new IOException("No such table schema version: " + version); + } + } + + Record getRecord(long key) throws IOException { + return variableStorageTable.getRecord(key); + } + + Record[] getRecordsForNamespace(long namespaceID) throws IOException { + Field[] keys = + variableStorageTable.findRecords(new LongField(namespaceID), NAMESPACE_ID_COL); + Record[] records = new Record[keys.length]; + for (int i = 0; i < keys.length; i++) { + records[i] = variableStorageTable.getRecord(keys[i]); + } + return records; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV1.java deleted file mode 100644 index b3fa158c26..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageDBAdapterV1.java +++ /dev/null @@ -1,165 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.symbol; - -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; - -import java.io.IOException; - -import db.*; - -public class OldVariableStorageDBAdapterV1 extends OldVariableStorageDBAdapter { - - private static final int TABLE_VERSION = 1; - private Table variableStorageTable; - - OldVariableStorageDBAdapterV1(DBHandle handle, boolean create) throws VersionException, - IOException { - - if (create) { - variableStorageTable = - handle.createTable(VARIABLE_STORAGE_TABLE_NAME, VARIABLE_STORAGE_SCHEMA, - new int[] { NAMESPACE_ID_COL }); - } - else { - variableStorageTable = handle.getTable(VARIABLE_STORAGE_TABLE_NAME); - if (variableStorageTable == null) { - throw new VersionException(VersionException.OLDER_VERSION, true); - } - int version = variableStorageTable.getSchema().getVersion(); - if (version < TABLE_VERSION) { - throw new VersionException(VersionException.OLDER_VERSION, true); - } - if (version > TABLE_VERSION) { - throw new VersionException(VersionException.NEWER_VERSION, false); - } - } - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getNextStorageID() - */ - @Override - long getNextStorageID() { - long nextKey = variableStorageTable.getMaxKey() + 1; - if (nextKey <= 0) { - nextKey = 1; - } - return nextKey; - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#deleteRecord(long) - */ - @Override - void deleteRecord(long key) throws IOException { - variableStorageTable.deleteRecord(key); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecord(long) - */ - @Override - Record getRecord(long key) throws IOException { - return variableStorageTable.getRecord(key); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordsForNamespace(long) - */ - @Override - Record[] getRecordsForNamespace(long namespaceID) throws IOException { - long[] keys = - variableStorageTable.findRecords(new LongField(namespaceID), NAMESPACE_ID_COL); - Record[] records = new Record[keys.length]; - for (int i = 0; i < keys.length; i++) { - records[i] = variableStorageTable.getRecord(keys[i]); - } - return records; - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#updateRecord(db.Record) - */ - @Override - void updateRecord(Record record) throws IOException { - variableStorageTable.putRecord(record); - } - - /** - * @throws IOException - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecords() - */ - @Override - RecordIterator getRecords() throws IOException { - return variableStorageTable.iterator(); - } - - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordCount() - */ - @Override - int getRecordCount() { - return variableStorageTable.getRecordCount(); - } - - public static OldVariableStorageDBAdapter upgrade(DBHandle dbHandle, - OldVariableStorageDBAdapter oldAdapter, TaskMonitor monitor) throws IOException, - CancelledException { - DBHandle tmpHandle = dbHandle.getScratchPad(); - try { - - monitor.setMessage("Upgrading Variable Storage Table..."); - monitor.setMaximum((oldAdapter.getRecordCount()) * 2); - int count = 0; - - OldVariableStorageDBAdapterV1 tmpAdapter = - new OldVariableStorageDBAdapterV1(tmpHandle, true); - RecordIterator iter = oldAdapter.getRecords(); - while (iter.hasNext()) { - monitor.checkCanceled(); - Record rec = iter.next(); - Record newRec = VARIABLE_STORAGE_SCHEMA.createRecord(rec.getKey()); - newRec.setLongValue(STORAGE_ADDR_COL, rec.getLongValue(STORAGE_ADDR_COL)); - newRec.setLongValue(NAMESPACE_ID_COL, rec.getLongValue(NAMESPACE_ID_COL)); - newRec.setIntValue(SYMBOL_COUNT_COL, rec.getIntValue(SYMBOL_COUNT_COL)); - tmpAdapter.updateRecord(newRec); - monitor.setProgress(++count); - } - - dbHandle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); - OldVariableStorageDBAdapterV1 newAdapter = - new OldVariableStorageDBAdapterV1(dbHandle, true); - - iter = tmpAdapter.getRecords(); - while (iter.hasNext()) { - monitor.checkCanceled(); - newAdapter.updateRecord(iter.next()); - monitor.setProgress(++count); - } - return newAdapter; - } - catch (VersionException e) { - throw new AssertException(); - } - finally { - tmpHandle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); - } - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageManagerDB.java index 576b7ccd26..fe266f5cbf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/OldVariableStorageManagerDB.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,30 +15,22 @@ */ package ghidra.program.database.symbol; -import ghidra.program.database.ManagerDB; -import ghidra.program.database.ProgramDB; -import ghidra.program.database.map.AddressMap; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.lang.Register; -import ghidra.program.util.LanguageTranslator; -import ghidra.util.Lock; -import ghidra.util.exception.CancelledException; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import java.util.Hashtable; -import db.*; +import db.DBHandle; +import db.Record; +import ghidra.program.database.map.AddressMap; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; -public class OldVariableStorageManagerDB implements ManagerDB { +public class OldVariableStorageManagerDB { - private ProgramDB program; private AddressMap addrMap; - private Lock lock; - private OldVariableStorageDBAdapter adapter; + private OldVariableStorageDBAdapterV0V1 adapter; private DBHandle handle; private long lastNamespaceCacheID; // ID of cached namespace variables @@ -49,53 +40,31 @@ public class OldVariableStorageManagerDB implements ManagerDB { new Hashtable(); /** - * Construct a new variable manager. + * Construct a read-only variable storage manager for the old record format + * utilized by the VariableStorage table (NOTE: old table name does not have + * a space in the name). This adapter is intended for use during upgrades + * only. * @param handle the database handle. * @param addrMap the address map - * @param openMode the open mode - * @param lock the program synchronization lock * @param monitor the task monitor. * @throws IOException if a database error occurs. - * @throws VersionException if the table version is different from this adapter. - * @throws IOException * @throws CancelledException if the user cancels the upgrade. */ - public OldVariableStorageManagerDB(DBHandle handle, AddressMap addrMap, int openMode, - Lock lock, TaskMonitor monitor) throws VersionException, IOException, - CancelledException { + public OldVariableStorageManagerDB(DBHandle handle, AddressMap addrMap, TaskMonitor monitor) + throws IOException, CancelledException { this.addrMap = addrMap; - this.lock = lock; this.handle = handle; - adapter = OldVariableStorageDBAdapter.getAdapter(handle, openMode, monitor); - + adapter = new OldVariableStorageDBAdapterV0V1(handle); } - public static boolean isOldVariableStorageManagerUpgradeRequired(DBHandle handle) { - return handle.getTable(OldVariableStorageDBAdapter.VARIABLE_STORAGE_TABLE_NAME) != null; - } - - @Override - public void invalidateCache(boolean all) { - lastNamespaceCacheID = -1; - variableAddrLookupCache.clear(); - storageAddrLookupCache.clear(); - } - - @Override - public void programReady(int openMode, int currentRevision, TaskMonitor monitor) - throws IOException, CancelledException { - } - - @Override - public void setProgram(ProgramDB program) { - this.program = program; + static boolean isOldVariableStorageManagerUpgradeRequired(DBHandle handle) { + return handle.getTable(OldVariableStorageDBAdapterV0V1.VARIABLE_STORAGE_TABLE_NAME) != null; } void deleteTable() throws IOException { - handle.deleteTable(OldVariableStorageDBAdapter.VARIABLE_STORAGE_TABLE_NAME); - invalidateCache(true); + handle.deleteTable(OldVariableStorageDBAdapterV0V1.VARIABLE_STORAGE_TABLE_NAME); } private void cacheNamespaceStorage(long namespaceID) throws IOException { @@ -124,38 +93,13 @@ public class OldVariableStorageManagerDB implements ManagerDB { if (rec == null) { return null; } - cacheNamespaceStorage(rec.getLongValue(OldVariableStorageDBAdapter.NAMESPACE_ID_COL)); + cacheNamespaceStorage(rec.getLongValue(OldVariableStorageDBAdapterV0V1.NAMESPACE_ID_COL)); return variableAddrLookupCache.get(variableAddr); } public Address getStorageAddress(Address variableAddr) throws IOException { - lock.acquire(); - try { - OldVariableStorage varStore = getVariableStorage(variableAddr); - if (varStore != null) { - return varStore.storageAddr; - } - } - finally { - lock.release(); - } - return null; - } - - public boolean isUpgradeOldVariableAddressesRequired() { - return adapter.upgradeOldVariableAddressesRequired; - } - - @Override - public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) - throws CancelledException { - throw new UnsupportedOperationException(); - } - - @Override - public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) - throws CancelledException { - throw new UnsupportedOperationException(); + OldVariableStorage varStore = getVariableStorage(variableAddr); + return varStore != null ? varStore.storageAddr : null; } private class OldVariableStorage { @@ -164,8 +108,8 @@ public class OldVariableStorageManagerDB implements ManagerDB { private OldVariableStorage(Record record) { this.variableAddr = AddressSpace.VARIABLE_SPACE.getAddress(record.getKey()); - this.storageAddr = - addrMap.decodeAddress(record.getLongValue(OldVariableStorageDBAdapter.STORAGE_ADDR_COL)); + this.storageAddr = addrMap.decodeAddress( + record.getLongValue(OldVariableStorageDBAdapterV0V1.STORAGE_ADDR_COL)); } @Override @@ -182,46 +126,4 @@ public class OldVariableStorageManagerDB implements ManagerDB { } } - /** - * Update storage locations to reflect register changes resulting from - * a new/updated language. Programs address map must already be updated - * to reflect new language. - * @param translator - * @param monitor - * @throws CancelledException - * @throws IOException - */ - public void setLanguage(LanguageTranslator translator, TaskMonitor monitor) - throws CancelledException, IOException { - - monitor.initialize(adapter.getRecordCount()); - int cnt = 0; - - lock.acquire(); - try { - RecordIterator recIter = adapter.getRecords(); - while (recIter.hasNext()) { - monitor.checkCanceled(); - Record rec = recIter.next(); - // NOTE: addrMap has already been switched-over to new language and its address spaces - Address storageAddr = - addrMap.decodeAddress(rec.getLongValue(OldVariableStorageDBAdapter.STORAGE_ADDR_COL)); - Register oldReg = translator.getOldRegister(storageAddr, 0); - if (oldReg != null) { - Register newReg = translator.getNewRegister(oldReg); - if (newReg != null) { - rec.setLongValue(OldVariableStorageDBAdapter.STORAGE_ADDR_COL, - addrMap.getKey(newReg.getAddress(), true)); - adapter.updateRecord(rec); - } - } - monitor.setProgress(++cnt); - } - } - finally { - invalidateCache(true); - lock.release(); - } - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapter.java index 8b69a566cb..3ea9921999 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapter.java @@ -36,8 +36,9 @@ abstract class SymbolDatabaseAdapter { static final String SYMBOL_TABLE_NAME = "Symbols"; static final Schema SYMBOL_SCHEMA = new Schema(2, "Key", - new Class[] { StringField.class, LongField.class, LongField.class, ByteField.class, - LongField.class, IntField.class, StringField.class, ByteField.class }, + new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + ByteField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, StringField.INSTANCE, + ByteField.INSTANCE }, new String[] { "Name", "Address", "Parent", "Symbol Type", "SymbolData1", "SymbolData2", "SymbolData3", "Flags" }); @@ -161,10 +162,10 @@ abstract class SymbolDatabaseAdapter { /** * Get the symbolIDs at the given address. * @param addr address to filter on - * @return array of database keys + * @return array of database LongField keys contained within a Field array. * @throws IOException if there was a problem accessing the database */ - abstract long[] getSymbolIDs(Address addr) throws IOException; + abstract Field[] getSymbolIDs(Address addr) throws IOException; /** * Get the number of symbols. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java index 7f04c65702..65a9fe9211 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV0.java @@ -18,6 +18,10 @@ */ package ghidra.program.database.symbol; +import java.io.IOException; +import java.util.Set; + +import db.*; import ghidra.program.database.map.AddressIndexPrimaryKeyIterator; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; @@ -26,11 +30,6 @@ import ghidra.program.model.symbol.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.Set; - -import db.*; - /** * SymbolDatabaseAdapterV0 handles symbol tables which were created * prior to the addition of Namespace support and Function symbols. Function symbols @@ -81,8 +80,8 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { } } - long extractLocalSymbols(DBHandle handle, TaskMonitor monitor) throws IOException, - CancelledException { + long extractLocalSymbols(DBHandle handle, TaskMonitor monitor) + throws IOException, CancelledException { monitor.setMessage("Extracting Local and Dynamic Symbols..."); monitor.initialize(symbolTable.getRecordCount()); @@ -129,84 +128,54 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#removeSymbol(long) - */ @Override void removeSymbol(long symbolID) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#hasSymbol(ghidra.program.model.address.Address) - */ @Override boolean hasSymbol(Address addr) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolIDs(ghidra.program.model.address.Address) - */ @Override - long[] getSymbolIDs(Address addr) throws IOException { + Field[] getSymbolIDs(Address addr) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolRecord(long) - */ @Override Record getSymbolRecord(long symbolID) throws IOException { return convertRecord(symbolTable.getRecord(symbolID)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolCount() - */ @Override int getSymbolCount() { return symbolTable.getRecordCount(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress() - */ @Override RecordIterator getSymbolsByAddress(boolean forward) throws IOException { return new V0ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, V0_SYMBOL_ADDR_COL, addrMap, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress(ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException { - return new V0ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, - new AddressIndexPrimaryKeyIterator(symbolTable, V0_SYMBOL_ADDR_COL, addrMap, startAddr, - forward))); + return new V0ConvertedRecordIterator( + new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, + V0_SYMBOL_ADDR_COL, addrMap, startAddr, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#updateSymbolRecord(ghidra.framework.store.db.Record) - */ @Override void updateSymbolRecord(Record record) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols() - */ @Override RecordIterator getSymbols() throws IOException { return new V0ConvertedRecordIterator(symbolTable.iterator()); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols(ghidra.program.model.address.Address, ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException { @@ -214,47 +183,32 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { throw new UnsupportedOperationException(); //TODO: Is there any reason we need to support reverse symbol iteration ??? // Yes, to search text backwards! - return new V0ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, - new AddressIndexPrimaryKeyIterator(symbolTable, V0_SYMBOL_ADDR_COL, addrMap, start, - end, forward))); + return new V0ConvertedRecordIterator( + new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, + V0_SYMBOL_ADDR_COL, addrMap, start, end, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteExternalEntries(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ void deleteExternalEntries(Address start, Address end) { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#moveAddress(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override void moveAddress(Address oldAddr, Address newAddr) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.LabelHistoryAdapter#moveAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, long, ghidra.util.task.TaskMonitor) - */ @Override void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) throws CancelledException, IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor) - */ @Override Set
deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) throws CancelledException, IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByNamespace(long) - */ @Override RecordIterator getSymbolsByNamespace(long id) throws IOException { @@ -264,14 +218,11 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { return null; } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByName(java.lang.String) - */ @Override RecordIterator getSymbolsByName(String name) throws IOException { StringField val = new StringField(name); - return new V0ConvertedRecordIterator(symbolTable.indexIterator(V0_SYMBOL_NAME_COL, val, - val, true)); + return new V0ConvertedRecordIterator( + symbolTable.indexIterator(V0_SYMBOL_NAME_COL, val, val, true)); } private class V0ConvertedRecordIterator implements RecordIterator { @@ -288,9 +239,6 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { this.symIter = symIter; } - /* - * @see db.RecordIterator#hasNext() - */ @Override public boolean hasNext() throws IOException { if (rec == null) { @@ -305,17 +253,11 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { return rec != null; } - /* - * @see db.RecordIterator#hasPrevious() - */ @Override public boolean hasPrevious() throws IOException { throw new UnsupportedOperationException(); } - /* - * @see db.RecordIterator#next() - */ @Override public Record next() throws IOException { if (hasNext()) { @@ -326,17 +268,11 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { return null; } - /* - * @see db.RecordIterator#previous() - */ @Override public Record previous() throws IOException { throw new UnsupportedOperationException(); } - /* - * @see db.RecordIterator#delete() - */ @Override public boolean delete() throws IOException { throw new UnsupportedOperationException(); @@ -344,17 +280,11 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter { } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getTable() - */ @Override Table getTable() { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getMaxSymbolAddress(ghidra.program.model.address.AddressSpace) - */ @Override Address getMaxSymbolAddress(AddressSpace space) throws IOException { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java index eb9c08eda9..0b864ed040 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV1.java @@ -78,17 +78,11 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#removeSymbol(long) - */ @Override void removeSymbol(long symbolID) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#hasSymbol(ghidra.program.model.address.Address) - */ @Override boolean hasSymbol(Address addr) throws IOException { long key = addrMap.getKey(addr, false); @@ -98,21 +92,15 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter { return symbolTable.hasRecord(new LongField(key), V1_SYMBOL_ADDR_COL); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolIDs(ghidra.program.model.address.Address) - */ @Override - long[] getSymbolIDs(Address addr) throws IOException { + Field[] getSymbolIDs(Address addr) throws IOException { long key = addrMap.getKey(addr, false); if (key == AddressMap.INVALID_ADDRESS_KEY) { - return new long[0]; + return Field.EMPTY_ARRAY; } return symbolTable.findRecords(new LongField(key), V1_SYMBOL_ADDR_COL); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolRecord(long) - */ @Override Record getSymbolRecord(long symbolID) throws IOException { return convertV1Record(symbolTable.getRecord(symbolID)); @@ -154,118 +142,79 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter { return rec; } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolCount() - */ @Override int getSymbolCount() { return symbolTable.getRecordCount(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress() - */ @Override RecordIterator getSymbolsByAddress(boolean forward) throws IOException { return new V1ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, V1_SYMBOL_ADDR_COL, addrMap, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress(ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException { - return new V1ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, - new AddressIndexPrimaryKeyIterator(symbolTable, V1_SYMBOL_ADDR_COL, addrMap, startAddr, - forward))); + return new V1ConvertedRecordIterator( + new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, + V1_SYMBOL_ADDR_COL, addrMap, startAddr, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#updateSymbolRecord(ghidra.framework.store.db.Record) - */ @Override void updateSymbolRecord(Record record) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols() - */ @Override RecordIterator getSymbols() throws IOException { return new V1ConvertedRecordIterator(symbolTable.iterator()); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols(ghidra.program.model.address.Address, ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException { - return new V1ConvertedRecordIterator(new KeyToRecordIterator(symbolTable, - new AddressIndexPrimaryKeyIterator(symbolTable, V1_SYMBOL_ADDR_COL, addrMap, start, - end, forward))); + return new V1ConvertedRecordIterator( + new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, + V1_SYMBOL_ADDR_COL, addrMap, start, end, forward))); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByName(boolean) - */ RecordIterator getSymbolsByName() throws IOException { return new V1ConvertedRecordIterator(symbolTable.indexIterator(V1_SYMBOL_NAME_COL)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteExternalEntries(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ void deleteExternalEntries(Address start, Address end) throws IOException { AddressRecordDeleter.deleteRecords(symbolTable, V1_SYMBOL_ADDR_COL, addrMap, start, end, null); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#moveAddress(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override void moveAddress(Address oldAddr, Address newAddr) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.LabelHistoryAdapter#moveAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, long, ghidra.util.task.TaskMonitor) - */ @Override void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) throws CancelledException, IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor) - */ @Override Set
deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) throws CancelledException, IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByNamespace(long) - */ @Override RecordIterator getSymbolsByNamespace(long id) throws IOException { LongField field = new LongField(id); - return new V1ConvertedRecordIterator(symbolTable.indexIterator(V1_SYMBOL_PARENT_COL, field, - field, true)); + return new V1ConvertedRecordIterator( + symbolTable.indexIterator(V1_SYMBOL_PARENT_COL, field, field, true)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByName(java.lang.String) - */ @Override RecordIterator getSymbolsByName(String name) throws IOException { StringField field = new StringField(name); - return new V1ConvertedRecordIterator(symbolTable.indexIterator(V1_SYMBOL_NAME_COL, field, - field, true)); + return new V1ConvertedRecordIterator( + symbolTable.indexIterator(V1_SYMBOL_NAME_COL, field, field, true)); } private class V1ConvertedRecordIterator extends ConvertedRecordIterator { @@ -280,17 +229,11 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter { } } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getTable() - */ @Override Table getTable() { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getMaxSymbolAddress(ghidra.program.model.address.AddressSpace) - */ @Override Address getMaxSymbolAddress(AddressSpace space) throws IOException { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV2.java index 058ddb0609..6ae8c48913 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolDatabaseAdapterV2.java @@ -44,9 +44,8 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { this.addrMap = addrMap; if (create) { - symbolTable = - handle.createTable(SYMBOL_TABLE_NAME, SYMBOL_SCHEMA, new int[] { SYMBOL_ADDR_COL, - SYMBOL_NAME_COL, SYMBOL_PARENT_COL }); + symbolTable = handle.createTable(SYMBOL_TABLE_NAME, SYMBOL_SCHEMA, + new int[] { SYMBOL_ADDR_COL, SYMBOL_NAME_COL, SYMBOL_PARENT_COL }); } else { symbolTable = handle.getTable(SYMBOL_TABLE_NAME); @@ -64,8 +63,8 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { } static SymbolDatabaseAdapter upgrade(DBHandle dbHandle, AddressMap addrMap, - SymbolDatabaseAdapter oldAdapter, TaskMonitor monitor) throws VersionException, - IOException, CancelledException { + SymbolDatabaseAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { AddressMap oldAddrMap = addrMap.getOldAddressMap(); @@ -211,17 +210,11 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { return rec; } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#removeSymbol(long) - */ @Override void removeSymbol(long symbolID) throws IOException { symbolTable.deleteRecord(symbolID); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#hasSymbol(ghidra.program.model.address.Address) - */ @Override boolean hasSymbol(Address addr) throws IOException { long key = addrMap.getKey(addr, false); @@ -231,102 +224,69 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { return symbolTable.hasRecord(new LongField(key), SYMBOL_ADDR_COL); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolIDs(ghidra.program.model.address.Address) - */ @Override - long[] getSymbolIDs(Address addr) throws IOException { + Field[] getSymbolIDs(Address addr) throws IOException { long key = addrMap.getKey(addr, false); if (key == AddressMap.INVALID_ADDRESS_KEY && !addr.equals(Address.NO_ADDRESS)) { - return new long[0]; + return Field.EMPTY_ARRAY; } return symbolTable.findRecords(new LongField(key), SYMBOL_ADDR_COL); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolRecord(long) - */ @Override Record getSymbolRecord(long symbolID) throws IOException { return symbolTable.getRecord(symbolID); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolCount() - */ @Override int getSymbolCount() { return symbolTable.getRecordCount(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress() - */ @Override RecordIterator getSymbolsByAddress(boolean forward) throws IOException { - return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, - SYMBOL_ADDR_COL, addrMap, forward)); + return new KeyToRecordIterator(symbolTable, + new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, forward)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByAddress(ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException { return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, startAddr, forward)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#updateSymbolRecord(ghidra.framework.store.db.Record) - */ @Override void updateSymbolRecord(Record record) throws IOException { symbolTable.putRecord(record); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols() - */ @Override RecordIterator getSymbols() throws IOException { return symbolTable.iterator(); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbols(ghidra.program.model.address.Address, ghidra.program.model.address.Address, boolean) - */ @Override RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException { return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, start, end, forward)); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteExternalEntries(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ void deleteExternalEntries(Address start, Address end) throws IOException { AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, start, end, null); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#moveAddress(ghidra.program.model.address.Address, ghidra.program.model.address.Address) - */ @Override void moveAddress(Address oldAddr, Address newAddr) throws IOException { LongField oldKey = new LongField(addrMap.getKey(oldAddr, false)); long newKey = addrMap.getKey(newAddr, true); - long[] keys = symbolTable.findRecords(oldKey, SYMBOL_ADDR_COL); - for (long key : keys) { + Field[] keys = symbolTable.findRecords(oldKey, SYMBOL_ADDR_COL); + for (Field key : keys) { Record rec = symbolTable.getRecord(key); rec.setLongValue(SYMBOL_ADDR_COL, newKey); symbolTable.putRecord(rec); } } - /** - * @see ghidra.program.database.symbol.LabelHistoryAdapter#moveAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, long, ghidra.util.task.TaskMonitor) - */ @Override void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) throws CancelledException, IOException { @@ -335,9 +295,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { fromAddr, toAddr, length, null, monitor); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#deleteAddressRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor) - */ @Override Set
deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) throws CancelledException, IOException { @@ -369,33 +326,23 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { } } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByNamespace(long) - */ @Override RecordIterator getSymbolsByNamespace(long id) throws IOException { LongField field = new LongField(id); return symbolTable.indexIterator(SYMBOL_PARENT_COL, field, field, true); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getSymbolsByName(java.lang.String) - */ @Override RecordIterator getSymbolsByName(String name) throws IOException { StringField field = new StringField(name); return symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true); } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getMaxSymbolAddress(ghidra.program.model.address.AddressSpace) - */ @Override Address getMaxSymbolAddress(AddressSpace space) throws IOException { if (space.isMemorySpace()) { - AddressIndexKeyIterator addressKeyIterator = - new AddressIndexKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, - space.getMinAddress(), space.getMaxAddress(), false); + AddressIndexKeyIterator addressKeyIterator = new AddressIndexKeyIterator(symbolTable, + SYMBOL_ADDR_COL, addrMap, space.getMinAddress(), space.getMaxAddress(), false); if (addressKeyIterator.hasNext()) { return addrMap.decodeAddress(addressKeyIterator.next()); } @@ -415,9 +362,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter { return null; } - /** - * @see ghidra.program.database.symbol.SymbolDatabaseAdapter#getTable() - */ @Override Table getTable() { return symbolTable; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java index a48aaaa891..66dd6472e9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/SymbolManager.java @@ -48,9 +48,9 @@ public class SymbolManager implements SymbolTable, ManagerDB { private static final int OLD_SYMBOL_ADDR_COL = 0; private static final int OLD_SYMBOL_NAME_COL = 1; private static final int OLD_SYMBOL_IS_PRIMARY_COL = 2; - private static final Schema OLD_LOCAL_SYMBOLS_SCHEMA = - new Schema(0, "ID", new Class[] { LongField.class, StringField.class, BooleanField.class }, - new String[] { "OldAddress", "Name", "IsPrimary" }); + private static final Schema OLD_LOCAL_SYMBOLS_SCHEMA = new Schema(0, "ID", + new Field[] { LongField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE }, + new String[] { "OldAddress", "Name", "IsPrimary" }); static final String OLD_EXTERNAL_ENTRY_TABLE_NAME = "External Entries"; @@ -97,9 +97,10 @@ public class SymbolManager implements SymbolTable, ManagerDB { cache = new DBObjectCache<>(100); variableStorageMgr = new VariableStorageManagerDB(handle, addrMap, openMode, lock, monitor); - if (OldVariableStorageManagerDB.isOldVariableStorageManagerUpgradeRequired(handle)) { - oldVariableStorageMgr = - new OldVariableStorageManagerDB(handle, addrMap, openMode, lock, monitor); + + if (openMode == DBConstants.UPGRADE && + OldVariableStorageManagerDB.isOldVariableStorageManagerUpgradeRequired(handle)) { + oldVariableStorageMgr = new OldVariableStorageManagerDB(handle, addrMap, monitor); } } @@ -139,18 +140,12 @@ public class SymbolManager implements SymbolTable, ManagerDB { refManager = (ReferenceDBManager) program.getReferenceManager(); namespaceMgr = program.getNamespaceManager(); variableStorageMgr.setProgram(program); - if (oldVariableStorageMgr != null) { - oldVariableStorageMgr.setProgram(program); - } } @Override public void programReady(int openMode, int currentRevision, TaskMonitor monitor) throws IOException, CancelledException { - if (oldVariableStorageMgr != null) { - oldVariableStorageMgr.programReady(openMode, currentRevision, monitor); - } if (openMode == DBConstants.UPGRADE) { processOldLocalSymbols(monitor); processOldExternalEntryPoints(monitor); @@ -168,12 +163,9 @@ public class SymbolManager implements SymbolTable, ManagerDB { } if (oldVariableStorageMgr != null) { - if (oldVariableStorageMgr.isUpgradeOldVariableAddressesRequired()) { - processOldVariableAddresses(monitor); - } - else { - migrateFromOldVariableStorageManager(monitor); - } + // migrate from old variable storage table which utilized namespace-specific + // storage addresses + migrateFromOldVariableStorageManager(monitor); } else if (currentRevision == ProgramDB.COMPOUND_VARIABLE_STORAGE_ADDED_VERSION) { // Revised (2nd) VariableStorageManager was already added but we may have forgotten @@ -818,7 +810,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { public Symbol[] getSymbols(Address addr) { lock.acquire(); try { - long[] symbolIDs = adapter.getSymbolIDs(addr); + Field[] symbolIDs = adapter.getSymbolIDs(addr); if (symbolIDs.length == 0) { if (addr.isMemoryAddress() && refManager.hasReferencesTo(addr)) { Symbol[] symbols = new SymbolDB[1]; @@ -830,7 +822,7 @@ public class SymbolManager implements SymbolTable, ManagerDB { int primarySymbolIndex = 0; Symbol[] symbols = new Symbol[symbolIDs.length]; for (int i = 0; i < symbols.length; i++) { - symbols[i] = getSymbol(symbolIDs[i]); + symbols[i] = getSymbol(symbolIDs[i].getLongValue()); // NOTE: Primary symbol concept only applies to in memory symbols if (addr.isMemoryAddress() && i != 0 && symbols[i].isPrimary()) { primarySymbolIndex = i; @@ -858,14 +850,14 @@ public class SymbolManager implements SymbolTable, ManagerDB { public Symbol[] getUserSymbols(Address addr) { lock.acquire(); try { - long[] symbolIDs = adapter.getSymbolIDs(addr); + Field[] symbolIDs = adapter.getSymbolIDs(addr); if (symbolIDs.length == 0) { return NO_SYMBOLS; } Symbol[] symbols = new Symbol[symbolIDs.length]; for (int i = 0; i < symbols.length; i++) { - symbols[i] = getSymbol(symbolIDs[i]); + symbols[i] = getSymbol(symbolIDs[i].getLongValue()); } return symbols; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java index 6fe1cda4a4..a78a334f68 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapter.java @@ -15,20 +15,20 @@ */ package ghidra.program.database.symbol; -import ghidra.program.database.map.AddressMap; -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import db.*; +import ghidra.program.database.map.AddressMap; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; abstract class VariableStorageDBAdapter { static final String VARIABLE_STORAGE_TABLE_NAME = "Variable Storage"; - static final Schema VARIABLE_STORAGE_SCHEMA = new Schema(2, "Key", new Class[] { - LongField.class, StringField.class }, new String[] { "Hash", "Storage" }); + static final Schema VARIABLE_STORAGE_SCHEMA = + new Schema(2, "Key", new Field[] { LongField.INSTANCE, StringField.INSTANCE }, + new String[] { "Hash", "Storage" }); static final int HASH_COL = 0; static final int STORAGE_COL = 1; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java index 5d37ef8138..98b63d6ede 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterNoTable.java @@ -26,9 +26,6 @@ public class VariableStorageDBAdapterNoTable extends VariableStorageDBAdapter { VariableStorageDBAdapterNoTable() { } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getNextStorageID() - */ @Override long getNextStorageID() { throw new UnsupportedOperationException(); @@ -39,42 +36,26 @@ public class VariableStorageDBAdapterNoTable extends VariableStorageDBAdapter { return -1; } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#deleteRecord(long) - */ @Override void deleteRecord(long key) throws IOException { throw new UnsupportedOperationException(); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecord(long) - */ @Override Record getRecord(long key) throws IOException { return null; } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#updateRecord(db.Record) - */ @Override void updateRecord(Record record) throws IOException { throw new UnsupportedOperationException(); } - /** - * @throws IOException - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return new EmptyRecordIterator(); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return 0; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java index 977dc81e34..ff7c4c670f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableStorageDBAdapterV2.java @@ -27,13 +27,12 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { private static final int TABLE_VERSION = 2; private Table variableStorageTable; - VariableStorageDBAdapterV2(DBHandle handle, boolean create) throws VersionException, - IOException { + VariableStorageDBAdapterV2(DBHandle handle, boolean create) + throws VersionException, IOException { if (create) { - variableStorageTable = - handle.createTable(VARIABLE_STORAGE_TABLE_NAME, VARIABLE_STORAGE_SCHEMA, - new int[] { HASH_COL }); + variableStorageTable = handle.createTable(VARIABLE_STORAGE_TABLE_NAME, + VARIABLE_STORAGE_SCHEMA, new int[] { HASH_COL }); } else { variableStorageTable = handle.getTable(VARIABLE_STORAGE_TABLE_NAME); @@ -50,9 +49,6 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { } } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getNextStorageID() - */ @Override long getNextStorageID() { long nextKey = variableStorageTable.getMaxKey() + 1; @@ -64,46 +60,30 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { @Override long findRecordKey(long hash) throws IOException { - long[] recs = variableStorageTable.findRecords(new LongField(hash), HASH_COL); - return recs.length == 0 ? -1 : recs[0]; + Field[] recs = variableStorageTable.findRecords(new LongField(hash), HASH_COL); + return recs.length == 0 ? -1 : recs[0].getLongValue(); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#deleteRecord(long) - */ @Override void deleteRecord(long key) throws IOException { variableStorageTable.deleteRecord(key); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecord(long) - */ @Override Record getRecord(long key) throws IOException { return variableStorageTable.getRecord(key); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#updateRecord(db.Record) - */ @Override void updateRecord(Record record) throws IOException { variableStorageTable.putRecord(record); } - /** - * @throws IOException - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecords() - */ @Override RecordIterator getRecords() throws IOException { return variableStorageTable.iterator(); } - /** - * @see ghidra.program.database.symbol.VariableStorageDBAdapter#getRecordCount() - */ @Override int getRecordCount() { return variableStorageTable.getRecordCount(); @@ -120,45 +100,4 @@ public class VariableStorageDBAdapterV2 extends VariableStorageDBAdapter { } } -// public static VariableStorageDBAdapter upgrade(DBHandle dbHandle, -// VariableStorageDBAdapter oldAdapter, TaskMonitor monitor) throws IOException, -// CancelledException { -// DBHandle tmpHandle = dbHandle.getScratchPad(); -// try { -// -// monitor.setMessage("Upgrading Variable Storage Table..."); -// monitor.initialize(0, (oldAdapter.getRecordCount()) * 2); -// int count = 0; -// -// VariableStorageDBAdapterV2 tmpAdapter = new VariableStorageDBAdapterV2(tmpHandle, true); -// RecordIterator iter = oldAdapter.getRecords(); -// while (iter.hasNext()) { -// monitor.checkCanceled(); -// Record rec = iter.next(); -// Record newRec = VARIABLE_STORAGE_SCHEMA.createRecord(rec.getKey()); -// newRec.setString(HASH_COL, rec.getString(HASH_COL)); -// newRec.setString(STORAGE_COL, rec.getString(STORAGE_COL)); -// tmpAdapter.updateRecord(newRec); -// monitor.setProgress(++count); -// } -// -// dbHandle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); -// VariableStorageDBAdapterV2 newAdapter = new VariableStorageDBAdapterV2(dbHandle, true); -// -// iter = tmpAdapter.getRecords(); -// while (iter.hasNext()) { -// monitor.checkCanceled(); -// newAdapter.updateRecord(iter.next()); -// monitor.setProgress(++count); -// } -// return newAdapter; -// } -// catch (VersionException e) { -// throw new AssertException(); -// } -// finally { -// tmpHandle.deleteTable(VARIABLE_STORAGE_TABLE_NAME); -// } -// } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressRangeMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressRangeMapDB.java index a238abff14..e85d707799 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressRangeMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressRangeMapDB.java @@ -15,6 +15,12 @@ */ package ghidra.program.database.util; +import java.io.IOException; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +import db.*; +import db.util.ErrorHandler; import ghidra.program.database.map.AddressKeyRecordIterator; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; @@ -24,13 +30,6 @@ import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; -import java.util.ConcurrentModificationException; -import java.util.Iterator; - -import db.*; -import db.util.ErrorHandler; - /** * RangeMapDB provides a generic value range map backed by a database table. * A given range may be occupied by at most a single value which is painted over @@ -42,7 +41,7 @@ public class AddressRangeMapDB implements DBListener { private DBHandle dbHandle; private AddressMap addrMap; private ErrorHandler errHandler; - private Class valueFieldClass; + private Field valueField; private boolean indexed; private Table rangeMapTable; private Schema rangeMapSchema; @@ -70,17 +69,17 @@ public class AddressRangeMapDB implements DBListener { * @param name map name used in naming the underlying database table. * This name must be unique across all range maps. * @param errHandler database error handler. - * @param valueFieldClass Field class to be used for stored values. + * @param valueField Field to be used for stored values. * @param indexed if true, values will be indexed allowing use of the * getValueRangeIterator method. */ public AddressRangeMapDB(DBHandle dbHandle, AddressMap addrMap, Lock lock, String name, - ErrorHandler errHandler, Class valueFieldClass, boolean indexed) { + ErrorHandler errHandler, Field valueField, boolean indexed) { this.dbHandle = dbHandle; this.addrMap = addrMap; this.lock = lock; this.errHandler = errHandler; - this.valueFieldClass = valueFieldClass; + this.valueField = valueField; this.indexed = indexed; tableName = RANGE_MAP_TABLE_PREFIX + name; findTable(); @@ -133,10 +132,10 @@ public class AddressRangeMapDB implements DBListener { rangeMapTable = dbHandle.getTable(tableName); if (rangeMapTable != null) { rangeMapSchema = rangeMapTable.getSchema(); - Class[] fieldClasses = rangeMapSchema.getFieldClasses(); - if (fieldClasses.length != 2 || !fieldClasses[VALUE_COL].equals(valueFieldClass)) { - errHandler.dbError(new IOException( - "Existing range map table has unexpected value class")); + Field[] fields = rangeMapSchema.getFields(); + if (fields.length != 2 || !fields[VALUE_COL].isSameType(valueField)) { + errHandler.dbError( + new IOException("Existing range map table has unexpected value class")); } if (indexed) { int[] indexedCols = rangeMapTable.getIndexedColumns(); @@ -149,7 +148,7 @@ public class AddressRangeMapDB implements DBListener { private void createTable() throws IOException { rangeMapSchema = - new Schema(0, "From", new Class[] { LongField.class, valueFieldClass }, COLUMN_NAMES); + new Schema(0, "From", new Field[] { LongField.INSTANCE, valueField }, COLUMN_NAMES); if (indexed) { rangeMapTable = dbHandle.createTable(tableName, rangeMapSchema, INDEXED_COLUMNS); } @@ -243,9 +242,8 @@ public class AddressRangeMapDB implements DBListener { lock.acquire(); try { tmpDb = dbHandle.getScratchPad(); - tmpMap = - new AddressRangeMapDB(tmpDb, addrMap, lock, "TEMP", errHandler, valueFieldClass, - indexed); + tmpMap = new AddressRangeMapDB(tmpDb, addrMap, lock, "TEMP", errHandler, valueField, + indexed); Address fromEndAddr = fromAddr.add(length - 1); for (AddressRange range : getAddressRanges(fromAddr, fromEndAddr)) { @@ -655,9 +653,8 @@ public class AddressRangeMapDB implements DBListener { endIndex = addrMap.getKey(endAddr, false); checkEnd = (endIndex != AddressMap.INVALID_ADDRESS_KEY); - recIter = - new AddressKeyRecordIterator(rangeMapTable, addrMap, startAddr, endAddr, - startAddr, true); + recIter = new AddressKeyRecordIterator(rangeMapTable, addrMap, startAddr, + endAddr, startAddr, true); } catch (IOException e) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressSetPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressSetPropertyMapDB.java index 8a12212bab..db30305535 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressSetPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/AddressSetPropertyMapDB.java @@ -90,7 +90,7 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap { this.lock = lock; propertyMap = new AddressRangeMapDB(program.getDBHandle(), program.getAddressMap(), - program.getLock(), MY_PREFIX + mapName, errHandler, BooleanField.class, true); + program.getLock(), MY_PREFIX + mapName, errHandler, BooleanField.INSTANCE, true); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseTableUtils.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseTableUtils.java index ab3a08c43a..66ffc8e825 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseTableUtils.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/DatabaseTableUtils.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +15,14 @@ */ package ghidra.program.database.util; +import java.io.IOException; + +import db.*; import ghidra.program.database.map.*; import ghidra.program.model.address.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import java.io.IOException; - -import db.*; - /** * Collection of static functions for upgrading various database tables. */ @@ -56,14 +54,13 @@ public class DatabaseTableUtils { throw new IllegalArgumentException("Illegal range: end range overflow"); } boolean startFromTop = fromAddr.compareTo(toAddr) > 0; - DBLongIterator it = - new AddressIndexPrimaryKeyIterator(table, addrCol, addrMap, new AddressSet(fromAddr, - fromAddr.add(length - 1)), startFromTop); + DBFieldIterator it = new AddressIndexPrimaryKeyIterator(table, addrCol, addrMap, + new AddressSet(fromAddr, fromAddr.add(length - 1)), startFromTop); while (startFromTop ? it.hasNext() : it.hasPrevious()) { if (monitor.isCancelled()) { throw new CancelledException(); } - long key = startFromTop ? it.next() : it.previous(); + Field key = startFromTop ? it.next() : it.previous(); Record rec = table.getRecord(key); if (filter == null || filter.matches(rec)) { Address addr = addrMap.decodeAddress(rec.getLongValue(addrCol)); @@ -86,8 +83,8 @@ public class DatabaseTableUtils { * @throws CancelledException thrown if the user cancels the move operation. */ public static void updateAddressKey(Table table, AddressMap addrMap, Address fromAddr, - Address toAddr, long length, TaskMonitor monitor) throws IOException, - CancelledException { + Address toAddr, long length, TaskMonitor monitor) + throws IOException, CancelledException { if (length <= 0) { throw new IllegalArgumentException("length must be > 0"); @@ -116,8 +113,8 @@ public class DatabaseTableUtils { * @throws CancelledException thrown if the user cancels the move operation. */ public static void updateAddressKey(Table table, AddressMap addrMap, Address fromAddr, - Address endAddr, Address toAddr, TaskMonitor monitor) throws IOException, - CancelledException { + Address endAddr, Address toAddr, TaskMonitor monitor) + throws IOException, CancelledException { long length = endAddr.subtract(fromAddr); if (length < 0) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/SharedRangeMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/SharedRangeMapDB.java index ef86e026cd..a5430eca28 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/SharedRangeMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/util/SharedRangeMapDB.java @@ -15,15 +15,14 @@ */ package ghidra.program.database.util; -import ghidra.util.datastruct.IndexRange; -import ghidra.util.datastruct.IndexRangeIterator; -import ghidra.util.exception.NotYetImplementedException; - import java.io.IOException; import java.util.*; import db.*; import db.util.ErrorHandler; +import ghidra.util.datastruct.IndexRange; +import ghidra.util.datastruct.IndexRangeIterator; +import ghidra.util.exception.NotYetImplementedException; /** * SharedRangeMapDB provides a long value range map backed by a database table. @@ -56,12 +55,12 @@ public class SharedRangeMapDB { private static final int[] MAP_INDEXED_COLS = new int[] { MAP_VALUE_COL, MAP_RANGE_KEY_COL }; private static Schema createRangesSchema() { - return new Schema(0, "From", new Class[] { LongField.class }, new String[] { "To" }); + return new Schema(0, "From", new Field[] { LongField.INSTANCE }, new String[] { "To" }); } private static Schema createMapSchema() { - return new Schema(0, "Key", new Class[] { LongField.class, LongField.class }, new String[] { - "Value", "Range Key" }); + return new Schema(0, "Key", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, + new String[] { "Value", "Range Key" }); } /** @@ -72,7 +71,8 @@ public class SharedRangeMapDB { * @param errHandler database error handler. * @param create if true the underlying database tables will be created. */ - public SharedRangeMapDB(DBHandle dbHandle, String name, ErrorHandler errHandler, boolean create) { + public SharedRangeMapDB(DBHandle dbHandle, String name, ErrorHandler errHandler, + boolean create) { this.dbHandle = dbHandle; this.errHandler = errHandler; String rangeTableName = RANGES_TABLE_NAME_PREFIX + name; @@ -128,11 +128,11 @@ public class SharedRangeMapDB { //++modCount; try { // Consoldate existing ranges for this value which overlap - long[] mapKeys = mapTable.findRecords(new LongField(value), MAP_VALUE_COL); + Field[] mapKeys = mapTable.findRecords(new LongField(value), MAP_VALUE_COL); for (int i = 0; i < mapKeys.length; i++) { // Get next range - long mapKey = mapKeys[i]; + Field mapKey = mapKeys[i]; Record mapRec = mapTable.getRecord(mapKey); long rangeKey = mapRec.getLongValue(MAP_RANGE_KEY_COL); Record rangeRec = rangeTable.getRecord(rangeKey); @@ -257,7 +257,7 @@ public class SharedRangeMapDB { rangeTable.putRecord(newRange); // Split related map records - long[] mapKeys = mapTable.findRecords(rangeRecord.getKeyField(), MAP_RANGE_KEY_COL); + Field[] mapKeys = mapTable.findRecords(rangeRecord.getKeyField(), MAP_RANGE_KEY_COL); for (int i = 0; i < mapKeys.length; i++) { Record mapRec = mapTable.getRecord(mapKeys[i]); mapRec.setKey(mapTable.getMaxKey() + 1); @@ -274,12 +274,12 @@ public class SharedRangeMapDB { * @return long[] * @throws IOException */ - private long[] getMapValues(long[] mapKeys) throws IOException { + private Field[] getMapValues(Field[] mapKeys) throws IOException { - long[] values = new long[mapKeys.length]; + Field[] values = new Field[mapKeys.length]; for (int i = 0; i < mapKeys.length; i++) { Record rec = mapTable.getRecord(mapKeys[i]); - values[i] = rec.getLongValue(MAP_VALUE_COL); + values[i] = rec.getFieldValue(MAP_VALUE_COL); } Arrays.sort(values); return values; @@ -294,8 +294,8 @@ public class SharedRangeMapDB { private void consolidateRange(long rangeKey, long end) throws IOException { // Find map entries which occupy range - long[] mapKeys = mapTable.findRecords(new LongField(rangeKey), MAP_RANGE_KEY_COL); - long[] values = getMapValues(mapKeys); + Field[] mapKeys = mapTable.findRecords(new LongField(rangeKey), MAP_RANGE_KEY_COL); + Field[] values = getMapValues(mapKeys); // Delete range if not occupied if (mapKeys.length == 0) { @@ -306,7 +306,7 @@ public class SharedRangeMapDB { // Consolidate previous range if possible Record rangeRec = rangeTable.getRecordBefore(rangeKey); if (rangeRec != null && rangeRec.getLongValue(RANGE_TO_COL) == (rangeKey - 1)) { - long[] keys = mapTable.findRecords(rangeRec.getKeyField(), MAP_RANGE_KEY_COL); + Field[] keys = mapTable.findRecords(rangeRec.getKeyField(), MAP_RANGE_KEY_COL); // Can consolidate if range occupied by the same set of values if (Arrays.equals(getMapValues(keys), values)) { @@ -327,7 +327,7 @@ public class SharedRangeMapDB { // Consolidate next range if possible rangeRec = rangeTable.getRecordAfter(end); if (rangeRec != null && rangeRec.getKey() == (end + 1)) { - long[] keys = mapTable.findRecords(rangeRec.getKeyField(), MAP_RANGE_KEY_COL); + Field[] keys = mapTable.findRecords(rangeRec.getKeyField(), MAP_RANGE_KEY_COL); // Can consolidate if range occupied by the same set of values if (Arrays.equals(getMapValues(keys), values)) { @@ -353,11 +353,11 @@ public class SharedRangeMapDB { synchronized (dbHandle) { //++modCount; try { - long[] mapKeys = mapTable.findRecords(new LongField(value), MAP_VALUE_COL); + Field[] mapKeys = mapTable.findRecords(new LongField(value), MAP_VALUE_COL); for (int i = 0; i < mapKeys.length; i++) { // Remove Map entry - long mapKey = mapKeys[i]; + Field mapKey = mapKeys[i]; Record mapRec = mapTable.getRecord(mapKey); mapTable.deleteRecord(mapKey); @@ -426,9 +426,8 @@ public class SharedRangeMapDB { start = rec.getKey(); } - mapRecIter = - mapTable.indexIterator(MAP_RANGE_KEY_COL, new LongField(start), new LongField( - end), true); + mapRecIter = mapTable.indexIterator(MAP_RANGE_KEY_COL, new LongField(start), + new LongField(end), true); } catch (IOException e) { @@ -439,6 +438,7 @@ public class SharedRangeMapDB { /** * @see java.util.Iterator#remove() */ + @Override public void remove() { throw new NotYetImplementedException(); } @@ -446,6 +446,7 @@ public class SharedRangeMapDB { /** * @see java.util.Iterator#hasNext() */ + @Override public boolean hasNext() { synchronized (dbHandle) { try { @@ -469,6 +470,7 @@ public class SharedRangeMapDB { /** * @see java.util.Iterator#next() */ + @Override public Field next() { synchronized (dbHandle) { if (nextValue != null || hasNext()) { @@ -502,6 +504,7 @@ public class SharedRangeMapDB { /** * @see ghidra.util.datastruct.IndexRangeIterator#hasNext() */ + @Override public boolean hasNext() { synchronized (dbHandle) { try { @@ -517,6 +520,7 @@ public class SharedRangeMapDB { /** * @see ghidra.util.datastruct.IndexRangeIterator#next() */ + @Override public IndexRange next() { synchronized (dbHandle) { try { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java index 9681db369b..8faf7ef8c8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java @@ -19,7 +19,6 @@ import java.math.BigInteger; import org.apache.commons.lang3.StringUtils; -import generic.util.UnsignedDataUtils; import ghidra.util.MathUtilities; import ghidra.util.NumericUtilities; import ghidra.util.exception.AssertException; @@ -151,14 +150,6 @@ abstract class AbstractAddressSpace implements AddressSpace { @Override public long getAddressableWordOffset(long byteOffset) { - -// if (!isValidOffset(byteOffset)) { -// String max = Long.toHexString(maxOffset); -// String min = Long.toHexString(minOffset); -// throw new AddressOutOfBoundsException("Invalid byte offset 0x" + -// Long.toHexString(byteOffset) + ", must be between 0x" + min + " and 0x" + max); -// } - boolean isNegative = false; if (signed && byteOffset < 0) { byteOffset = -byteOffset; @@ -603,8 +594,10 @@ abstract class AbstractAddressSpace implements AddressSpace { @Override public long makeValidOffset(long offset) throws AddressOutOfBoundsException { - // TODO: Verify that this handle all cases - seems like it would not - if ((offset >= minOffset && offset <= maxOffset) || spaceSize == 0) { + if (size == 64 || spaceSize == 0) { + return offset; + } + if ((offset >= minOffset && offset <= maxOffset)) { return offset; } if (signed) { @@ -613,9 +606,11 @@ abstract class AbstractAddressSpace implements AddressSpace { return offset - spaceSize; } } - else if (offset < 0 && offset >= -maxOffset - 1) { - // recover from accidental sign extension - return offset + spaceSize; + else { + if (offset < 0 && offset >= -maxOffset - 1) { + // recover from accidental sign extension + return offset + spaceSize; + } } String max = Long.toHexString(maxOffset); String min = Long.toHexString(minOffset); @@ -623,14 +618,6 @@ abstract class AbstractAddressSpace implements AddressSpace { ", got 0x" + Long.toHexString(offset) + " instead!"); } - private boolean isValidOffset(long offset) { - if (signed) { - return (offset >= minOffset) && (offset <= maxOffset); - } - return UnsignedDataUtils.unsignedGreaterThanOrEqual(offset, minOffset) && - UnsignedDataUtils.unsignedLessThanOrEqual(offset, maxOffset); - } - @Override public long truncateOffset(long offset) { if ((offset >= minOffset && offset <= maxOffset) || spaceSize == 0) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java index fa68939881..62475f1a1f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java @@ -15,10 +15,10 @@ */ package ghidra.program.model.address; -import ghidra.util.UniversalIdGenerator; - import java.util.*; +import ghidra.util.UniversalIdGenerator; + /** * AddressMapImpl provides a stand-alone AddressMap. * An AddressMapImpl instance should only be used to decode keys which it has generated. @@ -79,7 +79,8 @@ public class AddressMapImpl { max = max < 0 ? MAX_OFFSET : Math.min(max, MAX_OFFSET); // Avoid use of add which fails for overlay addresses which have restricted min/max offsets long off = sortedBaseStartAddrs[i].getOffset() | max; - sortedBaseEndAddrs[i] = sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off); + sortedBaseEndAddrs[i] = + sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off); } addrToIndexMap.clear(); for (int i = 0; i < baseAddrs.length; i++) { @@ -94,6 +95,7 @@ public class AddressMapImpl { * start of a key range. */ private Comparator addressInsertionKeyRangeComparator = new Comparator() { + @Override public int compare(Object keyRangeObj, Object addrObj) { KeyRange range = (KeyRange) keyRangeObj; Address addr = (Address) addrObj; @@ -213,7 +215,8 @@ public class AddressMapImpl { * @see ghidra.program.database.map.AddressMap#getKeyRanges(Address, Address, boolean) */ public List getKeyRanges(Address start, Address end) { - if (start.getAddressSpace() != end.getAddressSpace() || start.getOffset() > end.getOffset()) { + if (start.getAddressSpace() != end.getAddressSpace() || + start.getOffset() > end.getOffset()) { throw new IllegalArgumentException(); } ArrayList keyRangeList = new ArrayList(); @@ -229,8 +232,8 @@ public class AddressMapImpl { ArrayList keyRangeList = new ArrayList(); if (set == null) { for (int i = 0; i < sortedBaseStartAddrs.length; i++) { - keyRangeList.add(new KeyRange(getKey(sortedBaseStartAddrs[i]), - getKey(sortedBaseEndAddrs[i]))); + keyRangeList.add( + new KeyRange(getKey(sortedBaseStartAddrs[i]), getKey(sortedBaseEndAddrs[i]))); } } else { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index 959a29c697..1e336b6f9a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -105,7 +105,9 @@ public class EnumDataType extends GenericDataType implements Enum { @Override public String[] getNames() { - return nameMap.keySet().toArray(new String[nameMap.size()]); + String[] names = nameMap.keySet().toArray(new String[nameMap.size()]); + Arrays.sort(names); + return names; } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java index 2b3227cd97..ad2ae1ca5b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java @@ -76,6 +76,11 @@ public interface Program extends DataTypeManagerDomainObject { */ public Listing getListing(); + /** + * Get the internal program address map + * @return internal address map + */ + // FIXME!! Should not expose on interface - anything using this should use ProgramDB or avoid using map! public AddressMap getAddressMap(); /** @@ -86,12 +91,14 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the programs function manager. + * @return the function manager */ public FunctionManager getFunctionManager(); /** * Returns the user-specific data manager for * this program. + * @return the program-specific user data manager */ public ProgramUserData getProgramUserData(); @@ -104,6 +111,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the external manager. + * @return the external manager */ public ExternalManager getExternalManager(); @@ -121,11 +129,13 @@ public interface Program extends DataTypeManagerDomainObject { /** * Get the reference manager. + * @return the reference manager */ public ReferenceManager getReferenceManager(); /** * Get the bookmark manager. + * @return the bookmark manager */ public BookmarkManager getBookmarkManager(); @@ -169,18 +179,19 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns a value corresponding to the original file format. + * @return original file format used to load program or null if unknown */ public String getExecutableFormat(); /** * Sets the value corresponding to the original file format. - * @param format the format string to set. + * @param format the binary file format string to set. */ public void setExecutableFormat(String format); /** * Returns a value corresponding to the original binary file MD5 hash. - * May be null if program source did not correspond to a binary file. + * @return original loaded file MD5 or null */ public String getExecutableMD5(); @@ -198,7 +209,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns a value corresponding to the original binary file SHA256 hash. - * May be null if program source did not correspond to a binary file. + * @return original loaded file SHA256 or null */ public String getExecutableSHA256(); @@ -212,6 +223,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Gets the relocation table. + * @return relocation table object */ public RelocationTable getRelocationTable(); @@ -245,6 +257,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the program context. + * @return the program context object */ public ProgramContext getProgramContext(); @@ -270,6 +283,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the AddressFactory for this program. + * @return the program address factory */ public AddressFactory getAddressFactory(); @@ -308,16 +322,16 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the largest register located at the specified address * - * @param addr - * @return largest register or null + * @param addr register minimum address + * @return largest register at addr or null */ public Register getRegister(Address addr); /** * Returns all registers located at the specified address * - * @param addr - * @return largest register + * @param addr register minimum address + * @return all registers at addr */ public Register[] getRegisters(Address addr); @@ -337,7 +351,8 @@ public interface Program extends DataTypeManagerDomainObject { public Register getRegister(Varnode varnode); /** - * Returns the current program image base address; + * Returns the current program image base address + * @return program image base address within default space */ public Address getImageBase(); @@ -365,6 +380,7 @@ public interface Program extends DataTypeManagerDomainObject { * Sets the language for the program. If the new language is "compatible" with the old language, * the addressMap is adjusted then the program is "re-disassembled". * @param language the new language to use. + * @param compilerSpecID the new compiler specification ID * @param forceRedisassembly if true a redisassembly will be forced. This should always be false. * @param monitor the task monitor * @throws IllegalStateException thrown if any error occurs, including a cancelled monitor, which leaves this @@ -380,6 +396,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns the global namespace for this program + * @return the global namespace */ public Namespace getGlobalNamespace(); @@ -430,6 +447,7 @@ public interface Program extends DataTypeManagerDomainObject { /** * Returns an ID that is unique for this program. This provides an easy way to store * references to a program across client persistence. + * @return unique program ID */ public long getUniqueProgramID(); } diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/RepositoryFileSystemTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/RepositoryFileSystemTest.java index 6b7b5030d7..d4d2d5f7e2 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/RepositoryFileSystemTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/RepositoryFileSystemTest.java @@ -83,7 +83,7 @@ public class RepositoryFileSystemTest extends AbstractGhidraHeadedIntegrationTes DBHandle dbh = new DBHandle(); long id = dbh.startTransaction(); Schema schema = - new Schema(0, "key", new Class[] { IntField.class }, new String[] { "dummy" }); + new Schema(0, "key", new Field[] { IntField.INSTANCE }, new String[] { "dummy" }); dbh.createTable("test", schema); dbh.endTransaction(id, true); ManagedBufferFile bf = folder.createDatabase(itemName, FileIDFactory.createFileID(),