/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.github.geophile.erdo.map.diskmap.tree;
import com.github.geophile.erdo.AbstractKey;
import com.github.geophile.erdo.bloomfilter.BloomFilter;
import com.github.geophile.erdo.map.diskmap.DBStructure;
import com.github.geophile.erdo.map.diskmap.Manifest;
import com.github.geophile.erdo.segmentfilemanager.AbstractSegmentFileManager;
import com.github.geophile.erdo.util.FileUtil;
import java.io.IOException;
public class TreeSegment
{
// Object interface
@Override
public String toString()
{
return String.format("%s/S%s(%s)", level, segmentNumber, segmentId);
}
// TreeSegment interface
public long segmentId()
{
return segmentId;
}
public int segmentNumber()
{
return segmentNumber;
}
public int pages()
{
return pageCount;
}
public int leafRecords()
{
assert level.isLeaf();
return summary.keyCount();
}
public void destroy()
{
Tree tree = level.tree();
AbstractSegmentFileManager segmentFileManager = tree.factory().segmentFileManager();
boolean deleted =
segmentFileManager.delete(tree.dbStructure().segmentFile(segmentId), tree.treeId(), segmentId);
// deleted is false if the segment is linked to another tree
if (deleted) {
FileUtil.deleteFile(tree.dbStructure().summaryFile(segmentId));
}
}
public static TreeSegment recover(TreeLevel level, int segmentNumber, Manifest manifest)
throws IOException, InterruptedException
{
Tree tree = level.tree();
AbstractSegmentFileManager segmentFileManager = tree.factory().segmentFileManager();
DBStructure dbStructure = tree.dbStructure();
long segmentId = manifest.segmentIds(level.levelNumber()).at(segmentNumber);
TreeSegment segment = new TreeSegment(level, segmentNumber, segmentId);
if (level.isLeaf()) {
segment.summary.read();
segmentFileManager.register(dbStructure.segmentFile(segmentId), tree.treeId(), segmentId);
}
segment.pageCount = (int) (tree.dbStructure().segmentFile(segmentId).length() / tree.pageSizeBytes());
return segment;
}
public AbstractKey leafLastKey()
{
assert level.isLeaf();
return summary.lastKey();
}
public boolean keyPossiblyPresent(AbstractKey key)
{
assert level.isLeaf();
checkClosed();
return !BloomFilter.USE_BLOOM_FILTER || summary.maybePresent(key);
}
public boolean isOpen()
{
return this instanceof WriteableTreeSegment;
}
public void checkClosed()
{
}
public static TreeSegment share(TreeLevel level, int segmentNumber, TreeSegment original)
{
TreeSegment copy = new TreeSegment(level, segmentNumber, original.segmentId);
copy.pageCount = original.pageCount;
// Sharing the summary will share the bloom filter. Memory tracking will read too high, but let's not worry
// about it.
copy.summary = original.summary;
return copy;
}
// For use by subclasses
protected TreeSegment(TreeLevel level, int segmentNumber, long segmentId)
{
Tree tree = level.tree();
this.level = level;
this.segmentNumber = segmentNumber;
this.segmentId = segmentId;
this.maxPagesInSegment = (int) (tree.maxFileSizeBytes() / tree.pageSizeBytes());
this.summary = level.isLeaf() ? new TreeSegmentSummary(this) : null;
}
// For use by this package
final TreeLevel treeLevel()
{
return level;
}
final TreeSegmentSummary summary()
{
return summary;
}
// Object state
protected final TreeLevel level;
protected final int segmentNumber; // position within level
protected final long segmentId; // unique within a database
protected final int maxPagesInSegment;
protected int pageCount = 0; // Counted as segment is created. Recovered from file size.
protected TreeSegmentSummary summary; // for leaf segments only
}