/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* 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 de.mxro.thrd.jdbm2V22.recman;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
/**
* This class manages free physical rowid pages and provides methods to free and allocate physical rowids on a high
* level.
*/
final class FreePhysicalRowIdPageManager {
// our record file
protected RecordFile _file;
// our page manager
protected PageManager _pageman;
private int blockSize;
/** if true, new records are always placed to end of file, new space is not reclaimed */
private boolean appendToEnd = false;
final ArrayList<Long> freeBlocksInTransactionRowid = new ArrayList<Long>();
final ArrayList<Integer> freeBlocksInTransactionSize = new ArrayList<Integer>();
/**
* Creates a new instance using the indicated record file and page manager.
*/
FreePhysicalRowIdPageManager(RecordFile file, PageManager pageman, boolean append) throws IOException {
_file = file;
_pageman = pageman;
this.blockSize = file.BLOCK_SIZE;
this.appendToEnd = append;
}
private int lastMaxSize = -1;
/**
* Returns a free physical rowid of the indicated size, or null if nothing was found. This scans the free physical
* row table, which is modeled as a linked list of pages. Each page on that list has slots that are either free
* (awaiting the insertion of the location of a free physical row) or available for reallocation requests. An
* allocated slot is indicated by a non-zero size field in that slot and the size is the size of the available free
* record in bytes.
*/
long get(int size) throws IOException {
//never reclaim used space
if(appendToEnd) return 0;
//requested record is bigger than any previously found
if(lastMaxSize!=-1 && size>lastMaxSize)
return 0;
// Loop through the free physical rowid list until we find
// a rowid that's large enough.
long retval = 0;
PageCursor curs = new PageCursor(_pageman, Magic.FREEPHYSIDS_PAGE);
int maxSize = -1;
while (curs.next() != 0) {
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage.getFreePhysicalRowIdPageView(_file.get(curs.getCurrent()), blockSize);
int slot = fp.getFirstLargerThan(size);
if (slot > 0) {
//reset maximal size, as record has changed
lastMaxSize = -1;
// got one!
retval = fp.slotToLocation(slot);
fp.free(slot);
if (fp.getCount() == 0) {
// page became empty - free it
_file.release(curs.getCurrent(), false);
_pageman.free(Magic.FREEPHYSIDS_PAGE, curs.getCurrent());
} else {
_file.release(curs.getCurrent(), true);
}
return retval;
} else {
if(maxSize<-slot)
maxSize=-slot;
// no luck, go to next page
_file.release(curs.getCurrent(), false);
}
}
//update maximal size available
lastMaxSize = maxSize;
return 0;
}
/**
* Puts the indicated rowid on the free list, which avaits for commit
*/
void put(long rowid, int size) throws IOException {
freeBlocksInTransactionRowid.add(Long.valueOf(rowid));
freeBlocksInTransactionSize.add(Integer.valueOf(size));
}
public void commit() throws IOException {
//write all uncommited free records
Iterator<Long> rowidIter = freeBlocksInTransactionRowid.iterator();
Iterator<Integer> sizeIter = freeBlocksInTransactionSize.iterator();
PageCursor curs = new PageCursor(_pageman, Magic.FREEPHYSIDS_PAGE);
//iterate over filled pages
while (curs.next() != 0) {
long freePage = curs.getCurrent();
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage.getFreePhysicalRowIdPageView(curBlock, blockSize);
int slot = fp.getFirstFree();
//iterate over free slots in page and fill them
while(slot!=-1 && rowidIter.hasNext()){
long rowid = rowidIter.next();
int size = sizeIter.next();
short freePhysRowId = fp.alloc(slot);
fp.setLocationBlock(freePhysRowId, Location.getBlock(rowid));
fp.setLocationOffset(freePhysRowId, Location.getOffset(rowid));
fp.FreePhysicalRowId_setSize(freePhysRowId, size);
slot = fp.getFirstFree();
}
_file.release(freePage, true);
if(!rowidIter.hasNext())
break;
}
//now we propably filled all already allocated pages,
//time to start allocationg new pages
while(rowidIter.hasNext()){
//allocate new page
long freePage = _pageman.allocate(Magic.FREEPHYSIDS_PAGE);
BlockIo curBlock = _file.get(freePage);
FreePhysicalRowIdPage fp = FreePhysicalRowIdPage.getFreePhysicalRowIdPageView(curBlock, blockSize);
int slot = fp.getFirstFree();
//iterate over free slots in page and fill them
while(slot!=-1 && rowidIter.hasNext()){
long rowid = rowidIter.next();
int size = sizeIter.next();
short freePhysRowId = fp.alloc(slot);
fp.setLocationBlock(freePhysRowId, Location.getBlock(rowid));
fp.setLocationOffset(freePhysRowId, Location.getOffset(rowid));
fp.FreePhysicalRowId_setSize(freePhysRowId, size);
slot = fp.getFirstFree();
}
_file.release(freePage, true);
if(!rowidIter.hasNext())
break;
}
if(rowidIter.hasNext())
throw new InternalError();
freeBlocksInTransactionRowid.clear();
freeBlocksInTransactionSize.clear();
}
}