001 /**
002 * JDBM LICENSE v1.00
003 *
004 * Redistribution and use of this software and associated documentation
005 * ("Software"), with or without modification, are permitted provided
006 * that the following conditions are met:
007 *
008 * 1. Redistributions of source code must retain copyright
009 * statements and notices. Redistributions must also contain a
010 * copy of this document.
011 *
012 * 2. Redistributions in binary form must reproduce the
013 * above copyright notice, this list of conditions and the
014 * following disclaimer in the documentation and/or other
015 * materials provided with the distribution.
016 *
017 * 3. The name "JDBM" must not be used to endorse or promote
018 * products derived from this Software without prior written
019 * permission of Cees de Groot. For written permission,
020 * please contact cg@cdegroot.com.
021 *
022 * 4. Products derived from this Software may not be called "JDBM"
023 * nor may "JDBM" appear in their names without prior written
024 * permission of Cees de Groot.
025 *
026 * 5. Due credit should be given to the JDBM Project
027 * (http://jdbm.sourceforge.net/).
028 *
029 * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
030 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
031 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
032 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
033 * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
034 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
035 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
036 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
037 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
038 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
039 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
040 * OF THE POSSIBILITY OF SUCH DAMAGE.
041 *
042 * Copyright 2001 (C) Alex Boisvert. All Rights Reserved.
043 * Contributions are Copyright (C) 2001 by their associated contributors.
044 *
045 */
046
047 package jdbm.btree;
048
049 import jdbm.helper.Serializer;
050 import jdbm.helper.Tuple;
051 import jdbm.helper.TupleBrowser;
052
053 import java.io.IOException;
054 import java.io.ByteArrayOutputStream;
055 import java.io.ByteArrayInputStream;
056 import java.io.ObjectInput;
057 import java.io.ObjectOutput;
058 import java.io.ObjectInputStream;
059 import java.io.ObjectOutputStream;
060
061 /**
062 * Page of a Btree.
063 * <p>
064 * The page contains a number of key-value pairs. Keys are ordered to allow
065 * dichotomic search.
066 * <p>
067 * If the page is a leaf page, the keys and values are user-defined and
068 * represent entries inserted by the user.
069 * <p>
070 * If the page is non-leaf, each key represents the greatest key in the
071 * underlying BPages and the values are recids pointing to the children BPages.
072 * The only exception is the rightmost BPage, which is considered to have an
073 * "infinite" key value, meaning that any insert will be to the left of this
074 * pseudo-key
075 *
076 * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
077 * @version $Id: BPage.java,v 1.6 2003/09/21 15:46:59 boisvert Exp $
078 */
079 public final class BPage
080 implements Serializer
081 {
082
083 private static final boolean DEBUG = false;
084
085
086 /**
087 * Version id for serialization.
088 */
089 final static long serialVersionUID = 1L;
090
091
092 /**
093 * Parent B+Tree.
094 */
095 transient BTree _btree;
096
097
098 /**
099 * This BPage's record ID in the PageManager.
100 */
101 protected transient long _recid;
102
103
104 /**
105 * Flag indicating if this is a leaf BPage.
106 */
107 protected boolean _isLeaf;
108
109
110 /**
111 * Keys of children nodes
112 */
113 protected Object[] _keys;
114
115
116 /**
117 * Values associated with keys. (Only valid if leaf BPage)
118 */
119 protected Object[] _values;
120
121
122 /**
123 * Children pages (recids) associated with keys. (Only valid if non-leaf BPage)
124 */
125 protected long[] _children;
126
127
128 /**
129 * Index of first used item at the page
130 */
131 protected int _first;
132
133
134 /**
135 * Previous leaf BPage (only if this BPage is a leaf)
136 */
137 protected long _previous;
138
139
140 /**
141 * Next leaf BPage (only if this BPage is a leaf)
142 */
143 protected long _next;
144
145
146 /**
147 * No-argument constructor used by serialization.
148 */
149 public BPage()
150 {
151 // empty
152 }
153
154
155 /**
156 * Root page overflow constructor
157 */
158 BPage( BTree btree, BPage root, BPage overflow )
159 throws IOException
160 {
161 _btree = btree;
162
163 _isLeaf = false;
164
165 _first = _btree._pageSize-2;
166
167 _keys = new Object[ _btree._pageSize ];
168 _keys[ _btree._pageSize-2 ] = overflow.getLargestKey();
169 _keys[ _btree._pageSize-1 ] = root.getLargestKey();
170
171 _children = new long[ _btree._pageSize ];
172 _children[ _btree._pageSize-2 ] = overflow._recid;
173 _children[ _btree._pageSize-1 ] = root._recid;
174
175 _recid = _btree._recman.insert( this, this );
176 }
177
178
179 /**
180 * Root page (first insert) constructor.
181 */
182 BPage( BTree btree, Object key, Object value )
183 throws IOException
184 {
185 _btree = btree;
186
187 _isLeaf = true;
188
189 _first = btree._pageSize-2;
190
191 _keys = new Object[ _btree._pageSize ];
192 _keys[ _btree._pageSize-2 ] = key;
193 _keys[ _btree._pageSize-1 ] = null; // I am the root BPage for now
194
195 _values = new Object[ _btree._pageSize ];
196 _values[ _btree._pageSize-2 ] = value;
197 _values[ _btree._pageSize-1 ] = null; // I am the root BPage for now
198
199 _recid = _btree._recman.insert( this, this );
200 }
201
202
203 /**
204 * Overflow page constructor. Creates an empty BPage.
205 */
206 BPage( BTree btree, boolean isLeaf )
207 throws IOException
208 {
209 _btree = btree;
210
211 _isLeaf = isLeaf;
212
213 // page will initially be half-full
214 _first = _btree._pageSize/2;
215
216 _keys = new Object[ _btree._pageSize ];
217 if ( isLeaf ) {
218 _values = new Object[ _btree._pageSize ];
219 } else {
220 _children = new long[ _btree._pageSize ];
221 }
222
223 _recid = _btree._recman.insert( this, this );
224 }
225
226
227 /**
228 * Get largest key under this BPage. Null is considered to be the
229 * greatest possible key.
230 */
231 Object getLargestKey()
232 {
233 return _keys[ _btree._pageSize-1 ];
234 }
235
236
237 /**
238 * Return true if BPage is empty.
239 */
240 boolean isEmpty()
241 {
242 if ( _isLeaf ) {
243 return ( _first == _values.length-1 );
244 } else {
245 return ( _first == _children.length-1 );
246 }
247 }
248
249
250 /**
251 * Return true if BPage is full.
252 */
253 boolean isFull() {
254 return ( _first == 0 );
255 }
256
257
258 /**
259 * Find the object associated with the given key.
260 *
261 * @param height Height of the current BPage (zero is leaf page)
262 * @param key The key
263 * @return TupleBrowser positionned just before the given key, or before
264 * next greater key if key isn't found.
265 */
266 TupleBrowser find( int height, Object key )
267 throws IOException
268 {
269 int index = findChildren( key );
270
271 /*
272 if ( DEBUG ) {
273 System.out.println( "BPage.find() current: " + this
274 + " height: " + height);
275 }
276 */
277
278 height -= 1;
279
280 if ( height == 0 ) {
281 // leaf BPage
282 return new Browser( this, index );
283 } else {
284 // non-leaf BPage
285 BPage child = childBPage( index );
286 return child.find( height, key );
287 }
288 }
289
290
291 /**
292 * Find first entry and return a browser positioned before it.
293 *
294 * @return TupleBrowser positionned just before the first entry.
295 */
296 TupleBrowser findFirst()
297 throws IOException
298 {
299 if ( _isLeaf ) {
300 return new Browser( this, _first );
301 } else {
302 BPage child = childBPage( _first );
303 return child.findFirst();
304 }
305 }
306
307
308 /**
309 * Insert the given key and value.
310 * <p>
311 * Since the Btree does not support duplicate entries, the caller must
312 * specify whether to replace the existing value.
313 *
314 * @param height Height of the current BPage (zero is leaf page)
315 * @param key Insert key
316 * @param value Insert value
317 * @param replace Set to true to replace the existing value, if one exists.
318 * @return Insertion result containing existing value OR a BPage if the key
319 * was inserted and provoked a BPage overflow.
320 */
321 InsertResult insert( int height, Object key, Object value, boolean replace )
322 throws IOException
323 {
324 InsertResult result;
325 long overflow;
326
327 int index = findChildren( key );
328
329 height -= 1;
330 if ( height == 0 ) {
331
332 result = new InsertResult();
333
334 // inserting on a leaf BPage
335 overflow = -1;
336 if ( DEBUG ) {
337 System.out.println( "Bpage.insert() Insert on leaf Bpage key=" + key
338 + " value=" + value + " index="+index);
339 }
340 if ( compare( key, _keys[ index ] ) == 0 ) {
341 // key already exists
342 if ( DEBUG ) {
343 System.out.println( "Bpage.insert() Key already exists." ) ;
344 }
345 result._existing = _values[ index ];
346 if ( replace ) {
347 _values [ index ] = value;
348 _btree._recman.update( _recid, this, this );
349 }
350 // return the existing key
351 return result;
352 }
353 } else {
354 // non-leaf BPage
355 BPage child = childBPage( index );
356 result = child.insert( height, key, value, replace );
357
358 if ( result._existing != null ) {
359 // return existing key, if any.
360 return result;
361 }
362
363 if ( result._overflow == null ) {
364 // no overflow means we're done with insertion
365 return result;
366 }
367
368 // there was an overflow, we need to insert the overflow page
369 // on this BPage
370 if ( DEBUG ) {
371 System.out.println( "BPage.insert() Overflow page: " + result._overflow._recid );
372 }
373 key = result._overflow.getLargestKey();
374 overflow = result._overflow._recid;
375
376 // update child's largest key
377 _keys[ index ] = child.getLargestKey();
378
379 // clean result so we can reuse it
380 result._overflow = null;
381 }
382
383 // if we get here, we need to insert a new entry on the BPage
384 // before _children[ index ]
385 if ( !isFull() ) {
386 if ( height == 0 ) {
387 insertEntry( this, index-1, key, value );
388 } else {
389 insertChild( this, index-1, key, overflow );
390 }
391 _btree._recman.update( _recid, this, this );
392 return result;
393 }
394
395 // page is full, we must divide the page
396 int half = _btree._pageSize >> 1;
397 BPage newPage = new BPage( _btree, _isLeaf );
398 if ( index < half ) {
399 // move lower-half of entries to overflow BPage,
400 // including new entry
401 if ( DEBUG ) {
402 System.out.println( "Bpage.insert() move lower-half of entries to overflow BPage, including new entry." ) ;
403 }
404 if ( height == 0 ) {
405 copyEntries( this, 0, newPage, half, index );
406 setEntry( newPage, half+index, key, value );
407 copyEntries( this, index, newPage, half+index+1, half-index-1 );
408 } else {
409 copyChildren( this, 0, newPage, half, index );
410 setChild( newPage, half+index, key, overflow );
411 copyChildren( this, index, newPage, half+index+1, half-index-1 );
412 }
413 } else {
414 // move lower-half of entries to overflow BPage,
415 // new entry stays on this BPage
416 if ( DEBUG ) {
417 System.out.println( "Bpage.insert() move lower-half of entries to overflow BPage. New entry stays" ) ;
418 }
419 if ( height == 0 ) {
420 copyEntries( this, 0, newPage, half, half );
421 copyEntries( this, half, this, half-1, index-half );
422 setEntry( this, index-1, key, value );
423 } else {
424 copyChildren( this, 0, newPage, half, half );
425 copyChildren( this, half, this, half-1, index-half );
426 setChild( this, index-1, key, overflow );
427 }
428 }
429
430 _first = half-1;
431
432 // nullify lower half of entries
433 for ( int i=0; i<_first; i++ ) {
434 if ( height == 0 ) {
435 setEntry( this, i, null, null );
436 } else {
437 setChild( this, i, null, -1 );
438 }
439 }
440
441 if ( _isLeaf ) {
442 // link newly created BPage
443 newPage._previous = _previous;
444 newPage._next = _recid;
445 if ( _previous != 0 ) {
446 BPage previous = loadBPage( _previous );
447 previous._next = newPage._recid;
448 _btree._recman.update( _previous, previous, this );
449 }
450 _previous = newPage._recid;
451 }
452
453 _btree._recman.update( _recid, this, this );
454 _btree._recman.update( newPage._recid, newPage, this );
455
456 result._overflow = newPage;
457 return result;
458 }
459
460
461 /**
462 * Remove the entry associated with the given key.
463 *
464 * @param height Height of the current BPage (zero is leaf page)
465 * @param key Removal key
466 * @return Remove result object
467 */
468 RemoveResult remove( int height, Object key )
469 throws IOException
470 {
471 RemoveResult result;
472
473 int half = _btree._pageSize / 2;
474 int index = findChildren( key );
475
476 height -= 1;
477 if ( height == 0 ) {
478 // remove leaf entry
479 if ( compare( _keys[ index ], key ) != 0 ) {
480 throw new IllegalArgumentException( "Key not found: " + key );
481 }
482 result = new RemoveResult();
483 result._value = _values[ index ];
484 removeEntry( this, index );
485
486 // update this BPage
487 _btree._recman.update( _recid, this, this );
488
489 } else {
490 // recurse into Btree to remove entry on a children page
491 BPage child = childBPage( index );
492 result = child.remove( height, key );
493
494 // update children
495 _keys[ index ] = child.getLargestKey();
496 _btree._recman.update( _recid, this, this );
497
498 if ( result._underflow ) {
499 // underflow occured
500 if ( child._first != half+1 ) {
501 throw new IllegalStateException( "Error during underflow [1]" );
502 }
503 if ( index < _children.length-1 ) {
504 // exists greater brother page
505 BPage brother = childBPage( index+1 );
506 int bfirst = brother._first;
507 if ( bfirst < half ) {
508 // steal entries from "brother" page
509 int steal = ( half - bfirst + 1 ) / 2;
510 brother._first += steal;
511 child._first -= steal;
512 if ( child._isLeaf ) {
513 copyEntries( child, half+1, child, half+1-steal, half-1 );
514 copyEntries( brother, bfirst, child, 2*half-steal, steal );
515 } else {
516 copyChildren( child, half+1, child, half+1-steal, half-1 );
517 copyChildren( brother, bfirst, child, 2*half-steal, steal );
518 }
519
520 for ( int i=bfirst; i<bfirst+steal; i++ ) {
521 if ( brother._isLeaf ) {
522 setEntry( brother, i, null, null );
523 } else {
524 setChild( brother, i, null, -1 );
525 }
526 }
527
528 // update child's largest key
529 _keys[ index ] = child.getLargestKey();
530
531 // no change in previous/next BPage
532
533 // update BPages
534 _btree._recman.update( _recid, this, this );
535 _btree._recman.update( brother._recid, brother, this );
536 _btree._recman.update( child._recid, child, this );
537
538 } else {
539 // move all entries from page "child" to "brother"
540 if ( brother._first != half ) {
541 throw new IllegalStateException( "Error during underflow [2]" );
542 }
543
544 brother._first = 1;
545 if ( child._isLeaf ) {
546 copyEntries( child, half+1, brother, 1, half-1 );
547 } else {
548 copyChildren( child, half+1, brother, 1, half-1 );
549 }
550 _btree._recman.update( brother._recid, brother, this );
551
552 // remove "child" from current BPage
553 if ( _isLeaf ) {
554 copyEntries( this, _first, this, _first+1, index-_first );
555 setEntry( this, _first, null, null );
556 } else {
557 copyChildren( this, _first, this, _first+1, index-_first );
558 setChild( this, _first, null, -1 );
559 }
560 _first += 1;
561 _btree._recman.update( _recid, this, this );
562
563 // re-link previous and next BPages
564 if ( child._previous != 0 ) {
565 BPage prev = loadBPage( child._previous );
566 prev._next = child._next;
567 _btree._recman.update( prev._recid, prev, this );
568 }
569 if ( child._next != 0 ) {
570 BPage next = loadBPage( child._next );
571 next._previous = child._previous;
572 _btree._recman.update( next._recid, next, this );
573 }
574
575 // delete "child" BPage
576 _btree._recman.delete( child._recid );
577 }
578 } else {
579 // page "brother" is before "child"
580 BPage brother = childBPage( index-1 );
581 int bfirst = brother._first;
582 if ( bfirst < half ) {
583 // steal entries from "brother" page
584 int steal = ( half - bfirst + 1 ) / 2;
585 brother._first += steal;
586 child._first -= steal;
587 if ( child._isLeaf ) {
588 copyEntries( brother, 2*half-steal, child,
589 half+1-steal, steal );
590 copyEntries( brother, bfirst, brother,
591 bfirst+steal, 2*half-bfirst-steal );
592 } else {
593 copyChildren( brother, 2*half-steal, child,
594 half+1-steal, steal );
595 copyChildren( brother, bfirst, brother,
596 bfirst+steal, 2*half-bfirst-steal );
597 }
598
599 for ( int i=bfirst; i<bfirst+steal; i++ ) {
600 if ( brother._isLeaf ) {
601 setEntry( brother, i, null, null );
602 } else {
603 setChild( brother, i, null, -1 );
604 }
605 }
606
607 // update brother's largest key
608 _keys[ index-1 ] = brother.getLargestKey();
609
610 // no change in previous/next BPage
611
612 // update BPages
613 _btree._recman.update( _recid, this, this );
614 _btree._recman.update( brother._recid, brother, this );
615 _btree._recman.update( child._recid, child, this );
616
617 } else {
618 // move all entries from page "brother" to "child"
619 if ( brother._first != half ) {
620 throw new IllegalStateException( "Error during underflow [3]" );
621 }
622
623 child._first = 1;
624 if ( child._isLeaf ) {
625 copyEntries( brother, half, child, 1, half );
626 } else {
627 copyChildren( brother, half, child, 1, half );
628 }
629 _btree._recman.update( child._recid, child, this );
630
631 // remove "brother" from current BPage
632 if ( _isLeaf ) {
633 copyEntries( this, _first, this, _first+1, index-1-_first );
634 setEntry( this, _first, null, null );
635 } else {
636 copyChildren( this, _first, this, _first+1, index-1-_first );
637 setChild( this, _first, null, -1 );
638 }
639 _first += 1;
640 _btree._recman.update( _recid, this, this );
641
642 // re-link previous and next BPages
643 if ( brother._previous != 0 ) {
644 BPage prev = loadBPage( brother._previous );
645 prev._next = brother._next;
646 _btree._recman.update( prev._recid, prev, this );
647 }
648 if ( brother._next != 0 ) {
649 BPage next = loadBPage( brother._next );
650 next._previous = brother._previous;
651 _btree._recman.update( next._recid, next, this );
652 }
653
654 // delete "brother" BPage
655 _btree._recman.delete( brother._recid );
656 }
657 }
658 }
659 }
660
661 // underflow if page is more than half-empty
662 result._underflow = _first > half;
663
664 return result;
665 }
666
667
668 /**
669 * Find the first children node with a key equal or greater than the given
670 * key.
671 *
672 * @return index of first children with equal or greater key.
673 */
674 private int findChildren( Object key )
675 {
676 int left = _first;
677 int right = _btree._pageSize-1;
678
679 // binary search
680 while ( left < right ) {
681 int middle = ( left + right ) / 2;
682 if ( compare( _keys[ middle ], key ) < 0 ) {
683 left = middle+1;
684 } else {
685 right = middle;
686 }
687 }
688 return right;
689 }
690
691
692 /**
693 * Insert entry at given position.
694 */
695 private static void insertEntry( BPage page, int index,
696 Object key, Object value )
697 {
698 Object[] keys = page._keys;
699 Object[] values = page._values;
700 int start = page._first;
701 int count = index-page._first+1;
702
703 // shift entries to the left
704 System.arraycopy( keys, start, keys, start-1, count );
705 System.arraycopy( values, start, values, start-1, count );
706 page._first -= 1;
707 keys[ index ] = key;
708 values[ index ] = value;
709 }
710
711
712 /**
713 * Insert child at given position.
714 */
715 private static void insertChild( BPage page, int index,
716 Object key, long child )
717 {
718 Object[] keys = page._keys;
719 long[] children = page._children;
720 int start = page._first;
721 int count = index-page._first+1;
722
723 // shift entries to the left
724 System.arraycopy( keys, start, keys, start-1, count );
725 System.arraycopy( children, start, children, start-1, count );
726 page._first -= 1;
727 keys[ index ] = key;
728 children[ index ] = child;
729 }
730
731 /**
732 * Remove entry at given position.
733 */
734 private static void removeEntry( BPage page, int index )
735 {
736 Object[] keys = page._keys;
737 Object[] values = page._values;
738 int start = page._first;
739 int count = index-page._first;
740
741 System.arraycopy( keys, start, keys, start+1, count );
742 keys[ start ] = null;
743 System.arraycopy( values, start, values, start+1, count );
744 values[ start ] = null;
745 page._first++;
746 }
747
748
749 /**
750 * Remove child at given position.
751 */
752 /*
753 private static void removeChild( BPage page, int index )
754 {
755 Object[] keys = page._keys;
756 long[] children = page._children;
757 int start = page._first;
758 int count = index-page._first;
759
760 System.arraycopy( keys, start, keys, start+1, count );
761 keys[ start ] = null;
762 System.arraycopy( children, start, children, start+1, count );
763 children[ start ] = (long) -1;
764 page._first++;
765 }
766 */
767
768 /**
769 * Set the entry at the given index.
770 */
771 private static void setEntry( BPage page, int index, Object key, Object value )
772 {
773 page._keys[ index ] = key;
774 page._values[ index ] = value;
775 }
776
777
778 /**
779 * Set the child BPage recid at the given index.
780 */
781 private static void setChild( BPage page, int index, Object key, long recid )
782 {
783 page._keys[ index ] = key;
784 page._children[ index ] = recid;
785 }
786
787
788 /**
789 * Copy entries between two BPages
790 */
791 private static void copyEntries( BPage source, int indexSource,
792 BPage dest, int indexDest, int count )
793 {
794 System.arraycopy( source._keys, indexSource, dest._keys, indexDest, count);
795 System.arraycopy( source._values, indexSource, dest._values, indexDest, count);
796 }
797
798
799 /**
800 * Copy child BPage recids between two BPages
801 */
802 private static void copyChildren( BPage source, int indexSource,
803 BPage dest, int indexDest, int count )
804 {
805 System.arraycopy( source._keys, indexSource, dest._keys, indexDest, count);
806 System.arraycopy( source._children, indexSource, dest._children, indexDest, count);
807 }
808
809
810 /**
811 * Return the child BPage at given index.
812 */
813 BPage childBPage( int index )
814 throws IOException
815 {
816 return loadBPage( _children[ index ] );
817 }
818
819
820 /**
821 * Load the BPage at the given recid.
822 */
823 private BPage loadBPage( long recid )
824 throws IOException
825 {
826 BPage child = (BPage) _btree._recman.fetch( recid, this );
827 child._recid = recid;
828 child._btree = _btree;
829 return child;
830 }
831
832
833 private final int compare( Object value1, Object value2 )
834 {
835 if ( value1 == null ) {
836 return 1;
837 }
838 if ( value2 == null ) {
839 return -1;
840 }
841 return _btree._comparator.compare( value1, value2 );
842 }
843
844 static byte[] readByteArray( ObjectInput in )
845 throws IOException
846 {
847 int len = in.readInt();
848 if ( len < 0 ) {
849 return null;
850 }
851 byte[] buf = new byte[ len ];
852 in.readFully( buf );
853 return buf;
854 }
855
856
857 static void writeByteArray( ObjectOutput out, byte[] buf )
858 throws IOException
859 {
860 if ( buf == null ) {
861 out.writeInt( -1 );
862 } else {
863 out.writeInt( buf.length );
864 out.write( buf );
865 }
866 }
867
868 /**
869 * Dump the structure of the tree on the screen. This is used for debugging
870 * purposes only.
871 */
872 private void dump( int height )
873 {
874 String prefix = "";
875 for ( int i=0; i<height; i++ ) {
876 prefix += " ";
877 }
878 System.out.println( prefix + "-------------------------------------- BPage recid=" + _recid);
879 System.out.println( prefix + "first=" + _first );
880 for ( int i=0; i< _btree._pageSize; i++ ) {
881 if ( _isLeaf ) {
882 System.out.println( prefix + "BPage [" + i + "] " + _keys[ i ] + " " + _values[ i ] );
883 } else {
884 System.out.println( prefix + "BPage [" + i + "] " + _keys[ i ] + " " + _children[ i ] );
885 }
886 }
887 System.out.println( prefix + "--------------------------------------" );
888 }
889
890
891 /**
892 * Recursively dump the state of the BTree on screen. This is used for
893 * debugging purposes only.
894 */
895 void dumpRecursive( int height, int level )
896 throws IOException
897 {
898 height -= 1;
899 level += 1;
900 if ( height > 0 ) {
901 for ( int i=_first; i<_btree._pageSize; i++ ) {
902 if ( _keys[ i ] == null ) break;
903 BPage child = childBPage( i );
904 child.dump( level );
905 child.dumpRecursive( height, level );
906 }
907 }
908 }
909
910
911 /**
912 * Assert the ordering of the keys on the BPage. This is used for testing
913 * purposes only.
914 */
915 private void assertConsistency()
916 {
917 for ( int i=_first; i<_btree._pageSize-1; i++ ) {
918 if ( compare( (byte[]) _keys[ i ], (byte[]) _keys[ i+1 ] ) >= 0 ) {
919 dump( 0 );
920 throw new Error( "BPage not ordered" );
921 }
922 }
923 }
924
925
926 /**
927 * Recursively assert the ordering of the BPage entries on this page
928 * and sub-pages. This is used for testing purposes only.
929 */
930 void assertConsistencyRecursive( int height )
931 throws IOException
932 {
933 assertConsistency();
934 if ( --height > 0 ) {
935 for ( int i=_first; i<_btree._pageSize; i++ ) {
936 if ( _keys[ i ] == null ) break;
937 BPage child = childBPage( i );
938 if ( compare( (byte[]) _keys[ i ], child.getLargestKey() ) != 0 ) {
939 dump( 0 );
940 child.dump( 0 );
941 throw new Error( "Invalid child subordinate key" );
942 }
943 child.assertConsistencyRecursive( height );
944 }
945 }
946 }
947
948
949 /**
950 * Deserialize the content of an object from a byte array.
951 *
952 * @param serialized Byte array representation of the object
953 * @return deserialized object
954 *
955 */
956 public Object deserialize( byte[] serialized )
957 throws IOException
958 {
959 ByteArrayInputStream bais;
960 ObjectInputStream ois;
961 BPage bpage;
962
963 bpage = new BPage();
964 bais = new ByteArrayInputStream( serialized );
965 ois = new ObjectInputStream( bais );
966
967 bpage._isLeaf = ois.readBoolean();
968 if ( bpage._isLeaf ) {
969 bpage._previous = ois.readLong();
970 bpage._next = ois.readLong();
971 }
972
973 bpage._first = ois.readInt();
974
975 bpage._keys = new Object[ _btree._pageSize ];
976 try {
977 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
978 if ( _btree._keySerializer == null ) {
979 bpage._keys[ i ] = ois.readObject();
980 } else {
981 serialized = readByteArray( ois );
982 if ( serialized != null ) {
983 bpage._keys[ i ] = _btree._keySerializer.deserialize( serialized );
984 }
985 }
986 }
987 } catch ( ClassNotFoundException except ) {
988 throw new IOException( except.getMessage() );
989 }
990
991 if ( bpage._isLeaf ) {
992 bpage._values = new Object[ _btree._pageSize ];
993 try {
994 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
995 if ( _btree._valueSerializer == null ) {
996 bpage._values[ i ] = ois.readObject();
997 } else {
998 serialized = readByteArray( ois );
999 if ( serialized != null ) {
1000 bpage._values[ i ] = _btree._valueSerializer.deserialize( serialized );
1001 }
1002 }
1003 }
1004 } catch ( ClassNotFoundException except ) {
1005 throw new IOException( except.getMessage() );
1006 }
1007 } else {
1008 bpage._children = new long[ _btree._pageSize ];
1009 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
1010 bpage._children[ i ] = ois.readLong();
1011 }
1012 }
1013 ois.close();
1014 bais.close();
1015
1016 return bpage;
1017 }
1018
1019
1020 /**
1021 * Serialize the content of an object into a byte array.
1022 *
1023 * @param obj Object to serialize
1024 * @return a byte array representing the object's state
1025 *
1026 */
1027 public byte[] serialize( Object obj )
1028 throws IOException
1029 {
1030 byte[] serialized;
1031 ByteArrayOutputStream baos;
1032 ObjectOutputStream oos;
1033 BPage bpage;
1034 byte[] data;
1035
1036 // note: It is assumed that BPage instance doing the serialization is the parent
1037 // of the BPage object being serialized.
1038
1039 bpage = (BPage) obj;
1040 baos = new ByteArrayOutputStream();
1041 oos = new ObjectOutputStream( baos );
1042
1043 oos.writeBoolean( bpage._isLeaf );
1044 if ( bpage._isLeaf ) {
1045 oos.writeLong( bpage._previous );
1046 oos.writeLong( bpage._next );
1047 }
1048
1049 oos.writeInt( bpage._first );
1050
1051 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
1052 if ( _btree._keySerializer == null ) {
1053 oos.writeObject( bpage._keys[ i ] );
1054 } else {
1055 if ( bpage._keys[ i ] != null ) {
1056 serialized = _btree._keySerializer.serialize( bpage._keys[ i ] );
1057 writeByteArray( oos, serialized );
1058 } else {
1059 writeByteArray( oos, null );
1060 }
1061 }
1062 }
1063
1064 if ( bpage._isLeaf ) {
1065 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
1066 if ( _btree._valueSerializer == null ) {
1067 oos.writeObject( bpage._values[ i ] );
1068 } else {
1069 if ( bpage._values[ i ] != null ) {
1070 serialized = _btree._valueSerializer.serialize( bpage._values[ i ] );
1071 writeByteArray( oos, serialized );
1072 } else {
1073 writeByteArray( oos, null );
1074 }
1075 }
1076 }
1077 } else {
1078 for ( int i=bpage._first; i<_btree._pageSize; i++ ) {
1079 oos.writeLong( bpage._children[ i ] );
1080 }
1081 }
1082
1083 oos.flush();
1084 data = baos.toByteArray();
1085 oos.close();
1086 baos.close();
1087 return data;
1088 }
1089
1090
1091 /** STATIC INNER CLASS
1092 * Result from insert() method call
1093 */
1094 static class InsertResult {
1095
1096 /**
1097 * Overflow page.
1098 */
1099 BPage _overflow;
1100
1101 /**
1102 * Existing value for the insertion key.
1103 */
1104 Object _existing;
1105
1106 }
1107
1108 /** STATIC INNER CLASS
1109 * Result from remove() method call
1110 */
1111 static class RemoveResult {
1112
1113 /**
1114 * Set to true if underlying pages underflowed
1115 */
1116 boolean _underflow;
1117
1118 /**
1119 * Removed entry value
1120 */
1121 Object _value;
1122 }
1123
1124
1125 /** PRIVATE INNER CLASS
1126 * Browser to traverse leaf BPages.
1127 */
1128 static class Browser
1129 extends TupleBrowser
1130 {
1131
1132 /**
1133 * Current page.
1134 */
1135 private BPage _page;
1136
1137
1138 /**
1139 * Current index in the page. The index positionned on the next
1140 * tuple to return.
1141 */
1142 private int _index;
1143
1144
1145 /**
1146 * Create a browser.
1147 *
1148 * @param page Current page
1149 * @param index Position of the next tuple to return.
1150 */
1151 Browser( BPage page, int index )
1152 {
1153 _page = page;
1154 _index = index;
1155 }
1156
1157 public boolean getNext( Tuple tuple )
1158 throws IOException
1159 {
1160 if ( _index < _page._btree._pageSize ) {
1161 if ( _page._keys[ _index ] == null ) {
1162 // reached end of the tree.
1163 return false;
1164 }
1165 } else if ( _page._next != 0 ) {
1166 // move to next page
1167 _page = _page.loadBPage( _page._next );
1168 _index = _page._first;
1169 }
1170 tuple.setKey( _page._keys[ _index ] );
1171 tuple.setValue( _page._values[ _index ] );
1172 _index++;
1173 return true;
1174 }
1175
1176 public boolean getPrevious( Tuple tuple )
1177 throws IOException
1178 {
1179 if ( _index == _page._first ) {
1180
1181 if ( _page._previous != 0 ) {
1182 _page = _page.loadBPage( _page._previous );
1183 _index = _page._btree._pageSize;
1184 } else {
1185 // reached beginning of the tree
1186 return false;
1187 }
1188 }
1189 _index--;
1190 tuple.setKey( _page._keys[ _index ] );
1191 tuple.setValue( _page._values[ _index ] );
1192 return true;
1193
1194 }
1195 }
1196
1197 }