Quadcap Embeddable Database

com/quadcap/sql/index/BtreeCursor.java

Go to the documentation of this file.
00001 package com.quadcap.sql.index; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.BufferedReader; 00042 import java.io.InputStreamReader; 00043 import java.io.IOException; 00044 00045 import java.util.Properties; 00046 00047 import com.quadcap.sql.file.Block; 00048 import com.quadcap.sql.file.BlockFile; 00049 import com.quadcap.sql.file.ByteUtil; 00050 import com.quadcap.sql.file.SubPageManager; 00051 00052 import com.quadcap.sql.lock.PooledObject; 00053 import com.quadcap.sql.lock.ObjectPool; 00054 00055 import com.quadcap.util.Debug; 00056 import com.quadcap.util.Util; 00057 00058 /** 00059 * A cursor for a Quadcap Btree. 00060 * 00061 * @author Stan Bailes 00062 */ 00063 public class BtreeCursor extends PooledObject implements BCursor { 00064 Object fileLock; 00065 00066 Btree tree; 00067 Bnode root = null; 00068 Comparator compare = null; 00069 00070 Block[] blocks = new Block[16]; 00071 int[] pointers = new int[16]; 00072 00073 byte[] curKey = new byte[4096]; 00074 byte[] curVal = new byte[4096]; 00075 int[] lengths = new int[2]; 00076 00077 int depth = -1; 00078 long size = -1; 00079 00080 /** 00081 * <pre> 00082 * For the position variable: 00083 * -2 unknown 00084 * -1 after last 00085 * 0 before first 00086 * >0 on key 00087 * </pre> 00088 */ 00089 static final long posUNKNOWN = -2; 00090 static final long posAFTER_LAST = -1; 00091 static final long posBEFORE_FIRST = 0; 00092 00093 long position = posUNKNOWN; 00094 00095 /** 00096 * Constructor for cursor on a given Btree. 00097 */ 00098 private BtreeCursor() {} 00099 00100 static final boolean noPool = false; 00101 static ObjectPool pool = new ObjectPool(new BtreeCursor()); 00102 00103 static BtreeCursor get(Btree tree, boolean skipSetup) throws IOException { 00104 BtreeCursor c = null; 00105 if (noPool) { 00106 c = new BtreeCursor(); 00107 } else { 00108 synchronized (tree.lock) { 00109 c = (BtreeCursor)pool.get(); 00110 } 00111 } 00112 if (c != null) { 00113 c.init(tree, skipSetup); 00114 } 00115 return c; 00116 } 00117 00118 public void release() { 00119 unsetup(); 00120 synchronized (fileLock) { 00121 Btree theTree = tree; 00122 tree = null; 00123 fileLock = null; 00124 theTree.releaseCursor(this); 00125 if (!noPool) { 00126 pool.release(this); 00127 } 00128 } 00129 } 00130 00131 /** 00132 * PooledObject factory 00133 */ 00134 public PooledObject create() { 00135 return new BtreeCursor(); 00136 } 00137 00138 /** 00139 * Init for use/reuse 00140 */ 00141 public void init(Btree tree, boolean skipSetup) throws IOException { 00142 this.tree = tree; 00143 this.fileLock = tree.lock; 00144 if (!skipSetup) { 00145 beforeFirst(); 00146 } 00147 } 00148 00149 /** 00150 * Called to re-establish the synchronization of this cursor in 00151 * the case where the underlying index has been modified. 00152 */ 00153 protected void setup(boolean restoreKey) throws IOException { 00154 if (depth < 0) { 00155 synchronized (fileLock) { 00156 this.root = tree.getRoot(); 00157 this.compare = tree.compare; 00158 if (restoreKey) { 00159 if (lengths[0] > 0) { 00160 seek(curKey, lengths[0]); 00161 } else { 00162 beforeFirst(); 00163 } 00164 } 00165 } 00166 } 00167 } 00168 00169 /** 00170 * Called (in theory) by the owning Btree when the index is modified. 00171 */ 00172 public void unsetup() { 00173 for (int i = 0; i < blocks.length; i++) { 00174 setBlock(i, null); 00175 } 00176 this.root = null; 00177 this.depth = -1; 00178 this.size = -1; 00179 if (position > 0) position = posUNKNOWN; 00180 } 00181 00182 /** 00183 * Seek: Position the cursor on or before the specified key value. 00184 * Return true if the key matches exactly. 00185 */ 00186 public final boolean seek(byte[] key) throws IOException { 00187 return seek(key, key.length); 00188 } 00189 00190 /** 00191 * Seek, but the key can be a subsequence of the given byte array. 00192 */ 00193 public boolean seek(byte[] key, int len) throws IOException { 00194 synchronized (fileLock) { 00195 position = posUNKNOWN; 00196 setup(false); 00197 setBlock(0, root.getBlock()); 00198 boolean ret = seek1(0, key, len); 00199 if (ret) { 00200 holdKey(0); 00201 } 00202 //#ifdef DEBUG 00203 if (Trace.bit(6)) { 00204 Debug.println(toString() + ".seek(" + 00205 Util.hexBytes(key, 0, len) + ") = " + ret); 00206 } 00207 //#endif 00208 return ret; 00209 } 00210 } 00211 00212 //#ifdef DEBUG 00213 final String id() { 00214 return tree.getFile().getName().substring(0, 1) + 00215 " " + SubPageManager.toString(tree.getRootBlock()); 00216 } 00217 final String dr() { 00218 return !Trace.bit(12) ? 00219 ", val = " + (lengths[1] > 0 ? Util.hexBytes(getVal()) : "null") : 00220 ""; 00221 } 00222 //#endif 00223 00224 /** 00225 * (Private) seek recursion kernel 00226 */ 00227 boolean seek1(int level, byte[] key, int len) throws IOException { 00228 Block b = blocks[level]; 00229 int bs; 00230 bs = Bnode.bsearch(b, compare, key, len, 0, Bnode.getCount(b)-1); 00231 int index = bs < 0 ? (-1-bs) : bs; 00232 if (!Bnode.isLeaf(b)) { 00233 if (bs < 0) { 00234 index = index - 1; 00235 if (index < 0) index = 0; 00236 } 00237 pointers[level] = index; 00238 Block c = root.getBlock(Bnode.blockRef(b, index)); 00239 setBlock(++level, c); 00240 return seek1(level, key, len); 00241 } else { 00242 pointers[level] = index; 00243 depth = level+1; 00244 return bs >= 0; 00245 } 00246 } 00247 00248 /** 00249 * Manage updates to the 'blocks' array through this function to 00250 * assuage refcount madness. 00251 */ 00252 final void setBlock(int i, Block b) { 00253 if (blocks[i] != null) blocks[i].decrRefCount(); 00254 blocks[i] = b; 00255 } 00256 00257 /** 00258 * Move the cursor before the first key. 00259 */ 00260 public void beforeFirst() throws IOException { 00261 synchronized (fileLock) { 00262 this.root = tree.getRoot(); 00263 this.compare = tree.compare; 00264 lengths[0] = lengths[1] = 0; 00265 Block b = root.getBlock(); 00266 for (int level = 0; b != null; level++) { 00267 setBlock(level, b); 00268 pointers[level] = 0; 00269 if (!Bnode.isLeaf(b)) { 00270 b = root.getBlock(Bnode.blockRef(b, 0)); 00271 } else { 00272 depth = level + 1; 00273 b = null; 00274 } 00275 } 00276 position = posBEFORE_FIRST; 00277 00278 //#ifdef DEBUG 00279 if (Trace.bit(1)) { 00280 Debug.println(toString() + ".beforeFirst()"); 00281 } 00282 //#endif 00283 } 00284 } 00285 00286 00287 /** 00288 * Move the cursor after the last key. 00289 */ 00290 public void afterLast() throws IOException { 00291 synchronized (fileLock) { 00292 setup(false); 00293 Block b = root.getBlock(); 00294 for (int level = 0; b != null; level++) { 00295 setBlock(level, b); 00296 int count = Bnode.getCount(b); 00297 pointers[level] = count; 00298 if (!Bnode.isLeaf(b)) { 00299 b = root.getBlock(Bnode.blockRef(b, pointers[level]-1)); 00300 } else { 00301 b = null; 00302 } 00303 } 00304 position = posAFTER_LAST; 00305 //#ifdef DEBUG 00306 if (Trace.bit(1)) { 00307 Debug.println(toString() + ".afterLast()"); 00308 } 00309 //#endif 00310 } 00311 } 00312 00313 /** 00314 * Move the cursor to the specified absolute position 00315 */ 00316 public boolean absolute(int x) throws IOException { 00317 synchronized (fileLock) { 00318 if (x > 0) { 00319 // from first 00320 beforeFirst(); 00321 int cur = 1; 00322 int cnt = Bnode.getCount(blocks[depth-1]); 00323 while (cur + cnt < x) { 00324 cur += cnt; 00325 if (!getNextBlock()) return false; 00326 cnt = Bnode.getCount(blocks[depth-1]); 00327 } 00328 int p = x - cur; 00329 if (p < 0 || p >= cnt) return false; 00330 pointers[depth-1] = p; 00331 holdKey(0); 00332 pointers[depth-1]++; 00333 position = x; 00334 //#ifdef DEBUG 00335 if (Trace.bit(1)) { 00336 Debug.println(toString() + ".absolute(" + x + ") = true"); 00337 } 00338 //#endif 00339 return true; 00340 } else if (x < 0) { 00341 int absx = 0 - x; 00342 // from last 00343 afterLast(); 00344 int cnt = Bnode.getCount(blocks[depth-1]); 00345 while (cnt < absx) { 00346 absx -= cnt; 00347 if (!getPrevBlock()) return false; 00348 cnt = Bnode.getCount(blocks[depth-1]); 00349 } 00350 pointers[depth-1] -= absx; 00351 holdKey(0); 00352 //#ifdef DEBUG 00353 if (Trace.bit(1)) { 00354 Debug.println(toString() + ".absolute(" + x + ") = true"); 00355 } 00356 //#endif 00357 if (size > 0) position = size + x; 00358 return true; 00359 } else { 00360 throw new IOException("absolute 0"); 00361 } 00362 } 00363 } 00364 00365 /** 00366 * Move the cursor to the next row and return <b>true</b> if the 00367 * cursor is positioned on a valid row. 00368 */ 00369 public boolean next() throws IOException { 00370 synchronized (fileLock) { 00371 setup(true); 00372 if (depth <= 0) return false; 00373 int p = pointers[depth-1]; 00374 int cnt = Bnode.getCount(blocks[depth-1]); 00375 if (p >= cnt) { 00376 if (cnt == 0 || !getNextBlock()) { 00377 //#ifdef DEBUG 00378 if (Trace.bit(1)) { 00379 Debug.println(toString() + ".next() false"); 00380 } 00381 //#endif 00382 return false; 00383 } 00384 p = pointers[depth-1]; 00385 cnt = Bnode.getCount(blocks[depth-1]); 00386 if (p >= cnt) { 00387 //#ifdef DEBUG 00388 if (Trace.bit(1)) { 00389 Debug.println(toString() + ".next() false"); 00390 } 00391 //#endif 00392 return false; 00393 } 00394 } 00395 holdKey(0); 00396 pointers[depth-1]++; 00397 if (position >= 0) position++; 00398 //#ifdef DEBUG 00399 if (Trace.bit(1)) { 00400 Debug.println(toString() + ".next(): " + 00401 Util.hexBytes(getKeyBuf(), 0, getKeyLen()) + 00402 " = " + dr()); 00403 } 00404 //#endif 00405 return true; 00406 } 00407 } 00408 00409 /** 00410 * Move the cursor to the next row and return <b>true</b> if the 00411 * cursor is positioned on a valid row. 00412 */ 00413 public boolean prev() throws IOException { 00414 synchronized (fileLock) { 00415 setup(true); 00416 if (depth < 0) return false; 00417 int p = pointers[depth-1] - 1; 00418 int cnt = Bnode.getCount(blocks[depth-1]); 00419 if (p < 0 || cnt <= 0) { 00420 if (cnt <= 0 || !getPrevBlock()) return false; 00421 p = pointers[depth-1]; 00422 if (p < 0 || cnt <= 0) return false; 00423 } 00424 pointers[depth-1] = p; 00425 holdKey(0); 00426 if (position > 0) { 00427 position--; 00428 } else if (position == posAFTER_LAST) { 00429 if (size < 0) { 00430 position = posUNKNOWN; 00431 } else { 00432 position = size; 00433 } 00434 } 00435 //#ifdef DEBUG 00436 if (Trace.bit(1)) { 00437 Debug.println(toString() + ".prev() true"); 00438 } 00439 //#endif 00440 return true; 00441 } 00442 } 00443 00444 /** 00445 * Delete the current row (if the cursor is positioned on a valid 00446 * row, that is ;-) 00447 */ 00448 public boolean delete() throws IOException { 00449 synchronized (fileLock) { 00450 boolean ret = false; 00451 setup(true); 00452 if (depth >= 0) { 00453 int p = pointers[depth-1]; 00454 int cnt = Bnode.getCount(blocks[depth-1]); 00455 if (p >= 0 && p < cnt) { 00456 root.deleteKeyAtPos(blocks[depth-1], p, false); 00457 tree.notifyUpdate(this); 00458 ret = true; 00459 } 00460 } 00461 //#ifdef DEBUG 00462 if (Trace.bit(9)) { 00463 Debug.println(toString() + ".delete() = " + ret); 00464 } 00465 //#endif 00466 return ret; 00467 } 00468 } 00469 00470 00471 /** 00472 * Insert a new key/data pair. We are presumably positioned just before 00473 * the spot where the new record should go, but we should check, anyway. 00474 * This will return false if the key already exists in the index. 00475 */ 00476 public boolean insert(byte[] key, int klen, 00477 byte[] data, int doff, int dlen) throws IOException { 00478 synchronized (fileLock) { 00479 setup(true); 00480 00481 int p = pointers[depth-1]; 00482 final Block b = blocks[depth-1]; 00483 final int cnt = Bnode.getCount(b); 00484 00485 if (p < 0 || p > cnt) throw new IOException("bad tree"); 00486 00487 boolean ret = true; 00488 if (p >= 0 && p < cnt) { 00489 int r = Bnode.keyCompareAtPos(compare, key, klen, b, p); 00490 if (r != -1) ret = false; 00491 } else if (p > 0 && cnt > 0) { 00492 int r = Bnode.keyCompareAtPos(compare, key, klen, b, p-1); 00493 if (r != 1) ret = false; 00494 } 00495 if (ret) { 00496 root.setKey(b, p, key, klen, data, doff, dlen, false); 00497 tree.notifyUpdate(this); 00498 } 00499 //#ifdef DEBUG 00500 if (Trace.bit(7)) { 00501 Debug.println(toString() + ".insert(" + k(key, 0, klen) + 00502 (!Trace.bit(12) ? (", " + k(data, doff, dlen)) : "") + 00503 ") = " + ret); 00504 } 00505 //#endif 00506 return ret; 00507 } 00508 } 00509 00510 public boolean insert(byte[] key, byte[] data) throws IOException { 00511 return insert(key, key.length, data, 0, data.length); 00512 } 00513 00514 /** 00515 * Replace the data portion of the current item with the specified data 00516 */ 00517 public boolean replace(byte[] data, int doff, int dlen) 00518 throws IOException 00519 { 00520 synchronized (fileLock) { 00521 setup(true); 00522 00523 int p = pointers[depth-1]; 00524 final Block b = blocks[depth-1]; 00525 final int cnt = Bnode.getCount(b); 00526 00527 if (p < 0 || p > cnt) { 00528 throw new IOException("bad tree"); 00529 } 00530 00531 root.setKey(b, p, curKey, lengths[0], data, doff, dlen, true); 00532 tree.notifyUpdate(this); 00533 //#ifdef DEBUG 00534 if (Trace.bit(8)) { 00535 Debug.println(toString() + ".replace(" + k(curKey, 0, lengths[0]) + ") = true"); 00536 } 00537 //#endif 00538 return true; 00539 } 00540 } 00541 00542 public boolean replace(byte[] data) throws IOException { 00543 return replace(data, 0, data.length); 00544 } 00545 00546 /** 00547 * Return the total number of entries in this index 00548 */ 00549 public long size() throws IOException { 00550 synchronized (fileLock) { 00551 setup(true); 00552 if (size < 0) { 00553 size = subtreeSize(root, root.getBlock()); 00554 } 00555 return size; 00556 } 00557 } 00558 00559 /** 00560 * Return the current position in the index 00561 */ 00562 public long position() throws IOException { 00563 synchronized (fileLock) { 00564 setup(true); 00565 if (position < 0) { 00566 long total = 0; 00567 boolean r = false; 00568 for (int d = 0; d < depth-1; d++) { 00569 Block b = blocks[d]; 00570 int p = pointers[d]; 00571 int c = Bnode.getCount(b); 00572 if (d == 0) r = (c / 2) <= p; 00573 if (r) p++; 00574 else { c = p; p = 0; } 00575 if (Bnode.isLeaf(b)) { 00576 total += (c - p); 00577 } else { 00578 for (int i = p; i < c; i++) { 00579 Block s = root.getBlock(Bnode.blockRef(b, i)); 00580 try { 00581 total += subtreeSize(root, s); 00582 } finally { 00583 s.decrRefCount(); 00584 } 00585 } 00586 } 00587 } 00588 position = r ? size() - total : total + 1; 00589 } 00590 return position; 00591 } 00592 } 00593 00594 /** 00595 * Close the index 00596 */ 00597 public void close() { 00598 unsetup(); 00599 } 00600 00601 public int getKey(byte[] buf) { 00602 System.arraycopy(curKey, 0, buf, 0, lengths[0]); 00603 return lengths[0]; 00604 } 00605 00606 public byte[] getKeyBuf() { return curKey; } 00607 00608 public void setKeyBuf(byte[] buf) { curKey = buf; } 00609 00610 public int getKeyLen() { return lengths[0]; } 00611 00612 public byte[] getKey() { 00613 byte[] ret = new byte[lengths[0]]; 00614 System.arraycopy(curKey, 0, ret, 0, ret.length); 00615 return ret; 00616 } 00617 00618 public int getVal(byte[] buf) { 00619 System.arraycopy(curVal, 0, buf, 0, lengths[1]); 00620 return lengths[1]; 00621 } 00622 00623 public byte[] getValBuf() { return curVal; } 00624 00625 public void setValBuf(byte[] buf) { curVal = buf; } 00626 00627 public int getValLen() { return lengths[1]; } 00628 00629 public byte[] getVal() { 00630 byte[] ret = new byte[lengths[1]]; 00631 System.arraycopy(curVal, 0, ret, 0, ret.length); 00632 return ret; 00633 } 00634 00635 public long getValAsLong() { 00636 return ByteUtil.getLong(curVal, 0); 00637 } 00638 00639 //--------------------------------- private stuff 00640 00641 final boolean getNextBlock() throws IOException { 00642 int d = depth - 2; 00643 while (d >= 0 && pointers[d]+1 >= Bnode.getCount(blocks[d])) { 00644 d--; 00645 } 00646 00647 if (d < 0) return false; 00648 Block b = root.getBlock(Bnode.blockRef(blocks[d], 00649 ++pointers[d])); 00650 00651 setBlock(d+1, b); 00652 pointers[d+1] = 0; 00653 00654 for (int i = d+2; i < depth; i++) { 00655 pointers[i] = 0; 00656 Block c = root.getBlock(Bnode.blockRef(blocks[i-1], 00657 pointers[i-1])); 00658 setBlock(i, c); 00659 } 00660 return true; 00661 } 00662 00663 final boolean getPrevBlock() throws IOException { 00664 int d = depth - 2; 00665 while (d >= 0 && pointers[d] <= 0) { 00666 d--; 00667 } 00668 00669 if (d < depth-1) { 00670 if (d < 0) { 00671 return false; 00672 } 00673 Block b = root.getBlock(Bnode.blockRef(blocks[d], 00674 --pointers[d])); 00675 setBlock(d+1, b); 00676 pointers[d+1] = 0; 00677 00678 for (int i = d+2; i < depth; i++) { 00679 Block c = root.getBlock(Bnode.blockRef(blocks[i-1], 00680 pointers[i-1])); 00681 setBlock(i, c); 00682 pointers[i] = Bnode.getCount(c)-1; 00683 } 00684 } 00685 return true; 00686 } 00687 00688 final void holdKey(int off) { 00689 int p = pointers[depth-1] - off; 00690 Block b = blocks[depth-1]; 00691 if (p >= 0 && p < Bnode.getCount(b)) { 00692 if (!Bnode.getKeyAndData(b, p, curKey, curVal, lengths)) { 00693 throw new RuntimeException("holdKey: getKeyAndData failed"); 00694 } 00695 } else { 00696 //msg("holdKey: p = " + p + ", pointers[depth(" + depth + ")-1]=" + 00697 // pointers[depth-1] + ", off = " + off + ", count = " + 00698 // Bnode.getCount(b)); 00699 throw new RuntimeException("Hold what, mate?"); 00700 } 00701 //Debug.println("holdKey: [" + k(curKey, 0, lengths[0]) + 00702 //" / " + k(curVal, 0, lengths[1]) + "]"); 00703 } 00704 00705 static long subtreeSize(Bnode root, Block b) throws IOException { 00706 int count = Bnode.getCount(b); 00707 if (Bnode.isLeaf(b)) return count; 00708 else { 00709 long total = 0; 00710 for (int i = 0; i < count; i++) { 00711 Block c = root.getBlock(Bnode.blockRef(b, i)); 00712 try { 00713 total += subtreeSize(root, c); 00714 } finally { 00715 c.decrRefCount(); 00716 } 00717 } 00718 return total; 00719 } 00720 } 00721 00722 //#ifdef DEBUG 00723 00724 public String toString() { 00725 return "BtreeCursor[" + tree + "]"; 00726 } 00727 00728 String k(byte[] b, int off, int len) { 00729 return tree.compare.toString(b, off, len); 00730 } 00731 00732 String t() { 00733 StringBuffer sb = new StringBuffer(); 00734 for (int i = 0; i < depth; i++) { 00735 if (i > 0) sb.append(' '); 00736 sb.append(String.valueOf(pointers[i])); 00737 sb.append('/'); 00738 sb.append(String.valueOf(Bnode.getCount(blocks[i]))); 00739 } 00740 return sb.toString(); 00741 } 00742 00743 static int lastCount = -1; 00744 static StringBuffer lastBuf = new StringBuffer(); 00745 00746 private static final int countNext(BCursor c) throws IOException { 00747 int cnt = 0; 00748 while (c.next()) cnt++; 00749 lastCount = cnt; 00750 return cnt; 00751 } 00752 00753 private static final int countPrev(BCursor c) throws IOException { 00754 int cnt = 0; 00755 StringBuffer sb = lastBuf; 00756 sb.setLength(0); 00757 sb.append("{"); 00758 byte[] buf = new byte[111]; 00759 while (c.prev()) { 00760 cnt++; 00761 int len = c.getKey(buf); 00762 if (cnt > 0) sb.append(','); 00763 sb.append(new String(buf, 0, len)); 00764 } 00765 sb.append("}"); 00766 lastCount = cnt; 00767 return cnt; 00768 } 00769 00770 final static String r(boolean b) { 00771 return b ? "{true}" : "{false}"; 00772 } 00773 static void itest(BCursor bc) throws IOException { 00774 InputStreamReader ir = new InputStreamReader(System.in); 00775 BufferedReader r = new BufferedReader(ir); 00776 String ret = ""; 00777 while (true) { 00778 System.out.print("BC" + ret + " (" + ((BtreeCursor)bc).t() + "): "); 00779 System.out.flush(); 00780 String line = r.readLine(); 00781 if (line == null) break; 00782 try { 00783 ret = doLine(bc, line); 00784 if (ret == null) break; 00785 } catch (Throwable t) { 00786 t.printStackTrace(System.out); 00787 System.out.println("-------------------------"); 00788 } 00789 } 00790 } 00791 00792 static String doLine(BCursor bc, String line) throws Exception { 00793 String ks = line.toLowerCase().substring(1).trim(); 00794 byte[] k = ks.getBytes(); 00795 byte[] d = ks.toUpperCase().getBytes(); 00796 String ret = ""; 00797 if (line.startsWith("g")) { 00798 ret = r(bc.seek(k)); 00799 } else if (line.equals("<")) { 00800 bc.beforeFirst(); 00801 } else if (line.equals(">")) { 00802 bc.afterLast(); 00803 } else if (line.equals("n")) { 00804 ret = r(bc.next()); 00805 } else if (line.equals("p")) { 00806 ret = r(bc.prev()); 00807 } else if (line.equals("d")) { 00808 ret = r(bc.delete()); 00809 } else if (line.startsWith("i")) { 00810 ret = r(bc.insert(k, k.length, d, 0, d.length)); 00811 } else if (line.startsWith("r")) { 00812 ret = r(bc.replace(d, 0, d.length)); 00813 } else if (line.equals("c")) { 00814 bc.close(); 00815 } else if (line.equals("s")) { 00816 show("position = " + bc.position() + " of " + 00817 bc.size() + " records"); 00818 } else if (line.equals("k")) { 00819 show("current key = " + new String(bc.getKeyBuf(), 0, 00820 bc.getKeyLen())); 00821 show("current val = " + new String(bc.getValBuf(), 0, 00822 bc.getValLen())); 00823 show("current val as long: " + bc.getValAsLong()); 00824 00825 } else if (line.equals("p")) { 00826 show("position = " + bc.position() + " of " + 00827 bc.size() + " records"); 00828 } else if (line.equals("q")) { 00829 return null; 00830 } else { 00831 int row = 0; 00832 boolean bad = true; 00833 try { 00834 row = Integer.parseInt(line); 00835 bad = false; 00836 } catch (Throwable t) {} 00837 if (!bad) { 00838 ret = r(bc.absolute(row)); 00839 } else { 00840 show("g <key> seek(key)"); 00841 show("i <key> insert(key,KEY)"); 00842 show("r <key> replace(KEY)"); 00843 show("< beforeFirst()"); 00844 show("> afterLast()"); 00845 show("n next()"); 00846 show("p prev()"); 00847 show("c close()"); 00848 show("d delete()"); 00849 show("s position / size"); 00850 } 00851 } 00852 return ret; 00853 } 00854 00855 static void show(String s) { 00856 System.out.println(s); 00857 } 00858 00859 static final long tick() { return System.currentTimeMillis(); } 00860 00861 static final void mkey(int i, byte[] b) { 00862 int x = b.length; 00863 while (x > 0 && i > 0) { 00864 b[--x] = (byte)('0' + (i % 10)); 00865 i /= 10; 00866 } 00867 while (x > 0) b[--x] = '0'; 00868 } 00869 00870 static void ktest(BCursor c) throws IOException { 00871 long s = tick(); 00872 byte[] k = new byte[8]; 00873 for (int i = 0; i < k.length; i++) k[i] = '0'; 00874 for (int ki = 0; ki < 100000; ki++) { 00875 int i = (ki * 2003) % 100000; 00876 mkey(i, k); 00877 if (c.seek(k)) throw new IOException("*** Error, found " + i); 00878 if (!c.insert(k, k.length, k, 0, k.length)) { 00879 throw new IOException("*** Error insert: " + i); 00880 } 00881 } 00882 long elap = tick() - s; 00883 System.out.println("insert: " + elap + " ms"); 00884 kshow(c); 00885 } 00886 00887 static void kshow(BCursor c) throws IOException { 00888 long s = tick(); 00889 int count = 0; 00890 int total = 0; 00891 int etotal = 0; 00892 c.beforeFirst(); 00893 while (c.next()) { 00894 etotal += count; 00895 count++; 00896 total += Integer.parseInt(new String(c.getValBuf(), 0, 00897 c.getValLen())); 00898 } 00899 long elap = tick() - s; 00900 show("seek: " + elap + " ms"); 00901 show("Count: " + count + ", total/etotal = " + total + "/" + etotal); 00902 } 00903 00904 static void ktest(Btree t) throws IOException { 00905 long s = tick(); 00906 byte[] k = new byte[8]; 00907 for (int i = 0; i < k.length; i++) k[i] = '0'; 00908 for (int ki = 0; ki < 100000; ki++) { 00909 int i = (ki * 2003) % 100000; 00910 mkey(i, k); 00911 t.set(k, k); 00912 } 00913 long elap = tick() - s; 00914 System.out.println("insert: " + elap + " ms"); 00915 kshow(t.getCursor()); 00916 } 00917 00918 /** 00919 * Main for testing 00920 */ 00921 public static void main(String[] args) { 00922 try { 00923 String name = "test.db"; 00924 int blocksize = 4096; 00925 int cachesize = 256; 00926 BlockFile f = new BlockFile(name, "rw", 00927 new Properties(), 00928 blocksize, cachesize); 00929 long rootBlock = f.newPage(); 00930 Btree t = new Btree(f, rootBlock, true); 00931 // tree.set("abc".getBytes(), "def".getBytes()); 00932 // tree.set("aabc".getBytes(), "def".getBytes()); 00933 // tree.set("fabc".getBytes(), "def".getBytes()); 00934 00935 BCursor c = t.getCursor(); 00936 //itest(c); 00937 //jtest(c); 00938 //ktest(t); 00939 ktest(c); 00940 } catch (Throwable t) { 00941 t.printStackTrace(System.out); 00942 } 00943 } 00944 00945 static void jtest(BCursor c) throws Exception { 00946 c.seek("abc".getBytes()); 00947 c.insert("abc".getBytes(), 3, "def".getBytes(), 0, 3); 00948 c.seek("ahc".getBytes()); 00949 c.insert("ahc".getBytes(), 3, "deh".getBytes(), 0, 3); 00950 c.seek("bbc".getBytes()); 00951 c.insert("bbc".getBytes(), 3, "jej".getBytes(), 0, 3); 00952 00953 int cSize = countNext(c); 00954 if (c.size() != cSize) { 00955 System.out.println("Bad size: " + cSize + " vs " + c.size()); 00956 } 00957 for (int i = 1; i <= cSize; i++) { 00958 if (i == 0) c.beforeFirst(); else c.absolute(i); 00959 if (c.position() != i) { 00960 System.out.println("Bad position " + i + " vs " + 00961 c.position()); 00962 } 00963 if (countNext(c) != cSize - i) { 00964 System.out.println("Bad count next: " + lastCount + 00965 " vs " + (cSize - i)); 00966 } 00967 if (i == 0) c.beforeFirst(); else c.absolute(i); 00968 if (c.position() != i) { 00969 System.out.println("Bad position " + i + " vs " + 00970 c.position()); 00971 } 00972 if (countPrev(c) != i) { 00973 System.out.println("Bad count prev: " + i + " vs " + 00974 lastCount + " " + lastBuf); 00975 } 00976 if (i == 0) c.afterLast(); else c.absolute(0 - i); 00977 if (c.position() != cSize - i) { 00978 System.out.println("Bad position " + (cSize-i) + " vs " + 00979 c.position()); 00980 } 00981 if (countNext(c) != i-1) { 00982 System.out.println("Bad count next: " + lastCount + 00983 " vs " + (i-1)); 00984 } 00985 if (i == 0) c.afterLast(); else c.absolute(0 - i); 00986 if (c.position() != (cSize-i)) { 00987 System.out.println("Bad position " + (cSize-i) + " vs " + 00988 c.position()); 00989 } 00990 if (countPrev(c) != cSize - i + 1) { 00991 System.out.println("Bad count prev: " + (cSize-i+1) + 00992 " vs " + 00993 lastCount + " " + lastBuf); 00994 } 00995 } 00996 00997 System.out.println("seek(abc) = " + c.seek("abc".getBytes()) + 00998 ": " + c.position() + "/" + c.size()); 00999 System.out.println("c.next() = " + c.next()); 01000 System.out.println("c.next() = " + c.next()); 01001 01002 System.out.println("seek(abb) = " + c.seek("abb".getBytes())); 01003 System.out.println("c.next() = " + c.next()); 01004 System.out.println("c.next() = " + c.next()); 01005 01006 System.out.println("seek(abd) = " + c.seek("abd".getBytes())); 01007 System.out.println("c.next() = " + c.next()); 01008 System.out.println("c.next() = " + c.next()); 01009 01010 System.out.println("seek(zzz) = " + c.seek("zzz".getBytes())); 01011 System.out.println("c.next() = " + c.next()); 01012 System.out.println("c.next() = " + c.next()); 01013 } 01014 //#endif 01015 }