/** bit index value for an invalid page number */
private static final int INVALID_BIT_INDEX = -1;
-
+
/** owning database */
private final DatabaseImpl _database;
/** Page number of the map table declaration */
public DatabaseImpl getDatabase() {
return _database;
}
-
+
public JetFormat getFormat() {
return getDatabase().getFormat();
}
int umapPageNum = ByteUtil.get3ByteInt(buf);
return read(database, umapPageNum, umapRowNum, false);
}
-
+
/**
* @param database database that contains this usage map
* @param pageNum Page number that this usage map is contained in
pageChannel.readPage(tableBuffer, pageNum);
short rowStart = TableImpl.findRowStart(tableBuffer, rowNum, format);
int rowEnd = TableImpl.findRowEnd(tableBuffer, rowNum, format);
- tableBuffer.limit(rowEnd);
+ tableBuffer.limit(rowEnd);
byte mapType = tableBuffer.get(rowStart);
UsageMap rtn = new UsageMap(database, tableBuffer, pageNum, rowStart);
rtn.initHandler(mapType, isGlobal);
throws IOException
{
if (mapType == MAP_TYPE_INLINE) {
- _handler = (isGlobal ? new GlobalInlineHandler() :
+ _handler = (isGlobal ? new GlobalInlineHandler() :
new InlineHandler());
} else if (mapType == MAP_TYPE_REFERENCE) {
- _handler = (isGlobal ? new GlobalReferenceHandler() :
+ _handler = (isGlobal ? new GlobalReferenceHandler() :
new ReferenceHandler());
} else {
throw new IOException(MSG_PREFIX_UNRECOGNIZED_MAP + mapType);
}
}
-
+
public PageCursor cursor() {
return new PageCursor();
}
public int getPageCount() {
return _pageNumbers.cardinality();
}
-
+
protected short getRowStart() {
return _rowStart;
}
protected void setStartOffset(int startOffset) {
_startOffset = startOffset;
}
-
+
protected int getStartOffset() {
return _startOffset;
}
-
+
protected ByteBuffer getTableBuffer() {
return _tableBuffer;
}
-
+
protected int getTablePageNumber() {
return _tablePageNum;
}
-
+
protected int getStartPage() {
return _startPage;
}
-
+
protected int getEndPage() {
return _endPage;
}
-
+
protected BitSet getPageNumbers() {
return _pageNumbers;
}
}
protected int getFirstPageNumber() {
- return bitIndexToPageNumber(getNextBitIndex(-1),
+ return bitIndexToPageNumber(getNextBitIndex(-1),
RowIdImpl.LAST_PAGE_NUMBER);
}
return bitIndexToPageNumber(
getNextBitIndex(pageNumberToBitIndex(curPage)),
RowIdImpl.LAST_PAGE_NUMBER);
- }
-
+ }
+
protected int getNextBitIndex(int curIndex) {
return _pageNumbers.nextSetBit(curIndex + 1);
- }
-
+ }
+
protected int getLastPageNumber() {
return bitIndexToPageNumber(getPrevBitIndex(_pageNumbers.length()),
RowIdImpl.FIRST_PAGE_NUMBER);
return bitIndexToPageNumber(
getPrevBitIndex(pageNumberToBitIndex(curPage)),
RowIdImpl.FIRST_PAGE_NUMBER);
- }
-
+ }
+
protected int getPrevBitIndex(int curIndex) {
--curIndex;
while((curIndex >= 0) && !_pageNumbers.get(curIndex)) {
--curIndex;
}
return curIndex;
- }
-
+ }
+
protected int bitIndexToPageNumber(int bitIndex,
int invalidPageNumber) {
return((bitIndex >= 0) ? (_startPage + bitIndex) : invalidPageNumber);
_startPage = 0;
_endPage = 0;
++_modCount;
-
+
// clear out the table data (everything except map type)
int tableStart = getRowStart() + 1;
int tableEnd = getRowEnd();
ByteUtil.clearRange(_tableBuffer, tableStart, tableEnd);
}
-
+
protected void writeTable()
throws IOException
{
// note, we only want to write the row data with which we are working
getPageChannel().writePage(_tableBuffer, _tablePageNum, _rowStart);
}
-
+
/**
* Read in the page numbers in this inline map
*/
public boolean containsPageNumber(int pageNumber) {
return _handler.containsPageNumber(pageNumber);
}
-
+
/**
* Add a page number to this usage map
*/
++_modCount;
_handler.addOrRemovePageNumber(pageNumber, true, false);
}
-
+
/**
* Remove a page number from this usage map
*/
- public void removePageNumber(int pageNumber)
- throws IOException
+ public void removePageNumber(int pageNumber)
+ throws IOException
{
removePageNumber(pageNumber, true);
}
-
- private void removePageNumber(int pageNumber, boolean force)
- throws IOException
+
+ private void removePageNumber(int pageNumber, boolean force)
+ throws IOException
{
++_modCount;
_handler.addOrRemovePageNumber(pageNumber, false, force);
}
-
+
protected void updateMap(int absolutePageNumber,
int bufferRelativePageNumber,
ByteBuffer buffer, boolean add, boolean force)
" usage map, expected range " +
_startPage + " to " + _endPage);
}
-
+
//Apply the bitmask
if (add) {
b |= bitmask;
// clear out the main table (inline usage map data and start page)
clearTableAndPages();
-
+
// set the new map type
_tableBuffer.put(getRowStart(), MAP_TYPE_REFERENCE);
// write the new table data
writeTable();
-
+
// set new handler
_handler = new ReferenceHandler();
ranges.add(String.valueOf(rangeStart));
}
}
-
+
+ private static int toValidStartPage(int startPage) {
+ // start page must be a multiple of 8
+ return ((startPage / 8) * 8);
+ }
+
private abstract class Handler
{
protected Handler() {
return(isPageWithinRange(pageNumber) &&
getPageNumbers().get(pageNumberToBitIndex(pageNumber)));
}
-
+
/**
* @param pageNumber Page number to add or remove from this map
* @param add True to add it, false to remove it
private class InlineHandler extends Handler
{
private final int _maxInlinePages;
-
+
protected InlineHandler() throws IOException
{
_maxInlinePages = (getInlineDataEnd() - getInlineDataStart()) * 8;
protected final int getInlineDataEnd() {
return getRowEnd();
}
-
+
/**
* Sets the page range for an inline usage map starting from the given
* page.
*/
private void setInlinePageRange(int startPage) {
setPageRange(startPage, startPage + getMaxInlinePages());
- }
-
+ }
+
@Override
public void addOrRemovePageNumber(int pageNumber, boolean add,
boolean force)
force);
// Write the updated map back to disk
writeTable();
-
+
} else {
// uh-oh, we've split our britches. what now?
throws IOException
{
// determine what our status is before taking action
-
+
if(add) {
int firstPage = getFirstPageNumber();
} else {
firstPage = pageNumber;
}
+
+ firstPage = toValidStartPage(firstPage);
+
if((lastPage - firstPage + 1) < getMaxInlinePages()) {
// we can still fit within an inline map
moveToNewStartPage(firstPage, pageNumber);
-
+
} else {
// not going to happen, need to promote the usage map to a
// reference map
// since we assuming out-of-range bits are "on". Note, we are leaving
// small holes in the database here (leaving behind some free pages),
// but it's not the end of the world.
-
+
if(!add) {
int firstPage = getFirstPageNumber();
throws IOException
{
int oldEndPage = getEndPage();
- int newStartPage =
- ((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ? newPageNumber :
- // just shift a little and discard any initial unused pages.
- (newPageNumber - (getMaxInlinePages() / 2)));
+ int newStartPage =
+ toValidStartPage(
+ ((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ? newPageNumber :
+ // just shift a little and discard any initial unused pages.
+ (newPageNumber - (getMaxInlinePages() / 2))));
// move the current data
moveToNewStartPage(newStartPage, PageChannel.INVALID_PAGE_NUMBER);
*/
private class ReferenceHandler extends Handler
{
- /** Buffer that contains the current reference map page */
+ /** Buffer that contains the current reference map page */
private final TempPageHolder _mapPageHolder =
TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
private final int _maxPagesPerUsageMapPage;
-
+
private ReferenceHandler() throws IOException
{
- _maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
+ _maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
getFormat().OFFSET_USAGE_MAP_PAGE_DATA) * 8);
int numUsagePages = (getRowEnd() - getRowStart() - 1) / 4;
setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
setPageRange(0, (numUsagePages * _maxPagesPerUsageMapPage));
-
+
// there is no "start page" for a reference usage map, so we get an
// extra page reference on top of the number of page references that fit
// in the table
protected final int getMaxPagesPerUsagePage() {
return _maxPagesPerUsageMapPage;
}
-
+
@Override
public void addOrRemovePageNumber(int pageNumber, boolean add,
boolean force)
mapPageBuffer, add, force);
getPageChannel().writePage(mapPageBuffer, mapPageNum);
}
-
+
/**
* Create a new usage map page and update the map declaration with a
* pointer to it.
- * @param pageIndex Index of the page reference within the map declaration
+ * @param pageIndex Index of the page reference within the map declaration
*/
private ByteBuffer createNewUsageMapPage(int pageIndex) throws IOException
{
writeTable();
return mapPageBuffer;
}
-
+
private int calculateMapPagePointerOffset(int pageIndex) {
return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
(pageIndex * 4);
}
- protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
- throws IOException
+ protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
+ throws IOException
{
ByteBuffer mapPageBuffer = _mapPageHolder.setNewPage(getPageChannel());
mapPageBuffer.put(PageTypes.USAGE_MAP);
super.addOrRemovePageNumber(pageNumber, add, force);
while(_pendingPage != null) {
-
+
// while updating our usage map, we needed to allocate a new page (and
// thus mark a new page as used). we delayed that marking so that we
// didn't get into an infinite loop. now that we completed the
_pendingPage = null;
super.addOrRemovePageNumber(removedPageNumber, false, true);
- }
- }
+ }
+ }
@Override
protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
- throws IOException
+ throws IOException
{
try {
// keep track of the fact that we are actively allocating a page for our
// load this map, which is fine because we should only add pages which
// represent the size of the database currently in use).
int dataStart = getFormat().OFFSET_USAGE_MAP_PAGE_DATA;
- ByteUtil.fillRange(mapPageBuffer, dataStart,
+ ByteUtil.fillRange(mapPageBuffer, dataStart,
getFormat().PAGE_SIZE - dataStart);
int maxPagesPerUmapPage = getMaxPagesPerUsagePage();
public UsageMap getUsageMap() {
return UsageMap.this;
}
-
+
/**
* Returns the DirHandler for the given direction
*/
*/
public boolean isUpToDate() {
return(UsageMap.this._modCount == _lastModCount);
- }
+ }
/**
* @return valid page number if there was another page to read,
}
checkForModification();
-
+
_prevPageNumber = _curPageNumber;
_curPageNumber = handler.getAnotherPageNumber(_curPageNumber);
return _curPageNumber;
{
restorePosition(curPageNumber, _curPageNumber);
}
-
+
/**
* Restores a current and previous position for the cursor.
*/
}
return pageNumber;
}
-
+
@Override
public String toString() {
return getClass().getSimpleName() + " CurPosition " + _curPageNumber +
", PrevPosition " + _prevPageNumber;
}
-
-
+
+
/**
* Handles moving the cursor in a given direction. Separates cursor
* logic from value storage.
public abstract int getBeginningPageNumber();
public abstract int getEndPageNumber();
}
-
+
/**
* Handles moving the cursor forward.
*/
return RowIdImpl.LAST_PAGE_NUMBER;
}
}
-
+
/**
* Handles moving the cursor backward.
*/
return RowIdImpl.FIRST_PAGE_NUMBER;
}
}
-
- }
-
+
+ }
+
}