/* 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.ids; import com.db4o.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.internal.btree.*; import com.db4o.internal.slots.*; import com.db4o.marshall.*; /** * @exclude */ public class BTreeIdSystem implements StackableIdSystem { private static final int BTREE_ID_INDEX = 0; private static final int ID_GENERATOR_INDEX = 1; private static final int CHILD_ID_INDEX = 2; private final LocalObjectContainer _container; private final StackableIdSystem _parentIdSystem; private final TransactionalIdSystem _transactionalIdSystem; private final SequentialIdGenerator _idGenerator; private BTree _bTree; private PersistentIntegerArray _persistentState; public BTreeIdSystem(LocalObjectContainer container, final StackableIdSystem parentIdSystem, int maxValidId) { _container = container; _parentIdSystem = parentIdSystem; _transactionalIdSystem = container.newTransactionalIdSystem(null, new Closure4<IdSystem>() { public IdSystem run() { return parentIdSystem; } }); int persistentArrayId = parentIdSystem.childId(); if(persistentArrayId == 0){ initializeNew(); } else { initializeExisting(persistentArrayId); } _idGenerator = new SequentialIdGenerator(new Function4<Integer, Integer>() { public Integer apply(Integer start) { return findFreeId(start); } }, idGeneratorValue(), _container.handlers().lowestValidId(), maxValidId); } public BTreeIdSystem(LocalObjectContainer container, StackableIdSystem idSystem){ this(container, idSystem, Integer.MAX_VALUE); } private void initializeExisting(int persistentArrayId) { _persistentState = new PersistentIntegerArray(SlotChangeFactory.ID_SYSTEM, _transactionalIdSystem, persistentArrayId); _persistentState.read(transaction()); _bTree = new BTree(transaction(), bTreeConfiguration(), bTreeId(), new IdSlotMappingHandler()); } private BTreeConfiguration bTreeConfiguration() { return new BTreeConfiguration(_transactionalIdSystem, SlotChangeFactory.ID_SYSTEM, 64, false); } private int idGeneratorValue() { return _persistentState.array()[ID_GENERATOR_INDEX]; } private void idGeneratorValue(int value) { _persistentState.array()[ID_GENERATOR_INDEX] = value; } private int bTreeId() { return _persistentState.array()[BTREE_ID_INDEX]; } private void initializeNew() { _bTree = new BTree(transaction(), bTreeConfiguration(), new IdSlotMappingHandler()); int idGeneratorValue = _container.handlers().lowestValidId() - 1; _persistentState = new PersistentIntegerArray(SlotChangeFactory.ID_SYSTEM, _transactionalIdSystem, new int[]{_bTree.getID(), idGeneratorValue, 0 }); _persistentState.write(transaction()); _parentIdSystem.childId(_persistentState.getID()); } private int findFreeId(int start) { throw new NotImplementedException(); } public void close() { } public Slot committedSlot(int id) { IdSlotMapping mapping = (IdSlotMapping) _bTree.search(transaction(), new IdSlotMapping(id, 0, 0)); if(mapping == null){ throw new InvalidIDException(id); } return mapping.slot(); } public void completeInterruptedTransaction( int transactionId1, int transactionId2) { // do nothing } public int newId() { int id = _idGenerator.newId(); _bTree.add(transaction(), new IdSlotMapping(id, 0, 0)); return id; } private Transaction transaction(){ return _container.systemTransaction(); } public void commit(Visitable<SlotChange> slotChanges, FreespaceCommitter freespaceCommitter) { _container.freespaceManager().beginCommit(); slotChanges.accept(new Visitor4<SlotChange>() { public void visit(SlotChange slotChange) { if(! slotChange.slotModified()){ return; } _bTree.remove(transaction(), new IdSlotMapping(slotChange._key, 0, 0)); if(slotChange.removeId()){ return; } // TODO: Maybe we want a BTree that doesn't allow duplicates. // Then we could do the following in one step without removing first. _bTree.add(transaction(), new IdSlotMapping(slotChange._key, slotChange.newSlot())); if(DTrace.enabled){ DTrace.SLOT_MAPPED.logLength(slotChange._key, slotChange.newSlot()); } } }); _bTree.commit(transaction()); idGeneratorValue(_idGenerator.persistentGeneratorValue()); if(_idGenerator.isDirty()){ _idGenerator.setClean(); _persistentState.setStateDirty(); } if(_persistentState.isDirty()){ _persistentState.write(transaction()); } _container.freespaceManager().endCommit(); _transactionalIdSystem.commit(freespaceCommitter); _transactionalIdSystem.clear(); } public void returnUnusedIds(Visitable<Integer> visitable) { visitable.accept(new Visitor4<Integer>() { public void visit(Integer id) { _bTree.remove(transaction(), new IdSlotMapping(id, 0, 0)); } }); } public static class IdSlotMappingHandler implements Indexable4<IdSlotMapping> { public void defragIndexEntry(DefragmentContextImpl context) { throw new NotImplementedException(); } public IdSlotMapping readIndexEntry(Context context, ByteArrayBuffer buffer) { return IdSlotMapping.read(buffer); } public void writeIndexEntry(Context context, ByteArrayBuffer buffer, IdSlotMapping mapping) { mapping.write(buffer); } public PreparedComparison prepareComparison(Context context, final IdSlotMapping sourceMapping) { return new PreparedComparison<IdSlotMapping>() { public int compareTo(IdSlotMapping targetMapping) { return sourceMapping._id == targetMapping._id ? 0 : (sourceMapping._id < targetMapping._id ? - 1 : 1); } }; } public final int linkLength() { return Const4.INT_LENGTH * 3; } } public TransactionalIdSystem freespaceIdSystem() { return _transactionalIdSystem; } public int childId() { return _persistentState.array()[CHILD_ID_INDEX]; } public void childId(int id) { _persistentState.array()[CHILD_ID_INDEX] = id; _persistentState.setStateDirty(); } public void traverseIds(Visitor4<IdSlotMapping> visitor) { _bTree.traverseKeys(_container.systemTransaction(), visitor); } @Override public void traverseOwnSlots(Procedure4<Pair<Integer, Slot>> block) { _parentIdSystem.traverseOwnSlots(block); block.apply(ownSlotInfo(_persistentState.getID())); block.apply(ownSlotInfo(_bTree.getID())); Iterator4<Integer> nodeIds = _bTree.allNodeIds(_container.systemTransaction()); while(nodeIds.moveNext()) { block.apply(ownSlotInfo(nodeIds.current())); } } private Pair<Integer, Slot> ownSlotInfo(int id) { return Pair.of(id, _parentIdSystem.committedSlot(id)); } }