210 lines
7.5 KiB
Java
210 lines
7.5 KiB
Java
|
/*******************************************************************************
|
||
|
* 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 org.apache.jdbm;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.util.Arrays;
|
||
|
|
||
|
/**
|
||
|
* This class manages free physical rowid pages and provides methods to free and allocate physical rowids on a high
|
||
|
* level.
|
||
|
*/
|
||
|
final class PhysicalFreeRowIdManager {
|
||
|
|
||
|
/** maximal record size which can be hold. If record crosses multiple pages, it is trimmed before added to free list */
|
||
|
static final int MAX_REC_SIZE = Storage.PAGE_SIZE *2;
|
||
|
|
||
|
/** where data on root page starts, there are no extra data in page header */
|
||
|
static final int ROOT_HEADER_SIZE = Magic.PAGE_HEADER_SIZE;
|
||
|
|
||
|
/** page header size for slot page */
|
||
|
static final int SLOT_PAGE_HEADER_SIZE = Magic.PAGE_HEADER_SIZE + Magic.SZ_SHORT + Magic.SZ_SIX_BYTE_LONG;
|
||
|
|
||
|
/** number of recids on slot page */
|
||
|
static final int OFFSET_SLOT_PAGE_REC_COUNT = Magic.PAGE_HEADER_SIZE;
|
||
|
|
||
|
static final int SLOT_PAGE_REC_NUM = (Storage.PAGE_SIZE - SLOT_PAGE_HEADER_SIZE)/6;
|
||
|
|
||
|
/** pointer to next slo page in slot page header */
|
||
|
static final int OFFSET_SLOT_PAGE_NEXT = Magic.PAGE_HEADER_SIZE + Magic.SZ_SHORT;
|
||
|
|
||
|
/** number of size slots held in root page */
|
||
|
static final int MAX_RECIDS_PER_PAGE = (Storage.PAGE_SIZE -ROOT_HEADER_SIZE-6) / 6; //6 is size of page pointer
|
||
|
|
||
|
/** free records are grouped into slots by record size. Here is max diff in record size per group */
|
||
|
static final int ROOT_SLOT_SIZE = 1+MAX_REC_SIZE/ MAX_RECIDS_PER_PAGE;
|
||
|
|
||
|
|
||
|
protected final PageFile file;
|
||
|
|
||
|
protected final PageManager pageman;
|
||
|
|
||
|
/** list of free phys slots in current transaction. First two bytes are size, last 6 bytes are recid*/
|
||
|
private long[] inTrans = new long[8];
|
||
|
private int inTransSize = 0;
|
||
|
|
||
|
/**
|
||
|
* Creates a new instance using the indicated record file and page manager.
|
||
|
*/
|
||
|
PhysicalFreeRowIdManager(PageFile file, PageManager pageman) throws IOException {
|
||
|
this.file = file;
|
||
|
this.pageman = pageman;
|
||
|
}
|
||
|
|
||
|
long getFreeRecord(final int size) throws IOException {
|
||
|
if(size >= MAX_REC_SIZE) return 0;
|
||
|
|
||
|
final PageIo root = getRootPage();
|
||
|
final int rootPageOffset = sizeToRootOffset(size+ ROOT_SLOT_SIZE);
|
||
|
final long slotPageId = root.readSixByteLong(rootPageOffset);
|
||
|
|
||
|
if(slotPageId==0){
|
||
|
file.release(root);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
PageIo slotPage = file.get(slotPageId);
|
||
|
if(slotPage.readShort(Magic.PAGE_HEADER_O_MAGIC) != Magic.PAGE_MAGIC + Magic.FREEPHYSIDS_PAGE)
|
||
|
throw new InternalError();
|
||
|
|
||
|
short recidCount = slotPage.readShort(OFFSET_SLOT_PAGE_REC_COUNT);
|
||
|
if(recidCount<=0){
|
||
|
throw new InternalError();
|
||
|
}
|
||
|
|
||
|
final int offset = (recidCount-1) * 6 + SLOT_PAGE_HEADER_SIZE;
|
||
|
final long recid = slotPage.readSixByteLong(offset);
|
||
|
|
||
|
recidCount --;
|
||
|
if(recidCount>0){
|
||
|
//decrease counter and zero out old record
|
||
|
slotPage.writeSixByteLong(offset,0);
|
||
|
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT, recidCount);
|
||
|
file.release(root);
|
||
|
file.release(slotPage);
|
||
|
}else{
|
||
|
//release this page
|
||
|
long prevSlotPageId = slotPage.readSixByteLong(OFFSET_SLOT_PAGE_NEXT);
|
||
|
root.writeSixByteLong(rootPageOffset,prevSlotPageId);
|
||
|
file.release(root);
|
||
|
file.release(slotPage);
|
||
|
pageman.free(Magic.FREEPHYSIDS_PAGE,slotPageId);
|
||
|
|
||
|
}
|
||
|
|
||
|
return recid;
|
||
|
}
|
||
|
|
||
|
static final int sizeToRootOffset(int size) {
|
||
|
return ROOT_HEADER_SIZE + 6 * (size/ROOT_SLOT_SIZE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Puts the indicated rowid on the free list, which awaits for commit
|
||
|
*/
|
||
|
void putFreeRecord(final long rowid, final int size) throws IOException {
|
||
|
//ensure capacity
|
||
|
if(inTransSize==inTrans.length){
|
||
|
inTrans = Arrays.copyOf(inTrans, inTrans.length * 2);
|
||
|
}
|
||
|
inTrans[inTransSize] = rowid + (((long)size)<<48);
|
||
|
inTransSize++;
|
||
|
}
|
||
|
|
||
|
|
||
|
public void commit() throws IOException {
|
||
|
|
||
|
if(inTransSize==0)
|
||
|
return;
|
||
|
|
||
|
Arrays.sort(inTrans,0,inTransSize-1);
|
||
|
|
||
|
|
||
|
//write all uncommited free records
|
||
|
final PageIo root = getRootPage();
|
||
|
PageIo slotPage = null;
|
||
|
for(int rowIdPos = 0; rowIdPos<inTransSize; rowIdPos++){
|
||
|
final int size = (int) (inTrans[rowIdPos] >>>48);
|
||
|
|
||
|
final long rowid = inTrans[rowIdPos] & 0x0000FFFFFFFFFFFFL;
|
||
|
final int rootPageOffset = sizeToRootOffset(size);
|
||
|
|
||
|
long slotPageId = root.readSixByteLong(rootPageOffset);
|
||
|
if(slotPageId == 0){
|
||
|
if(slotPage!=null) file.release(slotPage);
|
||
|
//create new page for this slot
|
||
|
slotPageId = pageman.allocate(Magic.FREEPHYSIDS_PAGE);
|
||
|
root.writeSixByteLong(rootPageOffset,slotPageId);
|
||
|
}
|
||
|
|
||
|
if(slotPage == null || slotPage.getPageId()!=slotPageId){
|
||
|
if(slotPage!=null) file.release(slotPage);
|
||
|
slotPage = file.get(slotPageId);
|
||
|
}
|
||
|
if(slotPage.readShort(Magic.PAGE_HEADER_O_MAGIC) != Magic.PAGE_MAGIC + Magic.FREEPHYSIDS_PAGE)
|
||
|
throw new InternalError();
|
||
|
|
||
|
short recidCount = slotPage.readShort(OFFSET_SLOT_PAGE_REC_COUNT);
|
||
|
if(recidCount== MAX_RECIDS_PER_PAGE){
|
||
|
file.release(slotPage);
|
||
|
//allocate new slot page and update links
|
||
|
final long newSlotPageId = pageman.allocate(Magic.FREEPHYSIDS_PAGE);
|
||
|
slotPage = file.get(newSlotPageId);
|
||
|
slotPage.writeSixByteLong(OFFSET_SLOT_PAGE_NEXT,slotPageId);
|
||
|
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT,(short)0);
|
||
|
recidCount = 0;
|
||
|
slotPageId = newSlotPageId;
|
||
|
root.writeSixByteLong(rootPageOffset,newSlotPageId);
|
||
|
}
|
||
|
|
||
|
//write new recid
|
||
|
slotPage.writeSixByteLong(recidCount * 6 + SLOT_PAGE_HEADER_SIZE,rowid);
|
||
|
|
||
|
//and increase count
|
||
|
recidCount++;
|
||
|
slotPage.writeShort(OFFSET_SLOT_PAGE_REC_COUNT,recidCount);
|
||
|
|
||
|
}
|
||
|
if(slotPage!=null)
|
||
|
file.release(slotPage);
|
||
|
|
||
|
file.release(root);
|
||
|
clearFreeInTrans();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void rollback() {
|
||
|
clearFreeInTrans();
|
||
|
}
|
||
|
|
||
|
private void clearFreeInTrans() {
|
||
|
if(inTrans.length>128)
|
||
|
inTrans = new long[8];
|
||
|
inTransSize = 0;
|
||
|
}
|
||
|
|
||
|
/** return free phys row page. If not found create it */
|
||
|
final PageIo getRootPage() throws IOException {
|
||
|
long pageId = pageman.getFirst(Magic.FREEPHYSIDS_ROOT_PAGE);
|
||
|
if(pageId == 0){
|
||
|
pageId = pageman.allocate(Magic.FREEPHYSIDS_ROOT_PAGE);
|
||
|
}
|
||
|
return file.get(pageId);
|
||
|
}
|
||
|
}
|