/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: BTree.java * * Copyright (c) 2009 Sun Microsystems and Static Free Software * * Electric(tm) 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 3 of the License, or * (at your option) any later version. * * Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.database.geometry.btree; import java.io.*; import java.util.*; import com.sun.electric.database.geometry.btree.unboxed.*; import com.sun.electric.database.geometry.btree.CachingPageStorage.CachedPage; /** * Page format: * * int: isRightMost (1 or 0) * int: number of buckets * int: pageid of first bucket * S: summary of first bucket * repeat * int: number of values in (N-1)^th bucket * key: least key in N^th bucket * int: pageid of N^th bucket * S: summary of N^th bucket * * The arrangement above was chosen to deliberately avoid tracking * the number of values in the last bucket. This ensures that we * can append new values to the last bucket of the rightmost leaf * without touching any interior nodes (unless we're forced to * split, of course, but if the pages are large enough that doesn't * happen very often). This maintains the O(1)-unless-we-split * fastpath for appends while still keeping enough interior data to * perform ordinal queries. * * Note that we still need to keep the summary for the last bucket, * because we don't know as much about its semantics. This is just * one of the reasons why "number of values below here" is computed * separately rather than being part of the summary. */ class InteriorNodeCursor <K extends Serializable & Comparable, V extends Serializable, S extends Serializable> extends NodeCursor<K,V,S> { private final int INTERIOR_HEADER_SIZE; private final int INTERIOR_ENTRY_SIZE; private final int INTERIOR_MAX_BUCKETS; private final int SIZEOF_SUMMARY; private int numbuckets; // just a cache public InteriorNodeCursor(BTree<K,V,S> bt) { super(bt); this.SIZEOF_SUMMARY = bt.ao==null ? 0 : bt.ao.getSize(); this.INTERIOR_HEADER_SIZE = 2*SIZEOF_INT; this.INTERIOR_ENTRY_SIZE = bt.uk.getSize() + SIZEOF_SUMMARY + SIZEOF_INT + SIZEOF_INT; this.INTERIOR_MAX_BUCKETS = ((ps.getPageSize()-INTERIOR_HEADER_SIZE-SIZEOF_INT-this.SIZEOF_SUMMARY) / INTERIOR_ENTRY_SIZE); } /** * Creates a new bucket for a child at index "idx" by shifting * over the child previously in that bucket (if any) and all * after it. Returns the offset in the buffer at which to write * the least key beneath the new child. Note that the value in * the bucket and the summary for that bucket are not * initialized. */ public int insertNewBucketAt(int idx) { assert !isFull(); assert idx!=0; if (idx < getNumBuckets()) System.arraycopy(getBuf(), INTERIOR_HEADER_SIZE + (idx-1)*INTERIOR_ENTRY_SIZE, getBuf(), INTERIOR_HEADER_SIZE + idx*INTERIOR_ENTRY_SIZE, endOfBuf() - (INTERIOR_HEADER_SIZE + (idx-1)*INTERIOR_ENTRY_SIZE)); setNumBuckets(getNumBuckets()+1); return INTERIOR_HEADER_SIZE + idx*INTERIOR_ENTRY_SIZE - bt.uk.getSize(); } public static boolean isInteriorNode(byte[] buf) { return UnboxedInt.instance.deserializeInt(buf, 1*SIZEOF_INT)!=0; } public int getMaxBuckets() { return INTERIOR_MAX_BUCKETS; } public void initBuf(CachedPage cp, boolean isRightMost) { super.setBuf(cp); setRightMost(isRightMost); } public int getNumBuckets() { return numbuckets; } protected void setNumBuckets(int num) { bt.ui.serializeInt(numbuckets = num, getBuf(), 1*SIZEOF_INT); } public void setBuf(CachedPage cp) { super.setBuf(cp); numbuckets = bt.ui.deserializeInt(getBuf(), 1*SIZEOF_INT); } /** Initialize a new root node. */ public void initRoot() { bt.rootpage = ps.createPage(); super.setBuf(ps.getPage(bt.rootpage, false)); setNumBuckets(1); setRightMost(true); } public boolean isLeafNode() { return false; } protected int endOfBuf() { return INTERIOR_HEADER_SIZE + getNumBuckets()*INTERIOR_ENTRY_SIZE - SIZEOF_SUMMARY - SIZEOF_INT; } public int getBucketPageId(int idx) { return bt.ui.deserializeInt(getBuf(), INTERIOR_HEADER_SIZE+INTERIOR_ENTRY_SIZE*idx); } public void setBucketPageId(int idx, int pageid) { bt.ui.serializeInt(pageid, getBuf(), INTERIOR_HEADER_SIZE+INTERIOR_ENTRY_SIZE*idx); } public int compare(byte[] key, int key_ofs, int keynum) { if (keynum<=0) return 1; if (keynum>=getNumBuckets()) return -1; return bt.uk.compare(key, key_ofs, getBuf(), INTERIOR_HEADER_SIZE + keynum*INTERIOR_ENTRY_SIZE - bt.uk.getSize()); } protected void scoot(byte[] oldBuf, int endOfBuf, int splitPoint) { int len = INTERIOR_HEADER_SIZE + INTERIOR_ENTRY_SIZE*(splitPoint); System.arraycopy(oldBuf, len, getBuf(), INTERIOR_HEADER_SIZE, endOfBuf - len); } public void getKey(int keynum, byte[] key, int key_ofs) { System.arraycopy(getBuf(), INTERIOR_HEADER_SIZE + keynum*INTERIOR_ENTRY_SIZE - bt.uk.getSize(), key, key_ofs, bt.uk.getSize()); } public void setNumValsBelowBucket(int idx, int num) { if (idx==getNumBuckets()-1) throw new RuntimeException("InteriorNodeCursors don't store numValuesBelowBucket() for their last bucket"); assert idx>=0 && idx<getNumBuckets()-1; bt.ui.serializeInt(num, getBuf(), INTERIOR_HEADER_SIZE+SIZEOF_INT+SIZEOF_SUMMARY+INTERIOR_ENTRY_SIZE*idx); } public int getNumValsBelowBucket(int bucket) { if (bucket>=getNumBuckets()) return 0; // FIXME: should be an assertion if (bucket==getNumBuckets()-1) throw new RuntimeException("InteriorNodeCursors don't store numValuesBelowBucket() for their last bucket"); return bt.ui.deserializeInt(getBuf(), INTERIOR_HEADER_SIZE+SIZEOF_INT+SIZEOF_SUMMARY+INTERIOR_ENTRY_SIZE*bucket); } public void multiplySummaryCommutative(int idx, byte[] buf, int ofs) { if (idx==getNumBuckets()-1 && isRightMost()) throw new RuntimeException("RightMost InteriorNodeCursors don't store a summary value for their last bucket"); assert idx>=0 && idx<getNumBuckets(); bt.ao.multiply(buf, ofs, getBuf(), INTERIOR_HEADER_SIZE+SIZEOF_INT+INTERIOR_ENTRY_SIZE*idx, getBuf(), INTERIOR_HEADER_SIZE+SIZEOF_INT+INTERIOR_ENTRY_SIZE*idx); } public void getSummary(int idx, byte[] buf, int ofs) { if (idx==getNumBuckets()-1 && isRightMost()) throw new RuntimeException("RightMost InteriorNodeCursors don't store a summary value for their last bucket"); assert idx>=0 && idx<getNumBuckets(); System.arraycopy(getBuf(), INTERIOR_HEADER_SIZE+SIZEOF_INT+INTERIOR_ENTRY_SIZE*idx, buf, ofs, bt.ao.getSize()); } }