1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * 19 */ 20 package org.apache.directory.mavibot.btree; 21 22 23 import java.io.IOException; 24 import java.lang.reflect.Array; 25 26 27 /** 28 * A MVCC abstract Page. It stores the field and the methods shared by the Node and Leaf 29 * classes. 30 * 31 * @param <K> The type for the Key 32 * @param <V> The type for the stored value 33 * 34 * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a> 35 */ 36 /* No qualifier */abstract class AbstractPage<K, V> implements Page<K, V> 37 { 38 /** Parent B+Tree. */ 39 protected transient BTree<K, V> btree; 40 41 /** This BPage's revision */ 42 protected long revision; 43 44 /** Keys of children nodes */ 45 protected K[] keys; 46 47 /** The number of current values in the Page */ 48 protected int nbElems; 49 50 /** The first {@link PageIO} storing the serialized Page on disk */ 51 private long offset = -1L; 52 53 /** The last {@link PageIO} storing the serialized Page on disk */ 54 private long lastOffset = -1L; 55 56 57 /** 58 * Creates a default empty AbstractPage 59 * 60 * @param btree The associated BTree 61 */ 62 protected AbstractPage( BTree<K, V> btree ) 63 { 64 this.btree = btree; 65 } 66 67 68 /** 69 * Internal constructor used to create Page instance used when a page is being copied or overflow 70 */ 71 @SuppressWarnings("unchecked") 72 // Cannot create an array of generic objects 73 protected AbstractPage( BTree<K, V> btree, long revision, int nbElems ) 74 { 75 this.btree = btree; 76 this.revision = revision; 77 this.nbElems = nbElems; 78 79 // We get the type of array to create from the btree 80 // Yes, this is an hack... 81 Class<?> keyType = btree.getKeyType(); 82 this.keys = ( K[] ) Array.newInstance( keyType, nbElems ); 83 } 84 85 86 /** 87 * Selects the sibling (the previous or next page with the same parent) which has 88 * the more element assuming it's above N/2 89 * 90 * @param parent The parent of the current page 91 * @param The position of the current page reference in its parent 92 * @return The position of the sibling, or -1 if we have'nt found any sibling 93 * @throws IOException If we have an error while trying to access the page 94 */ 95 protected int selectSibling( Node<K, V> parent, int parentPos ) throws IOException 96 { 97 if ( parentPos == 0 ) 98 { 99 // The current page is referenced on the left of its parent's page : 100 // we will not have a previous page with the same parent 101 return 1; 102 } 103 104 if ( parentPos == parent.getNbElems() ) 105 { 106 // The current page is referenced on the right of its parent's page : 107 // we will not have a next page with the same parent 108 return parentPos - 1; 109 } 110 111 Page<K, V> prevPage = parent.children[parentPos - 1].getValue( btree ); 112 Page<K, V> nextPage = parent.children[parentPos + 1].getValue( btree ); 113 114 int prevPageSize = prevPage.getNbElems(); 115 int nextPageSize = nextPage.getNbElems(); 116 117 if ( prevPageSize >= nextPageSize ) 118 { 119 return parentPos - 1; 120 } 121 else 122 { 123 return parentPos + 1; 124 } 125 } 126 127 128 /** 129 * {@inheritDoc} 130 */ 131 public int getNbElems() 132 { 133 return nbElems; 134 } 135 136 137 /** 138 * Finds the position of the given key in the page. If we have found the key, 139 * we will return its position as a negative value. 140 * <p/> 141 * Assuming that the array is zero-indexed, the returned value will be : <br/> 142 * position = - ( position + 1) 143 * <br/> 144 * So for the following table of keys : <br/> 145 * <pre> 146 * +---+---+---+---+ 147 * | b | d | f | h | 148 * +---+---+---+---+ 149 * 0 1 2 3 150 * </pre> 151 * looking for 'b' will return -1 (-(0+1)) and looking for 'f' will return -3 (-(2+1)).<br/> 152 * Computing the real position is just a matter to get -(position++). 153 * <p/> 154 * If we don't find the key in the table, we will return the position of the key 155 * immediately above the key we are looking for. <br/> 156 * For instance, looking for : 157 * <ul> 158 * <li>'a' will return 0</li> 159 * <li>'b' will return -1</li> 160 * <li>'c' will return 1</li> 161 * <li>'d' will return -2</li> 162 * <li>'e' will return 2</li> 163 * <li>'f' will return -3</li> 164 * <li>'g' will return 3</li> 165 * <li>'h' will return -4</li> 166 * <li>'i' will return 4</li> 167 * </ul> 168 * 169 * 170 * @param key The key to find 171 * @return The position in the page. 172 */ 173 public int findPos( K key ) 174 { 175 // Deal with the special key where we have an empty page 176 if ( nbElems == 0 ) 177 { 178 return 0; 179 } 180 181 int min = 0; 182 int max = nbElems - 1; 183 184 // binary search 185 while ( min < max ) 186 { 187 int middle = ( min + max + 1 ) >> 1; 188 189 int comp = compare( keys[middle], key ); 190 191 if ( comp < 0 ) 192 { 193 min = middle + 1; 194 } 195 else if ( comp > 0 ) 196 { 197 max = middle - 1; 198 } 199 else 200 { 201 // Special case : the key already exists, 202 // we can return immediately. The value will be 203 // negative, and as the index may be 0, we subtract 1 204 return -( middle + 1 ); 205 } 206 } 207 208 // Special case : we don't know if the key is present 209 int comp = compare( keys[max], key ); 210 211 if ( comp == 0 ) 212 { 213 return -( max + 1 ); 214 } 215 else 216 { 217 if ( comp < 0 ) 218 { 219 return max + 1; 220 } 221 else 222 { 223 return max; 224 } 225 } 226 } 227 228 229 /** 230 * Compares two keys 231 * 232 * @param key1 The first key 233 * @param key2 The second key 234 * @return -1 if the first key is above the second one, 1 if it's below, and 0 235 * if the two keys are equal 236 */ 237 private final int compare( K key1, K key2 ) 238 { 239 if ( key1 == key2 ) 240 { 241 return 0; 242 } 243 244 if ( key1 == null ) 245 { 246 return 1; 247 } 248 249 if ( key2 == null ) 250 { 251 return -1; 252 } 253 254 return btree.getComparator().compare( key1, key2 ); 255 } 256 257 258 /** 259 * {@inheritDoc} 260 */ 261 public long getRevision() 262 { 263 return revision; 264 } 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 public K getKey( int pos ) 271 { 272 if ( pos < nbElems ) 273 { 274 return keys[pos]; 275 } 276 else 277 { 278 return null; 279 } 280 } 281 282 283 /** 284 * Sets the key at a give position 285 * 286 * @param pos The position in the keys array 287 * @param key the key to inject 288 */ 289 /* No qualifier*/void setKey( int pos, K key ) 290 { 291 keys[pos] = key; 292 } 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 public long getOffset() 299 { 300 return offset; 301 } 302 303 304 /** 305 * @param offset the offset to set 306 */ 307 /* No qualifier */void setOffset( long offset ) 308 { 309 this.offset = offset; 310 } 311 312 313 /** 314 * {@inheritDoc} 315 */ 316 public long getLastOffset() 317 { 318 return lastOffset; 319 } 320 321 322 /** 323 * {@inheritDoc} 324 */ 325 /* No qualifier */void setLastOffset( long lastOffset ) 326 { 327 this.lastOffset = lastOffset; 328 } 329 330 331 /** 332 * @see Object#toString() 333 */ 334 public String toString() 335 { 336 StringBuilder sb = new StringBuilder(); 337 338 sb.append( "r" ).append( revision ); 339 sb.append( ", nbElems:" ).append( nbElems ); 340 341 if ( offset > 0 ) 342 { 343 sb.append( ", offset:" ).append( offset ); 344 } 345 346 return sb.toString(); 347 } 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 public String dumpPage( String tabs ) 354 { 355 return ""; 356 } 357 }