1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import static org.apache.directory.mavibot.btree.BTreeFactory.createLeaf;
24 import static org.apache.directory.mavibot.btree.BTreeFactory.createNode;
25 import static org.apache.directory.mavibot.btree.BTreeFactory.setKey;
26
27 import java.io.IOException;
28 import java.lang.reflect.Array;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Comparator;
32 import java.util.List;
33 import java.util.UUID;
34
35 import org.apache.directory.mavibot.btree.exception.BTreeAlreadyCreatedException;
36 import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
37 import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
38 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
39 import org.apache.directory.mavibot.btree.serializer.IntSerializer;
40 import org.apache.directory.mavibot.btree.serializer.LongSerializer;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47
48
49
50
51 class PersistedValueHolder<V> extends AbstractValueHolder<V>
52 {
53
54 protected static final Logger LOG = LoggerFactory.getLogger( PersistedValueHolder.class );
55
56
57 protected PersistedBTree<V, V> parentBtree;
58
59
60 private byte[] raw;
61
62
63 private boolean isDeserialized = false;
64
65
66 private boolean isRawUpToDate = false;
67
68
69
70
71
72
73
74
75
76
77 PersistedValueHolder( BTree<?, V> parentBtree, int nbValues, byte[] raw )
78 {
79 this.parentBtree = ( PersistedBTree<V, V> ) parentBtree;
80 this.valueSerializer = parentBtree.getValueSerializer();
81 this.raw = raw;
82 isRawUpToDate = true;
83 valueThresholdUp = PersistedBTree.valueThresholdUp;
84 valueThresholdLow = PersistedBTree.valueThresholdLow;
85
86
87
88 if ( nbValues <= valueThresholdUp )
89 {
90
91 valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), nbValues );
92 }
93 }
94
95
96
97
98
99
100
101
102
103 PersistedValueHolder( BTree<?, V> parentBtree, V... values )
104 {
105 this.parentBtree = ( PersistedBTree<V, V> ) parentBtree;
106 this.valueSerializer = parentBtree.getValueSerializer();
107 valueThresholdUp = PersistedBTree.valueThresholdUp;
108 valueThresholdLow = PersistedBTree.valueThresholdLow;
109
110 if ( values != null )
111 {
112 int nbValues = values.length;
113
114 if ( nbValues < PersistedBTree.valueThresholdUp )
115 {
116
117 valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), nbValues );
118
119 try
120 {
121 System.arraycopy( values, 0, valueArray, 0, values.length );
122 }
123 catch ( ArrayStoreException ase )
124 {
125 ase.printStackTrace();
126 throw ase;
127 }
128 }
129 else
130 {
131
132 createSubTree();
133
134
135
136
137
138
139
140
141
142
143
144
145
146 try
147 {
148 build( ( PersistedBTree<V, V> ) valueBtree, values );
149 }
150 catch ( Exception e )
151 {
152 throw new RuntimeException( e );
153 }
154 }
155 }
156 else
157 {
158
159 valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), 0 );
160 }
161
162 isDeserialized = true;
163 }
164
165
166
167
168
169 public ValueCursor<V> getCursor()
170 {
171
172 checkAndDeserialize();
173
174 return super.getCursor();
175 }
176
177
178
179
180
181
182
183
184
185 byte[] getRaw()
186 {
187 if ( isRawUpToDate )
188 {
189
190 return raw;
191 }
192
193 if ( isSubBtree() )
194 {
195
196 long btreeOffset = ( ( PersistedBTree<V, V> ) valueBtree ).getBtreeOffset();
197 raw = LongSerializer.serialize( btreeOffset );
198 }
199 else
200 {
201
202 byte[][] valueBytes = new byte[valueArray.length * 2][];
203 int length = 0;
204 int pos = 0;
205
206
207 for ( V value : valueArray )
208 {
209
210 byte[] bytes = valueSerializer.serialize( value );
211 length += bytes.length;
212
213
214 byte[] sizeBytes = IntSerializer.serialize( bytes.length );
215 length += sizeBytes.length;
216
217
218 valueBytes[pos++] = sizeBytes;
219 valueBytes[pos++] = bytes;
220 }
221
222
223
224 raw = new byte[length];
225 pos = 0;
226
227 for ( byte[] bytes : valueBytes )
228 {
229 System.arraycopy( bytes, 0, raw, pos, bytes.length );
230 pos += bytes.length;
231 }
232 }
233
234
235 isRawUpToDate = true;
236
237 return raw;
238 }
239
240
241
242
243
244 public int size()
245 {
246 checkAndDeserialize();
247
248 if ( valueArray == null )
249 {
250 return ( int ) valueBtree.getNbElems();
251 }
252 else
253 {
254 return valueArray.length;
255 }
256 }
257
258
259
260
261
262 protected void createSubTree()
263 {
264 try
265 {
266 PersistedBTreeConfiguration<V, V> configuration = new PersistedBTreeConfiguration<V, V>();
267 configuration.setAllowDuplicates( false );
268 configuration.setKeySerializer( valueSerializer );
269 configuration.setName( UUID.randomUUID().toString() );
270 configuration.setValueSerializer( valueSerializer );
271 configuration.setParentBTree( parentBtree );
272 configuration.setBtreeType( BTreeTypeEnum.PERSISTED_SUB );
273
274 valueBtree = BTreeFactory.createPersistedBTree( configuration );
275
276 try
277 {
278
279 parentBtree.getRecordManager().manage( valueBtree, RecordManager.INTERNAL_BTREE );
280 raw = null;
281 }
282 catch ( BTreeAlreadyManagedException e )
283 {
284
285 throw new BTreeAlreadyCreatedException( e );
286 }
287 }
288 catch ( IOException e )
289 {
290 throw new BTreeCreationException( e );
291 }
292 }
293
294
295
296
297
298 void setSubBtree( BTree<V, V> subBtree )
299 {
300 valueBtree = subBtree;
301 raw = null;
302 valueArray = null;
303 isDeserialized = true;
304 isRawUpToDate = false;
305 }
306
307
308
309
310
311 private void checkAndDeserialize()
312 {
313 if ( !isDeserialized )
314 {
315 if ( valueArray == null )
316 {
317
318 deserializeSubBtree();
319 }
320 else
321 {
322
323 deserializeArray();
324 }
325
326
327 isDeserialized = true;
328 }
329 }
330
331
332
333
334
335 public void add( V value )
336 {
337
338 checkAndDeserialize();
339
340 super.add( value );
341
342
343 isRawUpToDate = false;
344 raw = null;
345 }
346
347
348
349
350
351 private V removeFromArray( V value )
352 {
353 checkAndDeserialize();
354
355
356 int pos = findPos( value );
357
358 if ( pos < 0 )
359 {
360
361 return null;
362 }
363
364
365
366 V[] newValueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length - 1 );
367
368 System.arraycopy( valueArray, 0, newValueArray, 0, pos );
369 System.arraycopy( valueArray, pos + 1, newValueArray, pos, valueArray.length - pos - 1 );
370
371
372 V removedValue = valueArray[pos];
373
374
375 valueArray = newValueArray;
376
377 return removedValue;
378 }
379
380
381
382
383
384 private V removeFromBtree( V removedValue )
385 {
386
387 checkAndDeserialize();
388
389 if ( btreeContains( removedValue ) )
390 {
391 try
392 {
393 if ( valueBtree.getNbElems() - 1 < PersistedBTree.valueThresholdLow )
394 {
395 int nbValues = ( int ) ( valueBtree.getNbElems() - 1 );
396
397
398 valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), nbValues );
399
400
401 TupleCursor<V, V> cursor = valueBtree.browse();
402 V returnedValue = null;
403 int pos = 0;
404
405 while ( cursor.hasNext() )
406 {
407 Tuple<V, V> tuple = cursor.next();
408
409 V value = tuple.getKey();
410
411 if ( valueSerializer.getComparator().compare( removedValue, value ) == 0 )
412 {
413
414 returnedValue = value;
415 }
416 else
417 {
418 valueArray[pos++] = value;
419 }
420 }
421
422 cursor.close();
423
424 return returnedValue;
425 }
426 else
427 {
428 Tuple<V, V> removedTuple = valueBtree.delete( removedValue );
429
430 if ( removedTuple != null )
431 {
432 return removedTuple.getKey();
433 }
434 else
435 {
436 return null;
437 }
438 }
439 }
440 catch ( IOException e )
441 {
442
443 e.printStackTrace();
444 return null;
445 }
446 catch ( KeyNotFoundException knfe )
447 {
448
449 knfe.printStackTrace();
450 return null;
451 }
452 }
453 else
454 {
455 return null;
456 }
457 }
458
459
460
461
462
463 public V remove( V value )
464 {
465 V removedValue = null;
466
467 if ( valueArray != null )
468 {
469 removedValue = removeFromArray( value );
470 }
471 else
472 {
473 removedValue = removeFromBtree( value );
474 }
475
476
477 isRawUpToDate = false;
478 raw = null;
479
480 return removedValue;
481 }
482
483
484
485
486
487 public boolean contains( V checkedValue )
488 {
489
490 checkAndDeserialize();
491
492 return super.contains( checkedValue );
493 }
494
495
496
497
498
499
500
501
502
503
504 private int findPos( V value )
505 {
506 if ( valueArray.length == 0 )
507 {
508 return -1;
509 }
510
511
512 int pivot = valueArray.length / 2;
513 int low = 0;
514 int high = valueArray.length - 1;
515 Comparator<V> comparator = valueSerializer.getComparator();
516
517 while ( high > low )
518 {
519 switch ( high - low )
520 {
521 case 1:
522
523 int result = comparator.compare( value, valueArray[pivot] );
524
525 if ( result == 0 )
526 {
527 return pivot;
528 }
529
530 if ( result < 0 )
531 {
532 if ( pivot == low )
533 {
534 return -( low + 1 );
535 }
536 else
537 {
538 result = comparator.compare( value, valueArray[low] );
539
540 if ( result == 0 )
541 {
542 return low;
543 }
544
545 if ( result < 0 )
546 {
547 return -( low + 1 );
548 }
549 else
550 {
551 return -( low + 2 );
552 }
553 }
554 }
555 else
556 {
557 if ( pivot == high )
558 {
559 return -( high + 2 );
560 }
561 else
562 {
563 result = comparator.compare( value, valueArray[high] );
564
565 if ( result == 0 )
566 {
567 return high;
568 }
569
570 if ( result < 0 )
571 {
572 return -( high + 1 );
573 }
574 else
575 {
576 return -( high + 2 );
577 }
578 }
579 }
580
581 default:
582
583 result = comparator.compare( value, valueArray[pivot] );
584
585 if ( result == 0 )
586 {
587 return pivot;
588 }
589
590 if ( result < 0 )
591 {
592 high = pivot - 1;
593 }
594 else
595 {
596 low = pivot + 1;
597 }
598
599 pivot = ( high + low ) / 2;
600
601 continue;
602 }
603 }
604
605 int result = comparator.compare( value, valueArray[pivot] );
606
607 if ( result == 0 )
608 {
609 return pivot;
610 }
611
612 if ( result < 0 )
613 {
614 return -( pivot + 1 );
615 }
616 else
617 {
618 return -( pivot + 2 );
619 }
620 }
621
622
623
624
625
626 public ValueHolder<V> clone() throws CloneNotSupportedException
627 {
628 PersistedValueHolder<V> copy = ( PersistedValueHolder<V> ) super.clone();
629
630
631
632
633 if ( valueArray != null )
634 {
635 copy.valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length );
636 System.arraycopy( valueArray, 0, copy.valueArray, 0, valueArray.length );
637 }
638
639
640 if ( isRawUpToDate )
641 {
642 copy.raw = new byte[raw.length];
643 System.arraycopy( raw, 0, copy.raw, 0, raw.length );
644 }
645
646 return copy;
647 }
648
649
650 @Override
651 public V replaceValueArray( V newValue )
652 {
653 V val = super.replaceValueArray( newValue );
654
655 isRawUpToDate = false;
656
657 return val;
658 }
659
660
661
662
663
664 private void deserializeArray()
665 {
666
667
668 int index = 0;
669 int pos = 0;
670
671 while ( pos < raw.length )
672 {
673 try
674 {
675 int size = IntSerializer.deserialize( raw, pos );
676 pos += 4;
677
678 V value = valueSerializer.fromBytes( raw, pos );
679 pos += size;
680 valueArray[index++] = value;
681 }
682 catch ( IOException e )
683 {
684 e.printStackTrace();
685 }
686 }
687 }
688
689
690
691
692
693 private void deserializeSubBtree()
694 {
695
696 long offset = LongSerializer.deserialize( raw );
697
698
699 valueBtree = parentBtree.getRecordManager().loadDupsBtree( offset, parentBtree );
700 }
701
702
703
704
705
706 long getOffset()
707 {
708 if ( valueArray == null )
709 {
710 return ( ( PersistedBTree<V, V> ) valueBtree ).getBtreeOffset();
711 }
712 else
713 {
714 return -1L;
715 }
716 }
717
718
719
720
721
722
723
724
725
726
727 private BTree build( PersistedBTree<V, V> btree, V[] dupKeyValues ) throws Exception
728 {
729 long newRevision = btree.getRevision() + 1;
730
731 int numKeysInNode = btree.getPageSize();
732
733 RecordManager rm = btree.getRecordManager();
734
735 List<Page<V, V>> lstLeaves = new ArrayList<Page<V, V>>();
736
737 int totalTupleCount = 0;
738
739 Page<V, V> leaf1 = BTreeFactory.createLeaf( btree, newRevision, numKeysInNode );
740 lstLeaves.add( leaf1 );
741
742 int leafIndex = 0;
743
744 for ( V v : dupKeyValues )
745 {
746 setKey( btree, leaf1, leafIndex, v );
747
748 leafIndex++;
749 totalTupleCount++;
750 if ( ( totalTupleCount % numKeysInNode ) == 0 )
751 {
752 leafIndex = 0;
753
754 PageHolder<V, V> pageHolder = ( PageHolder ) rm.writePage( btree, leaf1, newRevision );
755
756 leaf1 = createLeaf( btree, newRevision, numKeysInNode );
757 lstLeaves.add( leaf1 );
758 }
759
760
761 }
762
763 if ( lstLeaves.isEmpty() )
764 {
765 return btree;
766 }
767
768
769 PersistedLeaf lastLeaf = ( PersistedLeaf ) lstLeaves.get( lstLeaves.size() - 1 );
770 for ( int i = 0; i < lastLeaf.nbElems; i++ )
771 {
772 if ( lastLeaf.keys[i] == null )
773 {
774 int n = i;
775 lastLeaf.nbElems = n;
776 KeyHolder[] keys = lastLeaf.keys;
777
778 lastLeaf.keys = ( KeyHolder[] ) Array.newInstance( KeyHolder.class, n );
779 System.arraycopy( keys, 0, lastLeaf.keys, 0, n );
780
781 PageHolder pageHolder = ( PageHolder ) rm.writePage( btree, lastLeaf, newRevision );
782
783 break;
784 }
785 }
786
787 if ( lastLeaf.keys.length == 0 )
788 {
789 lstLeaves.remove( lastLeaf );
790 }
791
792
793
794 Page rootPage = attachNodes( lstLeaves, btree, numKeysInNode, rm );
795
796 Page oldRoot = btree.getRootPage();
797
798 long newRootPageOffset = ( ( AbstractPage ) rootPage ).getOffset();
799 LOG.debug( "replacing old offset {} of the BTree {} with {}",
800 btree.getRootPageOffset(), btree.getName(), newRootPageOffset );
801
802 BTreeHeader header = btree.getBtreeHeader();
803
804 header.setRootPage( rootPage );
805 header.setRevision( newRevision );
806 header.setNbElems( totalTupleCount );
807
808 long newBtreeHeaderOffset = rm.writeBtreeHeader( btree, header );
809
810 header.setBTreeHeaderOffset( newBtreeHeaderOffset );
811
812 rm.freePages( ( BTree ) btree, btree.getRevision(), ( List ) Arrays.asList( oldRoot ) );
813
814 return btree;
815 }
816
817
818
819
820
821
822
823
824
825
826
827
828 private Page attachNodes( List<Page<V, V>> children, BTree btree, int numKeysInNode, RecordManager rm )
829 throws IOException
830 {
831 if ( children.size() == 1 )
832 {
833 return children.get( 0 );
834 }
835
836 List<Page<V, V>> lstNodes = new ArrayList<Page<V, V>>();
837
838 int numChildren = numKeysInNode + 1;
839
840 PersistedNode node = ( PersistedNode ) createNode( btree, btree.getRevision(), numKeysInNode );
841 lstNodes.add( node );
842 int i = 0;
843 int totalNodes = 0;
844
845 for ( Page p : children )
846 {
847 if ( i != 0 )
848 {
849 setKey( btree, node, i - 1, p.getLeftMostKey() );
850 }
851
852 node.children[i] = new PersistedPageHolder( btree, p );
853
854 i++;
855 totalNodes++;
856
857 if ( ( totalNodes % numChildren ) == 0 )
858 {
859 i = 0;
860
861 PageHolder pageHolder = ( PageHolder ) rm.writePage( btree, node, 1 );
862
863 node = ( PersistedNode ) createNode( btree, btree.getRevision(), numKeysInNode );
864 lstNodes.add( node );
865 }
866 }
867
868
869 AbstractPage lastNode = ( AbstractPage ) lstNodes.get( lstNodes.size() - 1 );
870
871 for ( int j = 0; j < lastNode.nbElems; j++ )
872 {
873 if ( lastNode.keys[j] == null )
874 {
875 int n = j;
876 lastNode.nbElems = n;
877 KeyHolder[] keys = lastNode.keys;
878
879 lastNode.keys = ( KeyHolder[] ) Array.newInstance( KeyHolder.class, n );
880 System.arraycopy( keys, 0, lastNode.keys, 0, n );
881
882 PageHolder pageHolder = ( PageHolder ) rm.writePage( btree, lastNode, 1 );
883
884 break;
885 }
886 }
887
888 if ( lastNode.keys.length == 0 )
889 {
890 lstNodes.remove( lastNode );
891 }
892
893 return attachNodes( lstNodes, btree, numKeysInNode, rm );
894 }
895
896
897
898
899
900 public String toString()
901 {
902 StringBuilder sb = new StringBuilder();
903
904 sb.append( "ValueHolder[" ).append( valueSerializer.getClass().getSimpleName() );
905
906 if ( !isDeserialized )
907 {
908 sb.append( ", isRaw[" ).append( raw.length ).append( "]" );
909 }
910 else
911 {
912 if ( valueArray == null )
913 {
914 sb.append( ", SubBTree" );
915 }
916 else
917 {
918 sb.append( ", array{" );
919
920 if ( valueArray == null )
921 {
922 sb.append( "}" );
923 }
924 else
925 {
926 boolean isFirst = true;
927
928 for ( V value : valueArray )
929 {
930 if ( isFirst )
931 {
932 isFirst = false;
933 }
934 else
935 {
936 sb.append( "/" );
937 }
938
939 sb.append( value );
940 }
941
942 sb.append( "}" );
943 }
944 }
945 }
946
947 sb.append( "]" );
948
949 return sb.toString();
950 }
951 }