/******************************************************************************* * 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; /** * Class describing a page that holds physical rowids that were freed. */ final class FreePhysicalRowIdPage extends PageHeader { static final short FreePhysicalRowId_O_SIZE = PhysicalRowId_SIZE; // int size static final short FreePhysicalRowId_SIZE = FreePhysicalRowId_O_SIZE + Magic.SZ_INT; // offsets private static final short O_COUNT = PageHeader.SIZE; // short count static final short O_FREE = O_COUNT + Magic.SZ_SHORT; final short ELEMS_PER_PAGE; /** * Used to place a limit on the wasted capacity resulting in a modified first fit policy for re-allocated of free * records. This value is the maximum first fit waste that is accepted when scanning the available slots on a given * page of the free physical row page list. */ static public final transient int wasteMargin = 128; /** * Used to place a limit on the wasted capacity resulting in a modified first fit policy for re-allocated of free * records. This value is the upper bound of waste that is accepted before scanning another page on the free * physical row page list. If no page can be found whose waste for the re-allocation request would be less than this * value then a new page will be allocated and the requested physical row will be allocated from that new page. */ static public final transient int wasteMargin2 = PageHeader.SIZE / 4; // // slots we returned. // FreePhysicalRowId[] slots = new FreePhysicalRowId[ELEMS_PER_PAGE]; final int[] sizeCache; /** * Constructs a data page view from the indicated block. */ FreePhysicalRowIdPage(BlockIo block, int blockSize) { super(block); ELEMS_PER_PAGE = (short) ((blockSize - O_FREE) / FreePhysicalRowId_SIZE); sizeCache = new int[ELEMS_PER_PAGE]; for(int i = 0;i<ELEMS_PER_PAGE;i++) sizeCache[i] = -1; } /** * Factory method to create or return a data page for the indicated block. */ static FreePhysicalRowIdPage getFreePhysicalRowIdPageView(BlockIo block,int pageSize) { BlockView view = block.getView(); if (view != null && view instanceof FreePhysicalRowIdPage) return (FreePhysicalRowIdPage) view; else return new FreePhysicalRowIdPage(block, pageSize); } /** Returns the number of free rowids */ short getCount() { return block.readShort(O_COUNT); } /** Sets the number of free rowids */ protected void setCount(short i) { block.writeShort(O_COUNT, i); } /** Frees a slot */ void free(int slot) { short pos = slotToOffset(slot); FreePhysicalRowId_setSize(pos, 0); //get(slot).setSize(0); setCount((short) (getCount() - 1)); } /** Allocates a slot */ short alloc(int slot) { setCount((short) (getCount() + 1)); return slotToOffset(slot); } /** Returns true if a slot is allocated */ boolean isAllocated(int slot) { short pos = slotToOffset(slot); return FreePhysicalRowId_getSize(pos) != 0; } /** Returns true if a slot is free */ boolean isFree(int slot) { return !isAllocated(slot); } // /** Returns the value of the indicated slot */ // FreePhysicalRowId get(int slot) { // if (slots[slot] == null) { // slots[slot] = new FreePhysicalRowId(block, slotToOffset(slot)); // } // return slots[slot]; // } /** Converts slot to offset */ short slotToOffset(int slot) { return (short) (O_FREE + (slot * FreePhysicalRowId_SIZE)); } int offsetToSlot(short pos) { int pos2 = pos; return (pos2 - O_FREE)/FreePhysicalRowId_SIZE; } /** * Returns first free slot, -1 if no slots are available */ int getFirstFree() { for (int i = 0; i < ELEMS_PER_PAGE; i++) { if (isFree(i)) return i; } return -1; } /** * Returns first slot with available size >= indicated size, or minus maximal size available on this page * * @param size * The requested allocation size. * **/ int getFirstLargerThan(int size) { int maxSize = 0; /* * Tracks slot of the smallest available physical row on the page. */ int bestSlot = -1; /* * Tracks size of the smallest available physical row on the page. */ int bestSlotSize = 0; /* * Scan each slot in the page. */ for (int i = 0; i < ELEMS_PER_PAGE; i++) { /* * When large allocations are used, the space wasted by the first fit policy can become very large (25% of * the store). The first fit policy has been modified to only accept a record with a maximum amount of * wasted capacity given the requested allocation size. */ // Note: isAllocated(i) is equiv to get(i).getSize() != 0 //long theSize = get(i).getSize(); // capacity of this free record. short pos = slotToOffset(i); int theSize = FreePhysicalRowId_getSize(pos); // capacity of this free record. if(theSize>maxSize) maxSize = theSize; int waste = theSize - size; // when non-negative, record has suf. capacity. if (waste >= 0) { if (waste < wasteMargin) { return i; // record has suf. capacity and not too much waste. } else if (bestSlotSize >= size) { /* * This slot is a better fit that any that we have seen so far on this page so we update the slot# * and available size for that slot. */ bestSlot = i; bestSlotSize = size; } } } if (bestSlot != -1) { /* * An available slot was identified that is large enough, but it exceeds the first wasted capacity limit. At * this point we check to see whether it is under our second wasted capacity limit. If it is, then we return * that slot. */ long waste = bestSlotSize - size; // when non-negative, record has suf. capacity. if (waste >= 0 && waste < wasteMargin2) { // record has suf. capacity and not too much waste. return bestSlot; } /* * Will scan next page on the free physical row page list. */ } return -maxSize; } public long slotToLocation(int slot) { short pos = slotToOffset(slot); return Location.toLong(getLocationBlock(pos),getLocationOffset(pos)); } /** Returns the size */ int FreePhysicalRowId_getSize(short pos) { int slot = offsetToSlot(pos); if(sizeCache[slot] == -1) sizeCache[slot] = block.readInt(pos + FreePhysicalRowId_O_SIZE); return sizeCache[slot]; } /** Sets the size */ void FreePhysicalRowId_setSize(short pos,int value) { int slot = offsetToSlot(pos); sizeCache[slot] = value; block.writeInt(pos + FreePhysicalRowId_O_SIZE, value); } }