GT-3126 corrected FileBytes issue with undo/redo. Also corrected

FileBytes bug which could result in empty DBBuffer.
This commit is contained in:
ghidra1 2019-08-30 15:18:33 -04:00
parent 20ac7ece0a
commit cbd270cec2
8 changed files with 157 additions and 73 deletions

View file

@ -115,7 +115,7 @@ public class ChainedBuffer implements Buffer {
* Construct a new chained buffer with optional obfuscation and uninitialized data source. * Construct a new chained buffer with optional obfuscation and uninitialized data source.
* This method may only be invoked while a database transaction * This method may only be invoked while a database transaction
* is in progress. * is in progress.
* @param size buffer size * @param size buffer size (0 < size <= 0x7fffffff)
* @param enableObfuscation true to enable xor-ing of stored data to facilitate data obfuscation. * @param enableObfuscation true to enable xor-ing of stored data to facilitate data obfuscation.
* @param uninitializedDataSource optional data source for uninitialized data. This should be a * @param uninitializedDataSource optional data source for uninitialized data. This should be a
* read-only buffer which will always be used when re-instantiating the same stored ChainedBuffer. * read-only buffer which will always be used when re-instantiating the same stored ChainedBuffer.
@ -131,6 +131,9 @@ public class ChainedBuffer implements Buffer {
this.size = size; this.size = size;
this.useXORMask = enableObfuscation; this.useXORMask = enableObfuscation;
if (size == 0) {
throw new IllegalArgumentException("Zero length buffer not permitted");
}
if (size < 0) { if (size < 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Maximum bufer size is " + Integer.MAX_VALUE + "; given size of " + size); "Maximum bufer size is " + Integer.MAX_VALUE + "; given size of " + size);
@ -165,7 +168,7 @@ public class ChainedBuffer implements Buffer {
* Construct a new chained buffer with optional obfuscation. * Construct a new chained buffer with optional obfuscation.
* This method may only be invoked while a database transaction * This method may only be invoked while a database transaction
* is in progress. * is in progress.
* @param size buffer size * @param size buffer size (0 < size <= 0x7fffffff)
* @param enableObfuscation true to enable xor-ing of stored data to facilitate data obfuscation. * @param enableObfuscation true to enable xor-ing of stored data to facilitate data obfuscation.
* @param bufferMgr database buffer manager * @param bufferMgr database buffer manager
* @throws IOException * @throws IOException
@ -178,7 +181,7 @@ public class ChainedBuffer implements Buffer {
/** /**
* Construct a new chained buffer. * Construct a new chained buffer.
* This method may only be invoked while a database transaction is in progress. * This method may only be invoked while a database transaction is in progress.
* @param size buffer size * @param size buffer size (0 < size <= 0x7fffffff)
* @param bufferMgr database buffer manager * @param bufferMgr database buffer manager
* @throws IOException * @throws IOException
*/ */

View file

@ -18,6 +18,8 @@ package ghidra.program.database.mem;
import java.io.IOException; import java.io.IOException;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import org.apache.commons.lang3.StringUtils;
import db.*; import db.*;
/** /**
@ -26,22 +28,35 @@ import db.*;
*/ */
public class FileBytes { public class FileBytes {
private final DBBuffer[] originalBuffers; final FileBytesAdapter adapter;
private final DBBuffer[] layeredBuffers;
private final String filename;
private final long id; private final long id;
private final String filename;
private final long fileOffset; private final long fileOffset;
private final long size; private final long size;
private boolean invalid = false;
private MemoryMapDB memMap;
public FileBytes(FileBytesAdapter adapter, MemoryMapDB memMap, Record record) private DBBuffer[] originalBuffers;
throws IOException { private DBBuffer[] layeredBuffers;
this.memMap = memMap; private boolean invalid = false;
public FileBytes(FileBytesAdapter adapter, Record record) throws IOException {
this.adapter = adapter;
this.id = record.getKey();
this.filename = record.getString(FileBytesAdapter.FILENAME_COL); this.filename = record.getString(FileBytesAdapter.FILENAME_COL);
this.fileOffset = record.getLongValue(FileBytesAdapter.OFFSET_COL); this.fileOffset = record.getLongValue(FileBytesAdapter.OFFSET_COL);
this.size = record.getLongValue(FileBytesAdapter.SIZE_COL); this.size = record.getLongValue(FileBytesAdapter.SIZE_COL);
this.id = record.getKey(); refresh(record);
}
synchronized boolean refresh(Record record) throws IOException {
String f = record.getString(FileBytesAdapter.FILENAME_COL);
long offset = record.getLongValue(FileBytesAdapter.OFFSET_COL);
long sz = record.getLongValue(FileBytesAdapter.SIZE_COL);
if (offset != fileOffset || sz != size || !StringUtils.equals(f, filename)) {
return false;
}
BinaryField field = (BinaryField) record.getFieldValue(FileBytesAdapter.BUF_IDS_COL); BinaryField field = (BinaryField) record.getFieldValue(FileBytesAdapter.BUF_IDS_COL);
int[] bufferIds = new BinaryCodedField(field).getIntArray(); int[] bufferIds = new BinaryCodedField(field).getIntArray();
@ -56,7 +71,11 @@ public class FileBytes {
for (int i = 0; i < bufferIds.length; i++) { for (int i = 0; i < bufferIds.length; i++) {
layeredBuffers[i] = adapter.getBuffer(bufferIds[i], originalBuffers[i]); layeredBuffers[i] = adapter.getBuffer(bufferIds[i], originalBuffers[i]);
} }
return true;
}
long getId() {
return id;
} }
/** /**
@ -93,7 +112,7 @@ public class FileBytes {
* @throws IOException if there is a problem reading the database. * @throws IOException if there is a problem reading the database.
* @throws IndexOutOfBoundsException if the given offset is invalid. * @throws IndexOutOfBoundsException if the given offset is invalid.
*/ */
public byte getModifiedByte(long offset) throws IOException { public synchronized byte getModifiedByte(long offset) throws IOException {
return getByte(layeredBuffers, offset); return getByte(layeredBuffers, offset);
} }
@ -104,7 +123,7 @@ public class FileBytes {
* @throws IOException if there is a problem reading the database. * @throws IOException if there is a problem reading the database.
* @throws IndexOutOfBoundsException if the given offset is invalid. * @throws IndexOutOfBoundsException if the given offset is invalid.
*/ */
public byte getOriginalByte(long offset) throws IOException { public synchronized byte getOriginalByte(long offset) throws IOException {
return getByte(originalBuffers, offset); return getByte(originalBuffers, offset);
} }
@ -117,7 +136,7 @@ public class FileBytes {
* @return the number of bytes actually populated. * @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database * @throws IOException if there is an error reading from the database
*/ */
public int getModifiedBytes(long offset, byte[] b) throws IOException { public synchronized int getModifiedBytes(long offset, byte[] b) throws IOException {
return getBytes(layeredBuffers, offset, b, 0, b.length); return getBytes(layeredBuffers, offset, b, 0, b.length);
} }
@ -130,7 +149,7 @@ public class FileBytes {
* @return the number of bytes actually populated. * @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database * @throws IOException if there is an error reading from the database
*/ */
public int getOriginalBytes(long offset, byte[] b) throws IOException { public synchronized int getOriginalBytes(long offset, byte[] b) throws IOException {
return getBytes(originalBuffers, offset, b, 0, b.length); return getBytes(originalBuffers, offset, b, 0, b.length);
} }
@ -148,7 +167,8 @@ public class FileBytes {
* @throws IndexOutOfBoundsException if the destination offset and length would exceed the * @throws IndexOutOfBoundsException if the destination offset and length would exceed the
* size of the buffer b. * size of the buffer b.
*/ */
public int getModifiedBytes(long offset, byte[] b, int off, int length) throws IOException { public synchronized int getModifiedBytes(long offset, byte[] b, int off, int length)
throws IOException {
return getBytes(layeredBuffers, offset, b, off, length); return getBytes(layeredBuffers, offset, b, off, length);
} }
@ -166,24 +186,21 @@ public class FileBytes {
* @throws IndexOutOfBoundsException if the destination offset and length would exceed the * @throws IndexOutOfBoundsException if the destination offset and length would exceed the
* size of the buffer b. * size of the buffer b.
*/ */
public int getOriginalBytes(long offset, byte[] b, int off, int length) throws IOException { public synchronized int getOriginalBytes(long offset, byte[] b, int off, int length)
throws IOException {
return getBytes(originalBuffers, offset, b, off, length); return getBytes(originalBuffers, offset, b, off, length);
} }
void checkValid() { synchronized void checkValid() {
if (invalid) { if (invalid) {
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
} }
void invalidate() { synchronized void invalidate() {
invalid = true; invalid = true;
} }
long getId() {
return id;
}
/** /**
* Changes the byte at the given offset to the given value. Note, the * Changes the byte at the given offset to the given value. Note, the
* original byte can still be accessed via {@link #getOriginalByte(long)} * original byte can still be accessed via {@link #getOriginalByte(long)}
@ -193,13 +210,14 @@ public class FileBytes {
* @param b the new byte value; * @param b the new byte value;
* @throws IOException if the write to the database fails. * @throws IOException if the write to the database fails.
*/ */
void putByte(long offset, byte b) throws IOException { synchronized void putByte(long offset, byte b) throws IOException {
checkValid();
if (offset < 0 || offset >= size) { if (offset < 0 || offset >= size) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
checkValid();
// The max buffer size will be the size of the first buffer. (If more than // The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer, // one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.) // then its actual size can be used as the max size and it won't matter.)
@ -220,7 +238,7 @@ public class FileBytes {
* @return the number of bytes written * @return the number of bytes written
* @throws IOException if the write to the database fails. * @throws IOException if the write to the database fails.
*/ */
int putBytes(long offset, byte[] b) throws IOException { synchronized int putBytes(long offset, byte[] b) throws IOException {
return putBytes(offset, b, 0, b.length); return putBytes(offset, b, 0, b.length);
} }
@ -236,7 +254,10 @@ public class FileBytes {
* @return the number of bytes written * @return the number of bytes written
* @throws IOException if the write to the database fails. * @throws IOException if the write to the database fails.
*/ */
int putBytes(long offset, byte[] b, int off, int length) throws IOException { synchronized int putBytes(long offset, byte[] b, int off, int length) throws IOException {
checkValid();
if (b == null) { if (b == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
@ -247,8 +268,6 @@ public class FileBytes {
return 0; return 0;
} }
checkValid();
// adjust size if asking length is more than we have // adjust size if asking length is more than we have
length = (int) Math.min(length, size - offset); length = (int) Math.min(length, size - offset);
if (length == 0) { if (length == 0) {
@ -276,12 +295,13 @@ public class FileBytes {
} }
private byte getByte(DBBuffer[] buffers, long offset) throws IOException { private byte getByte(DBBuffer[] buffers, long offset) throws IOException {
checkValid();
if (offset < 0 || offset >= size) { if (offset < 0 || offset >= size) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
checkValid();
// The max buffer size will be the size of the first buffer. (If more than // The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer, // one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.) // then its actual size can be used as the max size and it won't matter.)
@ -295,6 +315,8 @@ public class FileBytes {
private int getBytes(DBBuffer[] buffers, long offset, byte[] b, int off, int length) private int getBytes(DBBuffer[] buffers, long offset, byte[] b, int off, int length)
throws IOException { throws IOException {
checkValid();
if (off < 0 || length < 0 || length > b.length - off) { if (off < 0 || length < 0 || length > b.length - off) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
@ -302,8 +324,6 @@ public class FileBytes {
return 0; return 0;
} }
checkValid();
// adjust size if asking length is more than we have // adjust size if asking length is more than we have
length = (int) Math.min(length, size - offset); length = (int) Math.min(length, size - offset);
if (length == 0) { if (length == 0) {
@ -332,7 +352,7 @@ public class FileBytes {
@Override @Override
public String toString() { public String toString() {
return filename; return getFilename();
} }
@Override @Override
@ -349,7 +369,7 @@ public class FileBytes {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
FileBytes other = (FileBytes) obj; FileBytes other = (FileBytes) obj;
if (memMap != other.memMap) if (adapter != other.adapter)
return false; return false;
if (id != other.id) if (id != other.id)
return false; return false;
@ -358,7 +378,4 @@ public class FileBytes {
return true; return true;
} }
MemoryMapDB getMemMap() {
return memMap;
}
} }

View file

@ -27,7 +27,7 @@ import ghidra.util.task.TaskMonitor;
* Database Adapter for storing and retrieving original file bytes. * Database Adapter for storing and retrieving original file bytes.
*/ */
abstract class FileBytesAdapter { abstract class FileBytesAdapter {
private static final int MAX_BUF_SIZE = 1_000_000_000; static final int MAX_BUF_SIZE = 1_000_000_000;
public static final int FILENAME_COL = FileBytesAdapterV0.V0_FILENAME_COL; public static final int FILENAME_COL = FileBytesAdapterV0.V0_FILENAME_COL;
public static final int OFFSET_COL = FileBytesAdapterV0.V0_OFFSET_COL; public static final int OFFSET_COL = FileBytesAdapterV0.V0_OFFSET_COL;
@ -40,40 +40,39 @@ abstract class FileBytesAdapter {
protected MemoryMapDB memMap; protected MemoryMapDB memMap;
FileBytesAdapter(DBHandle handle, MemoryMapDB memMap) { FileBytesAdapter(DBHandle handle) {
this.handle = handle; this.handle = handle;
this.memMap = memMap;
} }
static FileBytesAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap, static FileBytesAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor)
TaskMonitor monitor) throws VersionException, IOException { throws VersionException, IOException {
if (openMode == DBConstants.CREATE) { if (openMode == DBConstants.CREATE) {
return new FileBytesAdapterV0(handle, memMap, true); return new FileBytesAdapterV0(handle, true);
} }
try { try {
return new FileBytesAdapterV0(handle, memMap, false); return new FileBytesAdapterV0(handle, false);
} }
catch (VersionException e) { catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e; throw e;
} }
FileBytesAdapter adapter = findReadOnlyAdapter(handle, memMap); FileBytesAdapter adapter = findReadOnlyAdapter(handle);
if (openMode == DBConstants.UPGRADE) { if (openMode == DBConstants.UPGRADE) {
adapter = upgrade(handle, memMap, adapter, monitor); adapter = upgrade(handle, adapter, monitor);
} }
return adapter; return adapter;
} }
} }
private static FileBytesAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap) { private static FileBytesAdapter findReadOnlyAdapter(DBHandle handle) {
return new FileBytesAdapterNoTable(handle, memMap); return new FileBytesAdapterNoTable(handle);
} }
private static FileBytesAdapter upgrade(DBHandle handle, MemoryMapDB memMap, private static FileBytesAdapter upgrade(DBHandle handle, FileBytesAdapter oldAdapter,
FileBytesAdapter oldAdapter, TaskMonitor monitor) throws VersionException, IOException { TaskMonitor monitor) throws VersionException, IOException {
return new FileBytesAdapterV0(handle, memMap, true); return new FileBytesAdapterV0(handle, true);
} }
abstract FileBytes createFileBytes(String filename, long offset, long size, InputStream is) abstract FileBytes createFileBytes(String filename, long offset, long size, InputStream is)

View file

@ -27,8 +27,8 @@ import db.DBHandle;
*/ */
class FileBytesAdapterNoTable extends FileBytesAdapter { class FileBytesAdapterNoTable extends FileBytesAdapter {
public FileBytesAdapterNoTable(DBHandle handle, MemoryMapDB memMap) { public FileBytesAdapterNoTable(DBHandle handle) {
super(handle, memMap); super(handle);
} }
@Override @Override

View file

@ -45,9 +45,8 @@ class FileBytesAdapterV0 extends FileBytesAdapter {
private Table table; private Table table;
private List<FileBytes> fileBytesList = new ArrayList<>(); private List<FileBytes> fileBytesList = new ArrayList<>();
FileBytesAdapterV0(DBHandle handle, MemoryMapDB memMap, boolean create) FileBytesAdapterV0(DBHandle handle, boolean create) throws VersionException, IOException {
throws VersionException, IOException { super(handle);
super(handle, memMap);
if (create) { if (create) {
table = handle.createTable(TABLE_NAME, SCHEMA); table = handle.createTable(TABLE_NAME, SCHEMA);
@ -66,7 +65,7 @@ class FileBytesAdapterV0 extends FileBytesAdapter {
RecordIterator iterator = table.iterator(); RecordIterator iterator = table.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Record record = iterator.next(); Record record = iterator.next();
fileBytesList.add(new FileBytes(this, memMap, record)); fileBytesList.add(new FileBytes(this, record));
} }
} }
@ -85,7 +84,7 @@ class FileBytesAdapterV0 extends FileBytesAdapter {
record.setField(V0_BUF_IDS_COL, new BinaryCodedField(bufIds)); record.setField(V0_BUF_IDS_COL, new BinaryCodedField(bufIds));
record.setField(V0_LAYERED_BUF_IDS_COL, new BinaryCodedField(layeredBufIds)); record.setField(V0_LAYERED_BUF_IDS_COL, new BinaryCodedField(layeredBufIds));
table.putRecord(record); table.putRecord(record);
FileBytes fileBytes = new FileBytes(this, memMap, record); FileBytes fileBytes = new FileBytes(this, record);
fileBytesList.add(fileBytes); fileBytesList.add(fileBytes);
return fileBytes; return fileBytes;
} }
@ -108,8 +107,15 @@ class FileBytesAdapterV0 extends FileBytesAdapter {
while (iterator.hasNext()) { while (iterator.hasNext()) {
Record record = iterator.next(); Record record = iterator.next();
FileBytes fileBytes = map.remove(record.getKey()); FileBytes fileBytes = map.remove(record.getKey());
if (fileBytes != null) {
if (!fileBytes.refresh(record)) {
// FileBytes attributes changed
fileBytes.invalidate();
fileBytes = null;
}
}
if (fileBytes == null) { if (fileBytes == null) {
fileBytes = new FileBytes(this, memMap, record); fileBytes = new FileBytes(this, record);
} }
newList.add(fileBytes); newList.add(fileBytes);
} }
@ -148,16 +154,18 @@ class FileBytesAdapterV0 extends FileBytesAdapter {
private DBBuffer[] createBuffers(long size, InputStream is) throws IOException { private DBBuffer[] createBuffers(long size, InputStream is) throws IOException {
int maxBufSize = getMaxBufferSize(); int maxBufSize = getMaxBufferSize();
int bufCount = (int) (size / maxBufSize); int bufCount = (int) (size + maxBufSize - 1) / maxBufSize;
int sizeLastBuf = (int) (size % maxBufSize);
if (sizeLastBuf > 0) {
bufCount++;
}
DBBuffer[] buffers = new DBBuffer[bufCount]; DBBuffer[] buffers = new DBBuffer[bufCount];
for (int i = 0; i < bufCount - 1; i++) { int bufSize = maxBufSize;
buffers[i] = handle.createBuffer(maxBufSize); int lastBufSize = (int) (size % maxBufSize);
for (int i = 0; i < bufCount; i++) {
if (lastBufSize != 0 && i == (bufCount - 1)) {
bufSize = lastBufSize;
}
buffers[i] = handle.createBuffer(bufSize);
} }
buffers[bufCount - 1] = handle.createBuffer(sizeLastBuf);
try { try {
for (DBBuffer buffer : buffers) { for (DBBuffer buffer : buffers) {

View file

@ -86,7 +86,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
this.lock = lock; this.lock = lock;
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN; defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor); adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, this, monitor); fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, monitor);
initializeBlocks(); initializeBlocks();
buildAddressSets(); buildAddressSets();
} }
@ -2049,7 +2049,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
private void checkFileBytes(FileBytes fileBytes) { private void checkFileBytes(FileBytes fileBytes) {
if (fileBytes.getMemMap() != this) { if (fileBytes.adapter != fileBytesAdapter) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Attempted to delete FileBytes that doesn't belong to this program"); "Attempted to delete FileBytes that doesn't belong to this program");
} }

View file

@ -37,7 +37,11 @@ import ghidra.util.task.TaskMonitor;
public class FileBytesTest extends AbstractGenericTest { public class FileBytesTest extends AbstractGenericTest {
// Use of small buffer size will not exercise use of indexed ChainedBuffer,
// therefor those tests which need to exercise this should use a size which
// exceeds 16-KBytes.
private static final int MAX_BUFFER_SIZE_FOR_TESTING = 200; private static final int MAX_BUFFER_SIZE_FOR_TESTING = 200;
private Program program; private Program program;
private Memory mem; private Memory mem;
private int transactionID; private int transactionID;
@ -186,6 +190,59 @@ public class FileBytesTest extends AbstractGenericTest {
} }
} }
@Test
public void testGetLayeredBytesAfterUndo() throws Exception {
// NOTE: need to induce use of indexed ChainedBuffer
FileBytesAdapter.setMaxBufferSize(FileBytesAdapter.MAX_BUF_SIZE);
FileBytes fileBytes = createFileBytes("file1", 20000);
program.endTransaction(transactionID, true);
transactionID = program.startTransaction("modify");
incrementFileBytes(fileBytes, 0, 10);
incrementFileBytes(fileBytes, 18999, 10);
// undo layered buffer changes
program.endTransaction(transactionID, true);
program.undo();
transactionID = program.startTransaction("resume");
// check that the layered bytes are unchanged from the originals
assertEquals(1, fileBytes.getOriginalByte(1));
assertEquals(1, fileBytes.getModifiedByte(1));
byte b = (byte) 19000;
assertEquals(b, fileBytes.getOriginalByte(19000));
assertEquals(b, fileBytes.getModifiedByte(19000));
}
@Test
public void testGetLayeredBytesAfterUndoRedo() throws Exception {
// NOTE: need to induce use of indexed ChainedBuffer
FileBytesAdapter.setMaxBufferSize(FileBytesAdapter.MAX_BUF_SIZE);
FileBytes fileBytes = createFileBytes("file1", 20000);
program.endTransaction(transactionID, true);
transactionID = program.startTransaction("modify");
incrementFileBytes(fileBytes, 0, 10);
incrementFileBytes(fileBytes, 18999, 10);
// undo layered buffer changes
program.endTransaction(transactionID, true);
program.undo();
program.redo();
transactionID = program.startTransaction("resume");
// check that the layered bytes are unchanged from the originals
assertEquals(1, fileBytes.getOriginalByte(1));
assertEquals(2, fileBytes.getModifiedByte(1));
byte b = (byte) 19000;
assertEquals(b, fileBytes.getOriginalByte(19000));
assertEquals((byte) (b + 1), fileBytes.getModifiedByte(19000));
}
private FileBytes createFileBytes(String name, int size) throws Exception { private FileBytes createFileBytes(String name, int size) throws Exception {
byte[] bytes = new byte[size]; byte[] bytes = new byte[size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {

View file

@ -64,7 +64,7 @@ public class MemBlockDBTest extends AbstractGenericTest {
MemoryMapDBAdapter adapter = MemoryMapDBAdapter adapter =
new MemoryMapDBAdapterV3(handle, mem, MAX_SUB_BLOCK_SIZE, true); new MemoryMapDBAdapterV3(handle, mem, MAX_SUB_BLOCK_SIZE, true);
FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, mem, true); FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, true);
mem.init(adapter, fileBytesAdapter); mem.init(adapter, fileBytesAdapter);
mem.setProgram(program); mem.setProgram(program);