Quadcap Embeddable Database

com/quadcap/sql/file/BlockAccess.java

Go to the documentation of this file.
00001 package com.quadcap.sql.file; 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.IOException; 00042 00043 import com.quadcap.util.ConfigNumber; 00044 import com.quadcap.util.Debug; 00045 import com.quadcap.util.Util; 00046 00047 /** 00048 * This class implements a randomly accessible, growable region within 00049 * a <b>BlockFile</b> object. The region is defined by a root block, 00050 * which either contains the entire data for the region (if it will fit), 00051 * or contains a set of references to secondary blocks which contain the 00052 * actual data. If the region is large enough, those secondary blocks 00053 * themselves may be references, and so on. 00054 * 00055 * @author Stan Bailes 00056 */ 00057 00058 public class BlockAccess extends RandomAccess { 00059 static final int HEADER_SIZE = 8; // size of 'header' in bytes 00060 static final int REF_SIZE = 8; // size of a ref, in bytes 00061 00062 /** 00063 * The underlying store. 00064 */ 00065 PageManager file; 00066 00067 /** 00068 * The 'root' block of this data area. 00069 * The format of this block is as follows: 00070 * <p>Bytes 0-3: The size of this data area, in bytes. 00071 */ 00072 long rootBlock; 00073 long size; 00074 00075 /** 00076 * A vector of <b>depth</b> elements, indicating the size of 00077 * the recursive sub-block at each level. 00078 */ 00079 int[] radices = null; 00080 00081 byte[] b1 = new byte[1]; 00082 00083 /** 00084 * Depth of this data area. A depth of zero indicates 00085 * that the actual data area is entirely contained within the 00086 * root block. A depth of one indicates that the data is contained 00087 * in sub-blocks, and the root block contains the block numbers of 00088 * the sub-blocks. Larger depths indicate that the sub-blocks 00089 * contain pointers to other sub-blocks, etc. 00090 */ 00091 int depth = 0; 00092 00093 /** 00094 * Path from root to leaf blocks 00095 */ 00096 BlockPath path = null; 00097 00098 /** 00099 * The file lock 00100 */ 00101 Object fileLock = null; 00102 00103 /** 00104 * Default public constructor 00105 */ 00106 public BlockAccess() {} 00107 00108 /** 00109 * Construct a new <tt>BlockAccess</tt> attached to the specified 00110 * block. 00111 */ 00112 public BlockAccess(PageManager file, long rootBlock) throws IOException { 00113 init(file, rootBlock); 00114 } 00115 00116 public void init(PageManager file, long rootBlock) throws IOException { 00117 this.file = file; 00118 this.fileLock = file.getLock(); 00119 this.rootBlock = rootBlock; 00120 this.path = null; 00121 00122 synchronized (fileLock) { 00123 Page root = file.getPage(rootBlock); 00124 try { 00125 this.size = root.readLong(0); 00126 //#ifdef DEBUG 00127 if (Trace.bit(3)) { 00128 Debug.println("INIT: " + this); 00129 } 00130 //#endif 00131 } finally { 00132 root.decrRefCount(); 00133 } 00134 } 00135 this.depth = depthForSize(this.size); 00136 this.radices = radicesForDepth(this.depth); 00137 } 00138 00139 /** 00140 * Return the size of this region 00141 */ 00142 public long size() { 00143 return this.size; 00144 } 00145 00146 /** 00147 * Resize the region. 00148 */ 00149 public void resize(long newSize) throws IOException { 00150 //#ifdef DEBUG 00151 if (Trace.bit(2)) { 00152 Debug.println("BlockAccess[" + toString(rootBlock) + ":" + size + 00153 "] resize(" + newSize + ")"); 00154 } 00155 //#endif 00156 synchronized (fileLock) { 00157 Page root = file.getPage(rootBlock); 00158 try { 00159 long curSize = root.readLong(0); 00160 byte[] bufx = new byte[bufLen()]; 00161 int rsize = bufx.length - HEADER_SIZE; 00162 this.size = newSize; 00163 00164 int newDepth = depthForSize(newSize); 00165 while (newDepth > depth) { 00166 // ---- growing 00167 long newPage = file.newPage(); 00168 Page b = file.getPage(newPage); 00169 try { 00170 root.read(HEADER_SIZE, bufx, 0, rsize); 00171 root.clear(); 00172 b.write(0, bufx, 0, rsize); 00173 setBlockRef(root, 0, newPage, true); 00174 radices = radicesForDepth(++depth); 00175 } finally { 00176 b.decrRefCount(); 00177 } 00178 } 00179 00180 while (newDepth < depth) { 00181 // ---- shrinking 00182 for (int i = 1; i < refsForLevel(0); i++) { 00183 long blk = getBlockRef(root, i, true); 00184 if (blk != 0) { 00185 file.freePage(blk); 00186 setBlockRef(root, i, 0, true); 00187 } 00188 } 00189 long freePage = getBlockRef(root, 0, true); 00190 Page b = file.getPage(freePage); 00191 try { 00192 b.read(0, bufx, 0, rsize); 00193 root.write(HEADER_SIZE, bufx, 0, rsize); 00194 radices = radicesForDepth(--depth); 00195 } finally { 00196 b.decrRefCount(); 00197 } 00198 file.freePage(freePage); 00199 } 00200 root.writeLong(0, newSize); 00201 } finally { 00202 root.decrRefCount(); 00203 } 00204 } 00205 } 00206 00207 /** 00208 * Write into the data region. 00209 * 00210 * @param pos the starting position to write 00211 * @param buf the buffer containing the data to write 00212 * @param offset the position of the first byte in the buffer 00213 * @param len the number of bytes to write 00214 */ 00215 public void write(long pos, byte[] buf, int offset, int len) 00216 throws IOException 00217 { 00218 synchronized (fileLock) { 00219 //#ifdef DEBUG 00220 if (Trace.bit(2)) { 00221 Debug.println("[" + toString(rootBlock) + ":" + size + 00222 "] write(" + 00223 pos + ", " + Util.strBytes(buf, offset, len) + ")"); 00224 } 00225 //#endif 00226 Page root = file.getPage(rootBlock); 00227 try { 00228 BlockPath p = getPath(pos); 00229 int hdr = (depth >= 1) ? 0 : HEADER_SIZE; 00230 while (len > 0) { 00231 int bufloc = p.getBufPos(); 00232 int amt = Math.min(len, (bufLen() - hdr) - bufloc); 00233 Page b = p.getLeafBlock(); 00234 try { 00235 b.write(bufloc + hdr, buf, offset, amt); 00236 } finally { 00237 b.decrRefCount(); 00238 } 00239 len -= amt; 00240 offset += amt; 00241 //pos += amt; // pos used only for debug 00242 //Debug.println("Before incr: " + this); 00243 if (len > 0) p.incr(amt); 00244 } 00245 } finally { 00246 root.decrRefCount(); 00247 } 00248 } 00249 } 00250 00251 /** 00252 * Read from the data region. 00253 */ 00254 public void read(long pos, byte[] buf, int offset, int len) 00255 throws IOException 00256 { 00257 //#ifdef DEBUG 00258 if (pos + len > size) { 00259 throw new IOException("BlockAccess.read() out of bounds: " + 00260 "pos = " + pos + 00261 ", len = " + len + 00262 ", size = " + size()); 00263 } 00264 long save_pos = pos; 00265 int save_offset = offset; 00266 int save_len = len; 00267 //#endif 00268 00269 synchronized (fileLock) { 00270 Page root = file.getPage(rootBlock); 00271 try { 00272 BlockPath p = getPath(pos); 00273 int hdr = (depth >= 1) ? 0 : HEADER_SIZE; 00274 while (len > 0) { 00275 int bufloc = p.getBufPos(); 00276 int amt = Math.min(len, (bufLen() - hdr) - bufloc); 00277 //#ifdef DEBUG 00278 if (amt <= 0) { 00279 Debug.println("pos = " + pos + ", offset = " + offset + 00280 ", len = " + len + ", bufloc = " + bufloc + 00281 ", bufLen() = " + bufLen()); 00282 Debug.println("save_pos = " + save_pos + 00283 ", save_offset = " + save_offset + 00284 ", save_len = " + save_len); 00285 Debug.println("p = " + p); 00286 Debug.println("size = " + size); 00287 Debug.println("depth = " + depth); 00288 for (int i = 0; i < radices.length; i++) { 00289 Debug.println("radix[" + i + "] = " + radices[i]); 00290 } 00291 throw new RuntimeException("Internal error: amt: " + amt); 00292 } 00293 //#endif 00294 Page b = p.getLeafBlock(); 00295 try { 00296 b.read(bufloc + hdr, buf, offset, amt); 00297 } finally { 00298 b.decrRefCount(); 00299 } 00300 len -= amt; 00301 offset += amt; 00302 //pos += amt; 00303 if (len > 0) p.incr(amt); 00304 } 00305 } finally { 00306 root.decrRefCount(); 00307 } 00308 } 00309 } 00310 00311 /** 00312 * Finalization: We're done with this region. 00313 */ 00314 public void close() { 00315 } 00316 00317 public void flush() { 00318 } 00319 00320 private final BlockPath getPath(long pos) throws IOException { 00321 if (path == null || depth != path.getDepth()) { 00322 path = new BlockPath(this, pos); 00323 } else { 00324 path.setPos(pos); 00325 } 00326 return path; 00327 } 00328 00329 /** 00330 * Build the radices array for a region of the specified depth. 00331 */ 00332 final int[] radicesForDepth(int depth) { 00333 int[] v = new int[depth]; 00334 int r = bufLen(); 00335 for (int i = depth-1; i >= 0; i--) { 00336 v[i] = r; 00337 r *= refsForLevel(i); 00338 } 00339 return v; 00340 } 00341 00342 /** 00343 * Return the specified block reference. This method treats the data 00344 * portion of the block as an array of integer blockrefs. 00345 * 00346 * @param b the block containing the array of blockrefs 00347 * @param pos the location in the blockref array of the block number 00348 * to fetch. 00349 */ 00350 final long getBlockRef(Page b, int pos, boolean isRoot) 00351 throws IOException 00352 { 00353 int offset = pos * REF_SIZE + (isRoot ? HEADER_SIZE : 0); 00354 long ref = b.readLong(offset); 00355 return ref; 00356 } 00357 00358 /** 00359 * Return the specified block reference. This method treats the data 00360 * portion of the block as an array of long blockrefs. If the 00361 * selected blockref is zero, this routine will allocate a new 00362 * block and return the blockref of the newly created block, as well 00363 * as update <tt>b</tt>'s blockref array with the new blockref. 00364 * 00365 * @param b the block containing the array of blockrefs 00366 * @param pos the location in the blockref array of the block number 00367 * to fetch. 00368 */ 00369 final long makeBlockRef(Page b, int pos, boolean isRoot) 00370 throws IOException 00371 { 00372 int offset = pos * REF_SIZE + (isRoot ? HEADER_SIZE : 0); 00373 long ref = b.readLong(offset); 00374 if (ref == 0) { 00375 ref = file.newPage(); 00376 b.writeLong(offset, ref); 00377 } 00378 return ref; 00379 } 00380 00381 /** 00382 * Set the specified block reference. 00383 * 00384 * @param b the block containing the array of blockrefs 00385 * @param pos the (zero-offset) index of the blockref within the array 00386 * @param val the value to store in the array 00387 */ 00388 final void setBlockRef(Page b, int pos, long val, boolean isRoot) { 00389 int offset = pos * REF_SIZE + (isRoot ? HEADER_SIZE : 0); 00390 b.writeLong(offset, val); 00391 } 00392 00393 /** 00394 * Return the number of blockrefs that will fit in a block. 00395 */ 00396 final int refsForLevel(int level) { 00397 return (bufLen() - (level == 0 ? HEADER_SIZE : 0)) / REF_SIZE; 00398 } 00399 00400 /** 00401 * Return the size of the data portion of the block. 00402 */ 00403 final int bufLen() { 00404 return file.getPageSize(); 00405 } 00406 00407 /** 00408 * Return the maximum size for a region of the specified depth. 00409 */ 00410 private final long maxSizeForDepth(int level) { 00411 if (level == 0) { 00412 return bufLen() - HEADER_SIZE; 00413 } 00414 long ret = bufLen(); 00415 for (int i = 0; i < level; i++) { 00416 ret *= refsForLevel(i); 00417 } 00418 return ret; 00419 } 00420 00421 /** 00422 * Return the depth necessary to represent a region of the specified size 00423 * 00424 * @param size the size of the region 00425 */ 00426 private final int depthForSize(long size) { 00427 if (size <= bufLen() - HEADER_SIZE) return 0; 00428 00429 int ret = 1; 00430 long max = bufLen() * refsForLevel(0); 00431 00432 while (size > max) { 00433 max *= refsForLevel(++ret); 00434 } 00435 return ret; 00436 } 00437 00438 //#ifdef DEBUG 00439 final String toString(long page) { 00440 return SubPageManager.toString(page); 00441 } 00442 00443 public String toString() { 00444 StringBuffer sb = new StringBuffer("BlockAccess("); 00445 sb.append("root="); 00446 sb.append(SubPageManager.toString(rootBlock)); 00447 sb.append(" size="); 00448 sb.append(size); 00449 if (radices != null) { 00450 sb.append(" radices="); 00451 for (int i = 0; i < radices.length; i++) { 00452 if (i>0) sb.append(','); 00453 sb.append(radices[i]); 00454 } 00455 } 00456 sb.append(" depth="); 00457 sb.append(depth); 00458 sb.append(" path="); 00459 sb.append(path); 00460 sb.append(")"); 00461 return sb.toString(); 00462 } 00463 //#endif 00464 }