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