/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.internal.freespace; import com.db4o.*; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.internal.btree.*; import com.db4o.internal.ids.*; import com.db4o.internal.slots.*; /** * @exclude */ public class BTreeFreespaceManager extends AbstractFreespaceManager { private final LocalObjectContainer _file; private InMemoryFreespaceManager _delegate; private BTree _slotsByAddress; private BTree _slotsByLength; private PersistentIntegerArray _idArray; private int _delegationRequests; private FreespaceListener _listener = NullFreespaceListener.INSTANCE; private TransactionalIdSystem _idSystem; public BTreeFreespaceManager(LocalObjectContainer file, Procedure4<Slot> slotFreedCallback, int discardLimit, int remainderSizeLimit) { super(slotFreedCallback, discardLimit, remainderSizeLimit); _file = file; _delegate = new InMemoryFreespaceManager(slotFreedCallback, discardLimit, remainderSizeLimit); _idSystem = file.systemData().freespaceIdSystem(); } private void addSlot(Slot slot) { _slotsByLength.add(transaction(), slot); _slotsByAddress.add(transaction(), slot); _listener.slotAdded(slot.length()); } public Slot allocateSafeSlot(int length) { return _delegate.allocateSafeSlot(length); } public void beginCommit() { beginDelegation(); } private void beginDelegation(){ _delegationRequests++; } public void commit() { _slotsByAddress.commit(transaction()); _slotsByLength.commit(transaction()); } private void createBTrees(int addressID, int lengthID) { BTreeConfiguration config = new BTreeConfiguration(_idSystem, SlotChangeFactory.FREE_SPACE, 64, false); _slotsByAddress = new BTree(transaction(), config, addressID, new AddressKeySlotHandler()); _slotsByLength = new BTree(transaction(), config, lengthID, new LengthKeySlotHandler()); } public void endCommit() { endDelegation(); } private void endDelegation(){ _delegationRequests--; } public void free(Slot slot) { if(! isStarted()){ return; } if(isDelegating()){ _delegate.free(slot); return; } try{ beginDelegation(); if(DTrace.enabled){ DTrace.FREESPACEMANAGER_BTREE_FREE.logLength(slot.address(), slot.length()); } Slot remove[] = new Slot[2]; Slot newFreeSlot = slot; BTreePointer pointer = searchBTree(_slotsByAddress, slot, SearchTarget.LOWEST); BTreePointer previousPointer = pointer != null ? pointer.previous() : _slotsByAddress.lastPointer(transaction()); if(previousPointer != null){ Slot previousSlot = (Slot) previousPointer.key(); if(previousSlot.isDirectlyPreceding(newFreeSlot)){ remove[0] = previousSlot; newFreeSlot = previousSlot.append(newFreeSlot); } } if(pointer != null){ Slot nextSlot = (Slot) pointer.key(); if(newFreeSlot.isDirectlyPreceding(nextSlot)){ remove[1] = nextSlot; newFreeSlot = newFreeSlot.append(nextSlot); } } for (int i = 0; i < remove.length; i++) { if(remove[i] != null){ removeSlot(remove[i]); } } if(! canDiscard(newFreeSlot.length())){ addSlot(newFreeSlot); } slotFreed(slot); } finally{ endDelegation(); } } public void freeSelf() { _slotsByAddress.free(transaction()); _slotsByLength.free(transaction()); } public void freeSafeSlot(Slot slot) { _delegate.freeSafeSlot(slot); } public Slot allocateSlot (int length) { if(! isStarted()){ return null; } if(isDelegating()){ return _delegate.allocateSlot(length); } try{ beginDelegation(); BTreePointer pointer = searchBTree(_slotsByLength, new Slot(0, length), SearchTarget.HIGHEST); if(pointer == null){ return null; } Slot slot = (Slot) pointer.key(); removeSlot(slot); int remainingLength = slot.length() - length; if(splitRemainder(remainingLength)){ addSlot(slot.subSlot(length)); slot = slot.truncate(length); } if(DTrace.enabled){ DTrace.FREESPACEMANAGER_GET_SLOT.logLength(slot.address(), slot.length()); } return slot; } finally{ endDelegation(); } } private void initializeExisting(int id) { _idArray = new PersistentIntegerArray(SlotChangeFactory.FREE_SPACE, _idSystem, id); _idArray.read(transaction()); int[] ids = _idArray.array(); int addressId = ids[0]; int lengthID = ids[1]; createBTrees(addressId, lengthID); _slotsByAddress.read(transaction()); _slotsByLength.read(transaction()); _delegate.read(_file, _file.systemData().inMemoryFreespaceSlot()); } private void initializeNew() { createBTrees(0 , 0); _slotsByAddress.write(transaction()); _slotsByLength.write(transaction()); int[] ids = new int[] { _slotsByAddress.getID(), _slotsByLength.getID()}; _idArray = new PersistentIntegerArray(SlotChangeFactory.FREE_SPACE, _idSystem, ids); _idArray.write(transaction()); _file.systemData().bTreeFreespaceId(_idArray.getID()); } private boolean isDelegating(){ return _delegationRequests > 0; } public void read(LocalObjectContainer container, int freeSpaceID) { // do nothing // reading happens in start( ) } private void removeSlot(Slot slot) { _slotsByLength.remove(transaction(), slot); _slotsByAddress.remove(transaction(), slot); _listener.slotRemoved(slot.length()); } private BTreePointer searchBTree(BTree bTree, Slot slot, SearchTarget target) { BTreeNodeSearchResult searchResult = bTree.searchLeafByObject(transaction(), slot, target); return searchResult.firstValidPointer(); } public int slotCount() { return _slotsByAddress.size(transaction()) + _delegate.slotCount(); } public void start(int id) { try{ beginDelegation(); if(id == 0){ initializeNew(); }else{ initializeExisting(id); } }finally{ endDelegation(); } } public boolean isStarted(){ return _idArray != null; } public byte systemType() { return FM_BTREE; } public String toString() { return _slotsByLength.toString(); } public int totalFreespace() { return super.totalFreespace() + _delegate.totalFreespace(); } public void traverse(final Visitor4 visitor) { _slotsByAddress.traverseKeys(transaction(), visitor); } public void migrateTo(final FreespaceManager fm) { super.migrateTo(fm); _delegate.migrateTo(fm); } public void write(LocalObjectContainer container) { try{ beginDelegation(); _delegate.write(container); container.systemData().bTreeFreespaceId(_idArray.getID()); }finally{ endDelegation(); } } public void listener(FreespaceListener listener) { _listener = listener; } private final LocalTransaction transaction(){ return (LocalTransaction)_file.systemTransaction(); } public Slot allocateTransactionLogSlot(int length) { return _delegate.allocateTransactionLogSlot(length); } public void read(LocalObjectContainer container, Slot slot) { // do nothing // everything happens in start } }