long oldFileSize = fileChannel.size(); // includes overflow buckets
// Move any overflow buckets out of the way to a temporary file
File tmpFile = new File(file.getParentFile(), "rehash_" + file.getName());
RandomAccessFile tmpRaf = createEmptyFile(tmpFile);
FileChannel tmpChannel = tmpRaf.getChannel();
// Transfer the overflow buckets to the temp file
fileChannel.transferTo(oldTableSize, oldFileSize, tmpChannel);
// Increase hash table by factor 2
writeEmptyBuckets(oldTableSize, bucketCount);
bucketCount *= 2;
// Discard any remaining overflow buffers
fileChannel.truncate(newTableSize);
ByteBuffer bucket = ByteBuffer.allocate(recordSize);
ByteBuffer newBucket = ByteBuffer.allocate(recordSize);
// Rehash items in 'normal' buckets, half of these will move to a new
// location, but none of them will trigger the creation of new overflow
// buckets. Any (now deprecated) references to overflow buckets are
// removed too.
// All items that are moved to a new location end up in one and the same
// new and empty bucket. All items are divided between the old and the new
// bucket and the changes to the buckets are written to disk only once.
for (long bucketOffset = HEADER_LENGTH; bucketOffset < oldTableSize; bucketOffset += recordSize) {
fileChannel.read(bucket, bucketOffset);
boolean bucketChanged = false;
long newBucketOffset = 0L;
for (int slotNo = 0; slotNo < bucketSize; slotNo++) {
int id = bucket.getInt(ITEM_SIZE * slotNo + 4);
if (id != 0) {
// Slot is not empty
int hash = bucket.getInt(ITEM_SIZE * slotNo);
long newOffset = getBucketOffset(hash);
if (newOffset != bucketOffset) {
// Move this item to new bucket...
newBucket.putInt(hash);
newBucket.putInt(id);
// ...and remove it from the current bucket
bucket.putInt(ITEM_SIZE * slotNo, 0);
bucket.putInt(ITEM_SIZE * slotNo + 4, 0);
bucketChanged = true;
newBucketOffset = newOffset;
}
}
}
if (bucketChanged) {
// Some of the items were moved to the new bucket, write it to the
// file
newBucket.flip();
fileChannel.write(newBucket, newBucketOffset);
newBucket.clear();
}
// Reset overflow ID in the old bucket to 0 if necessary
if (bucket.getInt(ITEM_SIZE * bucketSize) != 0) {
bucket.putInt(ITEM_SIZE * bucketSize, 0);
bucketChanged = true;
}
if (bucketChanged) {
// Some of the items were moved to the new bucket or the overflow
// ID has been reset; write the bucket back to the file
bucket.rewind();
fileChannel.write(bucket, bucketOffset);
}
bucket.clear();
}
// Rehash items in overflow buckets. This might trigger the creation of
// new overflow buckets so we can't optimize this in the same way as we
// rehash the normal buckets.
long tmpFileSize = tmpChannel.size();
for (long bucketOffset = 0L; bucketOffset < tmpFileSize; bucketOffset += recordSize) {
tmpChannel.read(bucket, bucketOffset);
for (int slotNo = 0; slotNo < bucketSize; slotNo++) {
int id = bucket.getInt(ITEM_SIZE * slotNo + 4);
if (id != 0) {