btree.java
来自「RESIN 3.2 最新源码」· Java 代码 · 共 1,613 行 · 第 1/3 页
JAVA
1,613 行
/* * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */package com.caucho.db.index;import com.caucho.db.Database;import com.caucho.db.store.Block;import com.caucho.db.store.BlockManager;import com.caucho.db.store.Lock;import com.caucho.db.store.Store;import com.caucho.db.store.Transaction;import com.caucho.log.Log;import com.caucho.sql.SQLExceptionWrapper;import com.caucho.util.L10N;import com.caucho.vfs.Path;import java.io.IOException;import java.sql.SQLException;import java.util.ArrayList;import java.util.logging.Level;import java.util.logging.Logger;/** * Structure of the table: * * <pre> * b4 - flags * b4 - length * b8 - parent * b8 - next * tuples* * </pre> * * Structure of a tuple: * * <pre> * b8 - ptr to the actual data * key - the tuple's key * </pre> */public final class BTree { private final static L10N L = new L10N(BTree.class); private final static Logger log = Log.open(BTree.class); public final static long FAIL = 0; private final static int BLOCK_SIZE = Store.BLOCK_SIZE; private final static int PTR_SIZE = 8; private final static int FLAGS_OFFSET = 0; private final static int LENGTH_OFFSET = FLAGS_OFFSET + 4; private final static int PARENT_OFFSET = LENGTH_OFFSET + 4; private final static int NEXT_OFFSET = PARENT_OFFSET + PTR_SIZE; private final static int HEADER_SIZE = NEXT_OFFSET + PTR_SIZE; private final static int LEAF_FLAG = 1; private BlockManager _blockManager; private final Lock _lock; private Store _store; private long _rootBlockId; private Block _rootBlock; private int _keySize; private int _tupleSize; private int _n; private int _minN; private KeyCompare _keyCompare; private int _blockCount; private volatile boolean _isStarted; /** * Creates a new BTree with the given backing. * * @param store the underlying store containing the btree. */ public BTree(Store store, long rootBlockId, int keySize, KeyCompare keyCompare) throws IOException { if (keyCompare == null) throw new NullPointerException(); _store = store; _blockManager = _store.getBlockManager(); _rootBlockId = rootBlockId; _rootBlock = store.readBlock(rootBlockId); _lock = new Lock("index:" + store.getName()); if (BLOCK_SIZE < keySize + HEADER_SIZE) throw new IOException(L.l("BTree key size `{0}' is too large.", keySize)); _keySize = keySize; _tupleSize = keySize + PTR_SIZE; _n = (BLOCK_SIZE - HEADER_SIZE) / _tupleSize; _minN = (_n + 1) / 2; if (_minN < 0) _minN = 1; _keyCompare = keyCompare; } /** * Returns the index root. */ public long getIndexRoot() { return _rootBlockId; } /** * Creates and initializes the btree. */ public void create() throws IOException { } public long lookup(byte []keyBuffer, int keyOffset, int keyLength, Transaction xa) throws IOException, SQLException { return lookup(keyBuffer, keyOffset, keyLength, xa, _rootBlockId); } private long lookup(byte []keyBuffer, int keyOffset, int keyLength, Transaction xa, long blockId) throws IOException, SQLException { Block block; if (blockId == _rootBlockId) { block = _rootBlock; block.allocate(); } else block = _store.readBlock(blockId); try { Lock blockLock = block.getLock(); xa.lockRead(blockLock); try { byte []buffer = block.getBuffer(); boolean isLeaf = isLeaf(buffer); long value = lookupTuple(blockId, buffer, keyBuffer, keyOffset, keyLength, isLeaf); if (isLeaf || value == FAIL) return value; else return lookup(keyBuffer, keyOffset, keyLength, xa, value); } finally { xa.unlockRead(blockLock); } } finally { block.free(); } } /** * Inserts the new value for the given key. * * @return false if the block needs to be split */ public void insert(byte []keyBuffer, int keyOffset, int keyLength, long value, Transaction xa, boolean isOverride) throws SQLException { try { while (! insert(keyBuffer, keyOffset, keyLength, value, xa, isOverride, _rootBlockId)) { splitRoot(_rootBlockId, xa); } } catch (IOException e) { log.log(Level.FINE, e.toString(), e); throw new SQLExceptionWrapper(e.toString(), e); } } /** * Inserts the new value for the given key. * * @return false if the block needs to be split */ private boolean insert(byte []keyBuffer, int keyOffset, int keyLength, long value, Transaction xa, boolean isOverride, long blockId) throws IOException, SQLException { Block block; if (blockId == _rootBlockId) { block = _rootBlock; block.allocate(); } else block = _store.readBlock(blockId); try { Lock blockLock = block.getLock(); xa.lockRead(blockLock); try { byte []buffer = block.getBuffer(); int length = getLength(buffer); if (length == _n) { // return false if the block needs to be split return false; } if (isLeaf(buffer)) { insertValue(keyBuffer, keyOffset, keyLength, value, xa, isOverride, block); return true; } long childBlockId = lookupTuple(blockId, buffer, keyBuffer, keyOffset, keyLength, false); while (! insert(keyBuffer, keyOffset, keyLength, value, xa, isOverride, childBlockId)) { split(block, childBlockId, xa); childBlockId = lookupTuple(blockId, buffer, keyBuffer, keyOffset, keyLength, false); } return true; } finally { xa.unlockRead(blockLock); } } finally { block.free(); } } /** * Inserts into the next block given the current block and the given key. */ private void insertValue(byte []keyBuffer, int keyOffset, int keyLength, long value, Transaction xa, boolean isOverride, Block block) throws IOException, SQLException { byte []buffer = block.getBuffer(); Lock blockLock = block.getLock(); xa.lockWrite(blockLock); try { block.setFlushDirtyOnCommit(false); block.setDirty(0, Store.BLOCK_SIZE); insertLeafBlock(block.getBlockId(), buffer, keyBuffer, keyOffset, keyLength, value, isOverride); } finally { xa.unlockWrite(blockLock); } } /** * Inserts into the next block given the current block and the given key. */ private long insertLeafBlock(long blockId, byte []buffer, byte []keyBuffer, int keyOffset, int keyLength, long value, boolean isOverride) throws IOException, SQLException { int offset = HEADER_SIZE; int tupleSize = _tupleSize; int length = getLength(buffer); for (int i = 0; i < length; i++) { int cmp = _keyCompare.compare(keyBuffer, keyOffset, buffer, offset + PTR_SIZE, keyLength); if (0 < cmp) { offset += tupleSize; continue; } else if (cmp == 0) { if (! isOverride) { long oldValue = getPointer(buffer, offset); if (value != oldValue) throw new SQLException(L.l("'{0}' insert of key '{1}' fails index uniqueness.", _store, _keyCompare.toString(keyBuffer, keyOffset, keyLength))); } setPointer(buffer, offset, value); //writeBlock(blockIndex, block); return 0; } else if (length < _n) { return addKey(blockId, buffer, offset, i, length, keyBuffer, keyOffset, keyLength, value); } else { throw new IllegalStateException("ran out of key space"); } } if (length < _n) { return addKey(blockId, buffer, offset, length, length, keyBuffer, keyOffset, keyLength, value); } throw new IllegalStateException(); // return split(blockIndex, block); } private long addKey(long blockId, byte []buffer, int offset, int index, int length, byte []keyBuffer, int keyOffset, int keyLength, long value) throws IOException { int tupleSize = _tupleSize; if (index < length) { if (offset + tupleSize < HEADER_SIZE) throw new IllegalStateException(); System.arraycopy(buffer, offset, buffer, offset + tupleSize, (length - index) * tupleSize); } setPointer(buffer, offset, value); setLength(buffer, length + 1); if (log.isLoggable(Level.FINEST)) log.finest("btree insert at " + debugId(blockId) + ":" + offset + " value:" + debugId(value)); if (offset + PTR_SIZE < HEADER_SIZE) throw new IllegalStateException(); System.arraycopy(keyBuffer, keyOffset, buffer, offset + PTR_SIZE, keyLength); for (int j = PTR_SIZE + keyLength; j < tupleSize; j++) buffer[offset + j] = 0; return -value; } /** * The length in lBuf is assumed to be the length of the buffer. */ private void split(Block parent, long blockId, Transaction xa) throws IOException, SQLException { Lock parentLock = parent.getLock(); xa.lockWrite(parentLock); try { Block block = _store.readBlock(blockId); try { Lock blockLock = block.getLock(); xa.lockReadAndWrite(blockLock); try { split(parent, block, xa); } finally { xa.unlockReadAndWrite(blockLock); } } finally { block.free(); } } finally { xa.unlockWrite(parentLock); } } /** * The length in lBuf is assumed to be the length of the buffer. */ private void split(Block parentBlock, Block block, Transaction xa) throws IOException, SQLException { long parentId = parentBlock.getBlockId(); long blockId = block.getBlockId(); log.finer("btree splitting " + debugId(blockId)); block.setFlushDirtyOnCommit(false); block.setDirty(0, Store.BLOCK_SIZE); byte []buffer = block.getBuffer(); int length = getLength(buffer); // Check length to avoid possible timing issue, since we release the // read lock for the block between the initial check in insert() and // getting it back in split() if (length < _n / 2) return; if (length < 2) throw new IllegalStateException(L.l("illegal length '{0}' for block {1}", length, debugId(blockId))); //System.out.println("INDEX SPLIT: " + debugId(blockId) + " " + length + " " + block + " " + buffer); Block leftBlock = null; try { parentBlock.setFlushDirtyOnCommit(false); parentBlock.setDirty(0, Store.BLOCK_SIZE); byte []parentBuffer = parentBlock.getBuffer(); int parentLength = getLength(parentBuffer); leftBlock = _store.allocateIndexBlock(); leftBlock.setFlushDirtyOnCommit(false); leftBlock.setDirty(0, Store.BLOCK_SIZE); byte []leftBuffer = leftBlock.getBuffer(); long leftBlockId = leftBlock.getBlockId(); int pivot = length / 2; int pivotSize = pivot * _tupleSize; int pivotEnd = HEADER_SIZE + pivotSize; System.arraycopy(buffer, HEADER_SIZE, leftBuffer, HEADER_SIZE, pivotSize); setInt(leftBuffer, FLAGS_OFFSET, getInt(buffer, FLAGS_OFFSET)); setLength(leftBuffer, pivot); // XXX: NEXT_OFFSET needs to work with getRightIndex setPointer(leftBuffer, NEXT_OFFSET, 0); setPointer(leftBuffer, PARENT_OFFSET, parentId); System.arraycopy(buffer, pivotEnd, buffer, HEADER_SIZE, length * _tupleSize - pivotEnd); setLength(buffer, length - pivot); insertLeafBlock(parentId, parentBuffer, leftBuffer, pivotEnd - _tupleSize + PTR_SIZE, _keySize, leftBlockId, true); } finally { if (leftBlock != null) leftBlock.free(); } } /** * The length in lBuf is assumed to be the length of the buffer. */ private void splitRoot(long rootBlockId, Transaction xa) throws IOException, SQLException
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?