/* 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.slots.*; public class InMemoryFreespaceManager extends AbstractFreespaceManager { private final TreeIntObject _finder = new TreeIntObject(0); private Tree _freeByAddress; private Tree _freeBySize; private FreespaceListener _listener = NullFreespaceListener.INSTANCE; public InMemoryFreespaceManager(Procedure4<Slot> slotFreedCallback, int discardLimit, int remainderSizeLimit) { super(slotFreedCallback, discardLimit, remainderSizeLimit); } private void addFreeSlotNodes(int address, int length) { FreeSlotNode addressNode = new FreeSlotNode(address); addressNode.createPeer(length); _freeByAddress = Tree.add(_freeByAddress, addressNode); addToFreeBySize(addressNode._peer); } private void addToFreeBySize(FreeSlotNode node) { _freeBySize = Tree.add(_freeBySize, node); _listener.slotAdded(node._key); } public Slot allocateTransactionLogSlot(int length) { FreeSlotNode sizeNode = (FreeSlotNode) Tree.last(_freeBySize); if(sizeNode == null || sizeNode._key < length){ return null; } // We can just be appending to the end of the file, using one // really big contigous slot that keeps growing. Let's limit. int limit = length + 100; if(sizeNode._key > limit){ return allocateSlot(limit); } removeFromBothTrees(sizeNode); return new Slot(sizeNode._peer._key, sizeNode._key); } public Slot allocateSafeSlot(int length) { return allocateSlot(length); } public void freeSafeSlot(Slot slot) { if(Debug4.xbytes){ Procedure4<Slot> temp = _slotFreedCallback; _slotFreedCallback = null; free(slot); _slotFreedCallback = temp; return; } free(slot); } public void beginCommit() { // do nothing } public void commit() { // do nothing } public void endCommit() { // do nothing } public void free(final Slot slot) { int address = slot.address(); if (address <= 0) { throw new IllegalArgumentException(); } int length = slot.length(); if(DTrace.enabled){ DTrace.FREESPACEMANAGER_RAM_FREE.logLength(address, length); } _finder._key = address; FreeSlotNode sizeNode; FreeSlotNode addressnode = (FreeSlotNode) Tree.findSmaller(_freeByAddress, _finder); if ((addressnode != null) && ((addressnode._key + addressnode._peer._key) == address)) { sizeNode = addressnode._peer; removeFromFreeBySize(sizeNode); sizeNode._key += length; FreeSlotNode secondAddressNode = (FreeSlotNode) Tree .findGreaterOrEqual(_freeByAddress, _finder); if ((secondAddressNode != null) && (address + length == secondAddressNode._key)) { sizeNode._key += secondAddressNode._peer._key; removeFromBothTrees(secondAddressNode._peer); } sizeNode.removeChildren(); addToFreeBySize(sizeNode); } else { addressnode = (FreeSlotNode) Tree.findGreaterOrEqual( _freeByAddress, _finder); if ((addressnode != null) && (address + length == addressnode._key)) { sizeNode = addressnode._peer; removeFromBothTrees(sizeNode); sizeNode._key += length; addressnode._key = address; addressnode.removeChildren(); sizeNode.removeChildren(); _freeByAddress = Tree.add(_freeByAddress, addressnode); addToFreeBySize(sizeNode); } else { if (canDiscard(length)) { return; } addFreeSlotNodes(address, length); } } slotFreed(slot); } public void freeSelf() { // Do nothing. // The RAM manager frees itself on reading. } public Slot allocateSlot(int length) { _finder._key = length; _finder._object = null; _freeBySize = FreeSlotNode.removeGreaterOrEqual((FreeSlotNode) _freeBySize, _finder); if (_finder._object == null) { return null; } FreeSlotNode node = (FreeSlotNode) _finder._object; _listener.slotRemoved(node._key); int blocksFound = node._key; int address = node._peer._key; _freeByAddress = _freeByAddress.removeNode(node._peer); int remainingBlocks = blocksFound - length; if(splitRemainder(remainingBlocks)){ addFreeSlotNodes(address + length, remainingBlocks); }else{ length = blocksFound; } if(DTrace.enabled){ DTrace.FREESPACEMANAGER_GET_SLOT.logLength(address, length); } return new Slot(address, length); } int marshalledLength() { return TreeInt.marshalledLength((TreeInt)_freeBySize); } private void read(ByteArrayBuffer reader) { FreeSlotNode.sizeLimit = discardLimit(); _freeBySize = new TreeReader(reader, new FreeSlotNode(0), true).read(); final ByRef<Tree> addressTree = ByRef.newInstance(); if (_freeBySize != null) { _freeBySize.traverse(new Visitor4() { public void visit(Object a_object) { FreeSlotNode node = ((FreeSlotNode) a_object)._peer; addressTree.value = Tree.add(addressTree.value, node); } }); } _freeByAddress = addressTree.value; } public void read(LocalObjectContainer container, Slot slot){ if(Slot.isNull(slot)){ return; } ByteArrayBuffer buffer = container.readBufferBySlot(slot); if (buffer == null) { return; } read(buffer); if(! Debug4.freespace){ container.free(slot); } } private void removeFromBothTrees(FreeSlotNode sizeNode){ removeFromFreeBySize(sizeNode); _freeByAddress = _freeByAddress.removeNode(sizeNode._peer); } private void removeFromFreeBySize(FreeSlotNode node) { _freeBySize = _freeBySize.removeNode(node); _listener.slotRemoved(node._key); } public int slotCount() { return Tree.size(_freeByAddress); } public void start(int id) { // this is done in read(), nothing to do here } public byte systemType() { return FM_RAM; } public String toString(){ final StringBuffer sb = new StringBuffer(); sb.append("RAM FreespaceManager\n"); sb.append("Address Index\n"); _freeByAddress.traverse(new ToStringVisitor(sb)); sb.append("Length Index\n"); _freeBySize.traverse(new ToStringVisitor(sb)); return sb.toString(); } public void traverse(final Visitor4<Slot> visitor) { if (_freeByAddress == null) { return; } _freeByAddress.traverse(new Visitor4() { public void visit(Object a_object) { FreeSlotNode fsn = (FreeSlotNode) a_object; int address = fsn._key; int length = fsn._peer._key; visitor.visit(new Slot(address, length)); } }); } public void write(LocalObjectContainer container){ Slot slot = container.allocateSlot(marshalledLength()); while(slot.length() < marshalledLength()){ // This can happen if DatabaseGrowthSize is configured. // Allocating a slot may produce an additional entry // in this FreespaceManager. container.free(slot); slot = container.allocateSlot(marshalledLength()); } ByteArrayBuffer buffer = new ByteArrayBuffer(slot.length()); TreeInt.write(buffer, (TreeInt)_freeBySize); container.writeEncrypt(buffer, slot.address(), 0); container.systemData().inMemoryFreespaceSlot(slot); } final static class ToStringVisitor implements Visitor4 { private final StringBuffer _sb; ToStringVisitor(StringBuffer sb) { _sb = sb; } public void visit(Object obj) { _sb.append(obj); _sb.append("\n"); } } public void listener(FreespaceListener listener) { _listener = listener; } public boolean isStarted() { return true; } }