Return-Path: X-Original-To: apmail-directory-commits-archive@www.apache.org Delivered-To: apmail-directory-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 55682105E0 for ; Sun, 27 Oct 2013 08:48:50 +0000 (UTC) Received: (qmail 76830 invoked by uid 500); 27 Oct 2013 08:48:49 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 76752 invoked by uid 500); 27 Oct 2013 08:48:47 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 76683 invoked by uid 99); 27 Oct 2013 08:48:43 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Oct 2013 08:48:43 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Oct 2013 08:48:33 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 46AB323889FA; Sun, 27 Oct 2013 08:48:10 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1536066 [2/3] - in /directory/mavibot/trunk/mavibot/src: main/java/org/apache/directory/mavibot/btree/ main/java/org/apache/directory/mavibot/btree/managed/ main/java/org/apache/directory/mavibot/btree/memory/ main/java/org/apache/director... Date: Sun, 27 Oct 2013 08:48:08 -0000 To: commits@directory.apache.org From: elecharny@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131027084810.46AB323889FA@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RecordManager.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RecordManager.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RecordManager.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RecordManager.java Sun Oct 27 08:48:07 2013 @@ -131,6 +131,13 @@ public class RecordManager /** A global buffer used to store the header */ private static ByteBuffer HEADER_BUFFER; + /** A static buffer used to store the header */ + private static byte[] HEADER_BYTES; + + /** The length of an Offset, as a nagative value */ + private static byte[] LONG_LENGTH = new byte[] + { ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xF8 }; + /** The RecordManager underlying page size. */ private int pageSize = DEFAULT_PAGE_SIZE; @@ -180,6 +187,7 @@ public class RecordManager managedBTrees = new LinkedHashMap>(); HEADER_BUFFER = ByteBuffer.allocate( pageSize ); + HEADER_BYTES = new byte[pageSize]; HEADER_SIZE = pageSize; // Open the file or create it @@ -580,94 +588,140 @@ public class RecordManager // this ByteBuffer ByteBuffer byteBuffer = readBytes( pageIos, position ); - // Now, deserialize the data block + // Now, deserialize the data block. If the number of elements + // is positive, it's a Leaf, otherwise it's a Node + // Note that only a leaf can have 0 elements, and it's the root page then. if ( nbElems >= 0 ) { - // Its a leaf, create it - page = BTreeFactory.createLeaf( btree, revision, nbElems ); + // It's a leaf + page = readLeaf( btree, nbElems, revision, byteBuffer, pageIos ); + } + else + { + // It's a node + page = readNode( btree, -nbElems, revision, byteBuffer, pageIos ); + } - // Store the page offset on disk - ( ( AbstractPage ) page ).setOffset( pageIos[0].getOffset() ); - ( ( AbstractPage ) page ).setLastOffset( pageIos[pageIos.length - 1].getOffset() ); + return page; + } - int[] keyLengths = new int[nbElems]; - int[] valueLengths = new int[nbElems]; - // Read each key and value - for ( int i = 0; i < nbElems; i++ ) - { - //valueLengths[i] = byteBuffer.getInt(); + /** + * Deserialize a Leaf from some PageIOs + */ + private Leaf readLeaf( BTree btree, int nbElems, long revision, ByteBuffer byteBuffer, + PageIO[] pageIos ) + { + // Its a leaf, create it + Leaf leaf = BTreeFactory.createLeaf( btree, revision, nbElems ); + + // Store the page offset on disk + leaf.setOffset( pageIos[0].getOffset() ); + leaf.setLastOffset( pageIos[pageIos.length - 1].getOffset() ); + + int[] keyLengths = new int[nbElems]; + int[] valueLengths = new int[nbElems]; - ElementHolder valueHolder; + // Read each key and value + for ( int i = 0; i < nbElems; i++ ) + { + // Read the number of values + int nbValues = byteBuffer.getInt(); + ValueHolder valueHolder = null; + + if ( nbValues < 0 ) + { + // This is a sub-btree + long btreeOffset = byteBuffer.getLong(); - if ( btree.isAllowDuplicates() ) + // Load the BTree now. + try { - byte flag = byteBuffer.get(); + PageIO[] rootPageIos = readPageIOs( btreeOffset, Long.MAX_VALUE ); + + BTree subBtree = BTreeFactory.createBTree(); + subBtree.setBtreeOffset( btreeOffset ); - if ( flag == 0 ) + try { - V singleValue = btree.getValueSerializer().deserialize( byteBuffer ); - valueHolder = new MultipleMemoryHolder( btree, singleValue ); + loadBTree( rootPageIos, subBtree ); } - else if ( flag == 1 ) + catch ( Exception e ) { - long value = OFFSET_SERIALIZER.deserialize( byteBuffer ); + // should not happen + throw new RuntimeException( e ); + } - BTree dupValueContainer = loadDupsBTree( value ); + valueHolder = new ValueHolder( this, btree.getValueSerializer(), subBtree ); - valueHolder = new MultipleMemoryHolder( btree, dupValueContainer ); - } - else - { - throw new IllegalStateException( "Unknown multiple value holder flag " + flag ); - } + valueHolder.setSubBtree( subBtree ); } - else + catch ( EndOfFileExceededException e1 ) { - Object value = btree.getValueSerializer().deserialize( byteBuffer ); - - valueHolder = new MemoryHolder( btree, value ); + // TODO Auto-generated catch block + e1.printStackTrace(); } + catch ( IOException e1 ) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + else + { + // This is an array + // Read the value's array length + valueLengths[i] = byteBuffer.getInt(); - BTreeFactory.setValue( ( ( Leaf ) page ), i, valueHolder ); - - keyLengths[i] = byteBuffer.getInt(); - ByteBuffer slice = byteBuffer.slice(); - slice.limit( keyLengths[i] ); - byteBuffer.position( byteBuffer.position() + keyLengths[i] ); - BTreeFactory.setKey( page, i, slice, null ); + // This is an Array of values, read the byte[] associated with it + byte[] valueBytes = new byte[valueLengths[i]]; + byteBuffer.get( valueBytes ); + valueHolder = new ValueHolder( this, btree.getValueSerializer(), false, nbValues, + valueBytes ); } - } - else - { - // It's a node - int nodeNbElems = -nbElems; - page = BTreeFactory.createNode( btree, revision, nodeNbElems ); + BTreeFactory.setValue( leaf, i, valueHolder ); - // Read each value and key - for ( int i = 0; i < nodeNbElems; i++ ) - { - // This is an Offset - long offset = OFFSET_SERIALIZER.deserialize( byteBuffer ); - long lastOffset = OFFSET_SERIALIZER.deserialize( byteBuffer ); + keyLengths[i] = byteBuffer.getInt(); + byte[] data = new byte[keyLengths[i]]; + byteBuffer.get( data ); + BTreeFactory.setKey( leaf, i, data ); + } - ElementHolder valueHolder = new PageHolder( btree, null, offset, lastOffset ); - ( ( Node ) page ).setValue( i, valueHolder ); + return leaf; + } - K key = btree.getKeySerializer().deserialize( byteBuffer ); - BTreeFactory.setKey( page, i, key ); - } - // and read the last value, as it's a node + /** + * Deserialize a Node from some PageIos + */ + private Node readNode( BTree btree, int nbElems, long revision, ByteBuffer byteBuffer, + PageIO[] pageIos ) throws IOException + { + Node node = BTreeFactory.createNode( btree, revision, nbElems ); + + // Read each value and key + for ( int i = 0; i < nbElems; i++ ) + { + // This is an Offset long offset = OFFSET_SERIALIZER.deserialize( byteBuffer ); long lastOffset = OFFSET_SERIALIZER.deserialize( byteBuffer ); - ElementHolder valueHolder = new PageHolder( btree, null, offset, lastOffset ); - ( ( Node ) page ).setValue( nodeNbElems, valueHolder ); + PageHolder valueHolder = new PageHolder( btree, null, offset, lastOffset ); + node.setValue( i, valueHolder ); + + K key = btree.getKeySerializer().deserialize( byteBuffer ); + BTreeFactory.setKey( node, i, key ); } - return page; + // and read the last value, as it's a node + long offset = OFFSET_SERIALIZER.deserialize( byteBuffer ); + long lastOffset = OFFSET_SERIALIZER.deserialize( byteBuffer ); + + PageHolder valueHolder = new PageHolder( btree, null, offset, lastOffset ); + node.setValue( nbElems, valueHolder ); + + return node; } @@ -1103,27 +1157,7 @@ public class RecordManager if ( nbElems == 0 ) { - // We will have 1 single page if we have no elements - PageIO[] pageIos = new PageIO[1]; - - // This is either a new root page or a new page that will be filled later - PageIO newPage = fetchNewPage(); - - // We need first to create a byte[] that will contain all the data - // For the root page, this is easy, as we only have to store the revision, - // and the number of elements, which is 0. - long position = 0L; - - position = store( position, revision, newPage ); - position = store( position, nbElems, newPage ); - - // Update the page size now - newPage.setSize( ( int ) position ); - - // Insert the result into the array of PageIO - pageIos[0] = newPage; - - return pageIos; + return serializeRootPage( revision ); } else { @@ -1160,101 +1194,27 @@ public class RecordManager serializedSize += buffer.length; // Iterate on the keys and values. We first serialize the value, then the key - // until we are done with all of them. If w are serializing a page, we have + // until we are done with all of them. If we are serializing a page, we have // to serialize one more value for ( int pos = 0; pos < nbElems; pos++ ) { // Start with the value if ( page instanceof Node ) { - Page child = ( ( Node ) page ).getReference( pos ); - - // The first offset - buffer = LongSerializer.serialize( child.getOffset() ); - serializedData.add( buffer ); - dataSize += buffer.length; - - // The last offset - buffer = LongSerializer.serialize( child.getLastOffset() ); - serializedData.add( buffer ); - dataSize += buffer.length; - } - else - { - if ( btree.isAllowDuplicates() ) - { - MultipleMemoryHolder mvHolder = ( MultipleMemoryHolder ) ( ( Leaf ) page ) - .getValue( pos ); - if ( mvHolder.isSingleValue() ) - { - buffer = btree.getValueSerializer().serialize( mvHolder.getValue( btree ) ); - - //FIXME find a better way to avoid the copying - byte[] tmp = new byte[buffer.length + 1]; - tmp[0] = 0; // single value flag - System.arraycopy( buffer, 0, tmp, 1, buffer.length ); - buffer = tmp; - } - else - { - long duplicateContainerOffset = ( ( BTree ) mvHolder.getValue( btree ) ) - .getBtreeOffset(); - buffer = new byte[8 + 1]; - buffer[0] = 1; // sub-tree flag - buffer = LongSerializer.serialize( buffer, 1, duplicateContainerOffset ); - } - } - else - { - ElementHolder value = ( ( Leaf ) page ).getValue( pos ); - buffer = btree.getValueSerializer().serialize( value.getValue( btree ) ); - } - - serializedData.add( buffer ); - dataSize += buffer.length; - } - - // and the key - if ( page instanceof Leaf ) - { - KeyHolder keyHolder = ( ( Leaf ) page ).getKeyHolder( pos ); - ByteBuffer keyData = keyHolder.getBuffer(); - - if ( keyData != null ) - { - serializedData.add( IntSerializer.serialize( keyData.limit() ) ); - serializedData.add( keyData.array() ); - dataSize += keyData.limit() + 4; - } - else - { - serializedData.add( IntSerializer.serialize( 4 ) ); - serializedData.add( Strings.EMPTY_BYTES ); - dataSize += 4; - } + dataSize += serializeNodeValue( ( Node ) page, pos, serializedData ); + dataSize += serializeNodeKey( ( Node ) page, pos, serializedData ); } else { - buffer = btree.getKeySerializer().serialize( page.getKey( pos ) ); - serializedData.add( buffer ); - dataSize += buffer.length; + dataSize += serializeLeafValue( ( Leaf ) page, pos, serializedData ); + dataSize += serializeLeafKey( ( Leaf ) page, pos, serializedData ); } } // Nodes have one more value to serialize if ( page instanceof Node ) { - Page child = ( ( Node ) page ).getReference( nbElems ); - - // The first offset - buffer = LongSerializer.serialize( child.getOffset() ); - serializedData.add( buffer ); - dataSize += buffer.length; - - // The last offset - buffer = LongSerializer.serialize( child.getLastOffset() ); - serializedData.add( buffer ); - dataSize += buffer.length; + dataSize += serializeNodeValue( ( Node ) page, nbElems, serializedData ); } // Store the data size @@ -1281,32 +1241,204 @@ public class RecordManager /** + * Serialize a Node's key + */ + private int serializeNodeKey( Node node, int pos, List serializedData ) + { + byte[] buffer = node.btree.getKeySerializer().serialize( node.getKey( pos ) ); + serializedData.add( buffer ); + + return buffer.length; + } + + + /** + * Serialize a Node's Value. We store the two offsets of the child page. + */ + private int serializeNodeValue( Node node, int pos, List serializedData ) + throws IOException + { + // For a node, we just store the children's offsets + Page child = node.getReference( pos ); + + // The first offset + byte[] buffer = LongSerializer.serialize( child.getOffset() ); + serializedData.add( buffer ); + int dataSize = buffer.length; + + // The last offset + buffer = LongSerializer.serialize( child.getLastOffset() ); + serializedData.add( buffer ); + dataSize += buffer.length; + + return dataSize; + } + + + /** + * Serialize a Leaf's key + */ + private int serializeLeafKey( Leaf leaf, int pos, List serializedData ) + { + int dataSize = 0; + KeyHolder keyHolder = leaf.getKeyHolder( pos ); + byte[] keyData = keyHolder.getBuffer(); + + if ( keyData != null ) + { + byte[] data = new byte[keyData.length]; + + // The key length + byte[] buffer = IntSerializer.serialize( data.length ); + serializedData.add( buffer ); + dataSize += buffer.length; + + // The key data + serializedData.add( keyData ); + dataSize += data.length; + } + else + { + serializedData.add( IntSerializer.serialize( 0 ) ); + dataSize += 4; + } + + return dataSize; + } + + + /** + * Serialize a Leaf's Value. We store + */ + private int serializeLeafValue( Leaf leaf, int pos, List serializedData ) + throws IOException + { + // The value can be an Array or a sub-btree, but we don't care + // we just iterate on all the values + ValueHolder valueHolder = leaf.getValue( pos ); + + // First take the number of values + int nbValues = valueHolder.size(); + int dataSize = 0; + + if ( nbValues == 0 ) + { + // No value. + byte[] buffer = IntSerializer.serialize( nbValues ); + serializedData.add( buffer ); + + return buffer.length; + } + + if ( valueHolder.isSubBtree() ) + { + byte[] buffer = IntSerializer.serialize( -nbValues ); + serializedData.add( buffer ); + dataSize += buffer.length; + + // the BTree offset + buffer = LongSerializer.serialize( valueHolder.getOffset() ); + serializedData.add( buffer ); + dataSize += buffer.length; + } + else + { + // This is an array, store the nb of values as a positive number + byte[] buffer = IntSerializer.serialize( nbValues ); + serializedData.add( buffer ); + dataSize += buffer.length; + + // Now store each value + byte[] data = valueHolder.getRaw(); + buffer = IntSerializer.serialize( data.length ); + serializedData.add( buffer ); + dataSize += buffer.length; + + if ( data.length > 0 ) + { + serializedData.add( data ); + } + + dataSize += data.length; + } + + return dataSize; + } + + + /** + * Write a root page with no elements in it + */ + private PageIO[] serializeRootPage( long revision ) throws IOException + { + // We will have 1 single page if we have no elements + PageIO[] pageIos = new PageIO[1]; + + // This is either a new root page or a new page that will be filled later + PageIO newPage = fetchNewPage(); + + // We need first to create a byte[] that will contain all the data + // For the root page, this is easy, as we only have to store the revision, + // and the number of elements, which is 0. + long position = 0L; + + position = store( position, revision, newPage ); + position = store( position, 0, newPage ); + + // Update the page size now + newPage.setSize( ( int ) position ); + + // Insert the result into the array of PageIO + pageIos[0] = newPage; + + return pageIos; + } + + + /** * Update the header, injecting the nbBtree, firstFreePage and lastFreePage */ private void updateRecordManagerHeader() throws IOException { - HEADER_BUFFER.clear(); - // The page size - HEADER_BUFFER.putInt( pageSize ); + HEADER_BYTES[0] = ( byte ) ( pageSize >>> 24 ); + HEADER_BYTES[1] = ( byte ) ( pageSize >>> 16 ); + HEADER_BYTES[2] = ( byte ) ( pageSize >>> 8 ); + HEADER_BYTES[3] = ( byte ) ( pageSize ); // The number of managed BTree (currently we have only one : the discardedPage BTree - HEADER_BUFFER.putInt( nbBtree ); + HEADER_BYTES[4] = ( byte ) ( nbBtree >>> 24 ); + HEADER_BYTES[5] = ( byte ) ( nbBtree >>> 16 ); + HEADER_BYTES[6] = ( byte ) ( nbBtree >>> 8 ); + HEADER_BYTES[7] = ( byte ) ( nbBtree ); // The first free page - HEADER_BUFFER.putLong( firstFreePage ); + HEADER_BYTES[8] = ( byte ) ( firstFreePage >>> 56 ); + HEADER_BYTES[9] = ( byte ) ( firstFreePage >>> 48 ); + HEADER_BYTES[10] = ( byte ) ( firstFreePage >>> 40 ); + HEADER_BYTES[11] = ( byte ) ( firstFreePage >>> 32 ); + HEADER_BYTES[12] = ( byte ) ( firstFreePage >>> 24 ); + HEADER_BYTES[13] = ( byte ) ( firstFreePage >>> 16 ); + HEADER_BYTES[14] = ( byte ) ( firstFreePage >>> 8 ); + HEADER_BYTES[15] = ( byte ) ( firstFreePage ); // The last free page - HEADER_BUFFER.putLong( lastFreePage ); - - // Set the limit to the end of the page - HEADER_BUFFER.limit( pageSize ); + HEADER_BYTES[16] = ( byte ) ( lastFreePage >>> 56 ); + HEADER_BYTES[17] = ( byte ) ( lastFreePage >>> 48 ); + HEADER_BYTES[18] = ( byte ) ( lastFreePage >>> 40 ); + HEADER_BYTES[19] = ( byte ) ( lastFreePage >>> 32 ); + HEADER_BYTES[20] = ( byte ) ( lastFreePage >>> 24 ); + HEADER_BYTES[21] = ( byte ) ( lastFreePage >>> 16 ); + HEADER_BYTES[22] = ( byte ) ( lastFreePage >>> 8 ); + HEADER_BYTES[23] = ( byte ) ( lastFreePage ); // Write the header on disk - HEADER_BUFFER.rewind(); + HEADER_BUFFER.put( HEADER_BYTES ); + HEADER_BUFFER.flip(); LOG.debug( "Update RM header, FF : {}, LF : {}", firstFreePage, lastFreePage ); fileChannel.write( HEADER_BUFFER, 0 ); + HEADER_BUFFER.clear(); nbUpdateRMHeader.incrementAndGet(); } Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RevisionNameSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RevisionNameSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RevisionNameSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/RevisionNameSerializer.java Sun Oct 27 08:48:07 2013 @@ -86,6 +86,42 @@ public class RevisionNameSerializer exte /** + * A method used to deserialize a RevisionName from a byte array. + * + * @param in The byte array containing the RevisionName + * @return A RevisionName instance + */ + public RevisionName fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize a RevisionName from a byte array. + * + * @param in The byte array containing the RevisionName + * @param start the position in the byte[] we will deserialize the RevisionName from + * @return A RevisionName instance + */ + public RevisionName fromBytes( byte[] in, int start ) + { + // The buffer must be 8 bytes plus 4 bytes long (the revision is a long, and the name is a String + if ( ( in == null ) || ( in.length < 12 + start ) ) + { + throw new RuntimeException( "Cannot extract a RevisionName from a buffer with not enough bytes" ); + } + + long revision = LongSerializer.deserialize( in, start ); + String name = StringSerializer.deserialize( in, 8 + start ); + + RevisionName revisionName = new RevisionName( revision, name ); + + return revisionName; + } + + + /** * {@inheritDoc} */ @Override Added: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/ValueHolder.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/ValueHolder.java?rev=1536066&view=auto ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/ValueHolder.java (added) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/managed/ValueHolder.java Sun Oct 27 08:48:07 2013 @@ -0,0 +1,1076 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.directory.mavibot.btree.managed; + + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.Comparator; +import java.util.UUID; + +import org.apache.directory.mavibot.btree.TupleCursor; +import org.apache.directory.mavibot.btree.ValueCursor; +import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException; +import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException; +import org.apache.directory.mavibot.btree.serializer.ElementSerializer; +import org.apache.directory.mavibot.btree.serializer.IntSerializer; +import org.apache.directory.mavibot.btree.util.Strings; + + +/** + * A holder to store the Values + * + * @author Apache Directory Project + * @param The value type + */ +public class ValueHolder implements Cloneable +{ + /** The deserialized value */ + private V[] valueArray; + + /** The BTree storing multiple value, if we have moe than a threashold values */ + private BTree valueBtree; + + /** The serialized value */ + private byte[] raw; + + /** A flag set to true if the values are stored in a BTree */ + private boolean isSubBtree = false; + + /** The RecordManager */ + private RecordManager recordManager; + + /** The Value serializer */ + private ElementSerializer valueSerializer; + + /** An internal flag used when the values are not yet deserialized */ + private boolean isRaw = true; + + + /** + * Creates a new instance of a ValueHolder, containing the serialized values + * + * @param valueSerializer The Value's serializer + * @param raw The raw data containing the values + */ + /* No qualifier */ValueHolder( RecordManager recordManager, ElementSerializer valueSerializer, + boolean isSubBtree, int nbValues, + byte[] raw ) + { + this.valueSerializer = valueSerializer; + this.recordManager = recordManager; + this.raw = raw; + this.isSubBtree = isSubBtree; + + if ( nbValues < BTree.valueThresholdUp ) + { + // Keep an array + valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), nbValues ); + } + else + { + // Use a sub btree + + //raw = ByteBuffer.wrap( valueSerializer.serialize( key ) ); + } + } + + + /** + * Creates a new instance of a ValueHolder, containing the serialized values + * + * @param valueSerializer The Value's serializer + * @param raw The raw data containing the values + */ + /* No qualifier */ValueHolder( RecordManager recordManager, ElementSerializer valueSerializer, + BTree subBtree ) + { + this.valueSerializer = valueSerializer; + this.recordManager = recordManager; + raw = null; + isRaw = false; + isSubBtree = true; + valueBtree = subBtree; + } + + + /** + * Creates a new instance of a ValueHolder, containing Values + * + * @param valueSerializer The Value's serializer + * @param values The Values stored in the ValueHolder + */ + /* No qualifier */ValueHolder( RecordManager recordManager, ElementSerializer valueSerializer, V... values ) + { + this.valueSerializer = valueSerializer; + this.recordManager = recordManager; + + if ( values != null ) + { + int nbValues = values.length; + + if ( nbValues < BTree.valueThresholdUp ) + { + // Keep an array + valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), nbValues ); + + try + { + System.arraycopy( values, 0, valueArray, 0, values.length ); + } + catch ( ArrayStoreException ase ) + { + ase.printStackTrace(); + throw ase; + } + + // Serialize the values + byte[][] data = new byte[nbValues][]; + int pos = 0; + int length = 0; + + for ( V value : values ) + { + byte[] serializedValue = valueSerializer.serialize( value ); + + data[pos++] = serializedValue; + length += serializedValue.length; + } + + raw = new byte[length]; + pos = 0; + + for ( byte[] bytes : data ) + { + System.arraycopy( bytes, 0, raw, pos, bytes.length ); + pos += bytes.length; + } + } + else + { + // Use a sub btree, now that we have reached the threshold + createSubTree(); + + // Now inject all the values into it + for ( V value : values ) + { + try + { + valueBtree.insert( value, value ); + } + catch ( IOException e ) + { + e.printStackTrace(); + } + } + } + } + else + { + // No value, we create an empty array + valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), 0 ); + + //raw = ByteBuffer.wrap( valueSerializer.serialize( key ) ); + } + + isRaw = false; + } + + + /** + * @return a cursor on top of the values + */ + public ValueCursor getCursor() + { + checkRaw(); + + ValueCursor cursor; + + if ( isSubBtree ) + { + cursor = new ValueBtreeCursor(); + } + else + { + cursor = new ValueArrayCursor(); + } + + return cursor; + } + + /** + * A class that encapsulate the values into an array + */ + private class ValueArrayCursor implements ValueCursor + { + /** Store the current position in the array or in the BTree */ + private int currentPos; + + + /** + * Create an instance + */ + private ValueArrayCursor() + { + // Start at -1 to be positioned before the first element + currentPos = -1; + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNext() + { + if ( valueArray == null ) + { + // Load the array from the raw data + return false; + } + else + { + return ( valueArray != null ) && ( currentPos < valueArray.length ); + } + } + + + /** + * {@inheritDoc} + */ + public V next() + { + if ( valueArray == null ) + { + // Deserialize the array + return null; + } + else + { + currentPos++; + + if ( currentPos == valueArray.length ) + { + // We have reached the end of the array + return null; + } + else + { + return valueArray[currentPos]; + } + } + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean hasPrev() throws EndOfFileExceededException, IOException + { + return false; + } + + + /** + * {@inheritDoc} + */ + @Override + public void close() + { + } + + + /** + * {@inheritDoc} + */ + @Override + public void beforeFirst() throws IOException + { + } + + + /** + * {@inheritDoc} + */ + @Override + public void afterLast() throws IOException + { + } + + + /** + * {@inheritDoc} + */ + @Override + public V prev() throws EndOfFileExceededException, IOException + { + return null; + } + + + /** + * {@inheritDoc} + */ + @Override + public int size() + { + if ( valueArray != null ) + { + return valueArray.length; + } + else + { + return 0; + } + } + } + + /** + * A class that encapsulate the values into an sub-btree + */ + private class ValueBtreeCursor implements ValueCursor + { + /** Store the current position in the array or in the BTree */ + private TupleCursor cursor; + + + /** + * Create an instance + */ + private ValueBtreeCursor() + { + // Start at -1 to be positionned before the first element + try + { + if ( valueBtree != null ) + { + cursor = valueBtree.browse(); + } + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public boolean hasNext() + { + if ( cursor == null ) + { + return false; + } + else + { + try + { + return cursor.hasNext(); + } + catch ( EndOfFileExceededException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + } + } + + + /** + * {@inheritDoc}} + */ + public V next() + { + try + { + return cursor.next().getKey(); + } + catch ( EndOfFileExceededException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public boolean hasPrev() throws EndOfFileExceededException, IOException + { + if ( cursor == null ) + { + return false; + } + else + { + try + { + return cursor.hasPrev(); + } + catch ( EndOfFileExceededException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public void close() + { + if ( cursor != null ) + { + cursor.close(); + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public void beforeFirst() throws IOException + { + if ( cursor != null ) + { + cursor.beforeFirst(); + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public void afterLast() throws IOException + { + if ( cursor != null ) + { + cursor.afterLast(); + } + } + + + /** + * {@inheritDoc}} + */ + @Override + public V prev() throws EndOfFileExceededException, IOException + { + try + { + return cursor.prev().getKey(); + } + catch ( EndOfFileExceededException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + + /** + * {@inheritDoc} + */ + @Override + public int size() + { + if ( valueBtree != null ) + { + return ( int ) valueBtree.getNbElems(); + } + else + { + return 0; + } + } + } + + + /** + * @return the raw representation of the value holder. + */ + public byte[] getRaw() + { + if ( isRaw ) + { + // We don't have to serialize the ValueHolder, it has not been changed + return raw; + } + else + { + // Ok, some values have been added/modified/removed, we have to serialize the ValueHolder + byte[][] valueBytes = new byte[valueArray.length * 2][]; + int length = 0; + int pos = 0; + + for ( V value : valueArray ) + { + // Serialize the value + byte[] bytes = valueSerializer.serialize( value ); + length += bytes.length; + + // Serialize the value's length + byte[] sizeBytes = IntSerializer.serialize( bytes.length ); + length += sizeBytes.length; + + // And store the two byte[] + valueBytes[pos++] = sizeBytes; + valueBytes[pos++] = bytes; + } + + raw = new byte[length]; + pos = 0; + + for ( byte[] bytes : valueBytes ) + { + System.arraycopy( bytes, 0, raw, pos, bytes.length ); + pos += bytes.length; + } + + return raw; + } + } + + + /** + * @return the isSubBtree + */ + public boolean isSubBtree() + { + return isSubBtree; + } + + + /** + * @return the number of stored values + */ + public int size() + { + if ( isSubBtree ) + { + return ( int ) valueBtree.getNbElems(); + } + else + { + return valueArray.length; + } + } + + + /** + * Create a new Sub-BTree to store the values. + */ + private void createSubTree() + { + try + { + valueBtree = new BTree( UUID.randomUUID().toString(), valueSerializer, valueSerializer ); + + try + { + recordManager.manage( valueBtree, true ); + isSubBtree = true; + isRaw = false; + raw = null; + } + catch ( BTreeAlreadyManagedException e ) + { + // should never happen + throw new RuntimeException( e ); + } + } + catch ( IOException e ) + { + throw new RuntimeException( e ); + } + } + + + /** + * Set the subBtree in the ValueHolder + */ + /* No qualifier*/void setSubBtree( BTree subBtree ) + { + valueBtree = subBtree; + raw = null; + isRaw = false; + isSubBtree = true; + valueArray = null; + } + + + /** + * Add a new value in the ValueHolder + * + * @param value The added value + */ + public void add( V value ) + { + checkRaw(); + + if ( !isSubBtree ) + { + // We have to check that we have reached the threshold or not + if ( valueArray.length + 1 > BTree.valueThresholdUp ) + { + // Ok, transform the array into a btree + createSubTree(); + + try + { + for ( V val : valueArray ) + { + // Here, we should insert all the values in one shot then + // write the btree on disk only once. + valueBtree.insert( val, null ); + } + + // We can delete the array now + valueArray = null; + + // And inject the new value + valueBtree.insert( value, null ); + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + // First check that the value is not already present in the ValueHolder + int pos = findPos( value ); + + if ( pos >= 0 ) + { + // The value exists : nothing to do + return; + } + + // Ok, we just have to insert the new element at the right position + // We transform the position to a positive value + pos = -( pos + 1 ); + // First, copy the array + V[] newValueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length + 1 ); + + System.arraycopy( valueArray, 0, newValueArray, 0, pos ); + newValueArray[pos] = value; + System.arraycopy( valueArray, pos, newValueArray, pos + 1, valueArray.length - pos ); + + // And switch the arrays + valueArray = newValueArray; + } + } + else + { + try + { + valueBtree.insert( value, null ); + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + + /** + * Add a new value in the ValueHolder + * + * @param value The added value + */ + public void remove( V value ) + { + checkRaw(); + + if ( !isSubBtree ) + { + // First check that the value is not already present in the ValueHolder + int pos = findPos( value ); + + if ( pos < 0 ) + { + // The value does not exists : nothing to do + return; + } + + // Ok, we just have to delete the new element at the right position + // First, copy the array + V[] newValueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length + 1 ); + + System.arraycopy( valueArray, 0, newValueArray, 0, pos ); + System.arraycopy( valueArray, pos + 1, newValueArray, pos, valueArray.length - pos - 1 ); + + // And switch the arrays + valueArray = newValueArray; + } + else + { + try + { + valueBtree.delete( value ); + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + + /** + * Add a new value in the ValueHolder + * + * @param value The added value + */ + public boolean contains( V value ) + { + checkRaw(); + + if ( isSubBtree ) + { + try + { + return valueBtree.hasKey( value ); + } + catch ( IOException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + if ( valueArray.length == 0 ) + { + return false; + } + + // Do a search using dichotomy + return findPos( value ) >= 0; + } + + return true; + } + + + /** + * Find the position of a given value in the array, or the position where we + * would insert the element (in this case, the position will be negative). + * As we use a 0-based array, the negative position for 0 is -1. + * -1 means the element can be added in position 0 + * -2 means the element can be added in position 1 + * ... + */ + private int findPos( V value ) + { + if ( valueArray.length == 0 ) + { + return -1; + } + + // Do a search using dichotomy + int pivot = valueArray.length / 2; + int low = 0; + int high = valueArray.length - 1; + Comparator comparator = valueSerializer.getComparator(); + + while ( high > low ) + { + switch ( high - low ) + { + case 1: + // We have 2 elements + int result = comparator.compare( value, valueArray[pivot] ); + + if ( result == 0 ) + { + return pivot; + } + + if ( result < 0 ) + { + if ( pivot == low ) + { + return -( low + 1 ); + } + else + { + result = comparator.compare( value, valueArray[low] ); + + if ( result == 0 ) + { + return low; + } + + if ( result < 0 ) + { + return -( low + 1 ); + } + else + { + return -( low + 2 ); + } + } + } + else + { + if ( pivot == high ) + { + return -( high + 2 ); + } + else + { + result = comparator.compare( value, valueArray[high] ); + + if ( result == 0 ) + { + return high; + } + + if ( result < 0 ) + { + return -( high + 1 ); + } + else + { + return -( high + 2 ); + } + } + } + + default: + // We have 3 elements + result = comparator.compare( value, valueArray[pivot] ); + + if ( result == 0 ) + { + return pivot; + } + + if ( result < 0 ) + { + high = pivot - 1; + } + else + { + low = pivot + 1; + } + + pivot = ( high + low ) / 2; + + continue; + } + } + + int result = comparator.compare( value, valueArray[pivot] ); + + if ( result == 0 ) + { + return pivot; + } + + if ( result < 0 ) + { + return -( pivot + 1 ); + } + else + { + return -( pivot + 2 ); + } + } + + + /** + * Create a clone of this instance + */ + public ValueHolder clone() throws CloneNotSupportedException + { + ValueHolder copy = ( ValueHolder ) super.clone(); + + //copy the valueArray if it's not null + // We don't clone the BTree, as we will create new revisions when + //modifying it + if ( ( !isSubBtree ) && ( valueArray != null ) ) + { + copy.valueArray = ( V[] ) Array.newInstance( valueSerializer.getType(), valueArray.length ); + System.arraycopy( valueArray, 0, copy.valueArray, 0, valueArray.length ); + } + + return copy; + } + + + /** + * Check if we haven't yet deserialized the values, and if so, do it + */ + private void checkRaw() + { + if ( isRaw ) + { + // We haven't yet deserialized the values. Let's do it now + if ( isSubBtree ) + { + // This is a sub BTree, we have to read the tree from the offsets + + } + else + { + // We have to deserialize the array of values + int index = 0; + int pos = 0; + + while ( pos < raw.length ) + { + try + { + int size = IntSerializer.deserialize( raw, pos ); + pos += 4; + V value = valueSerializer.fromBytes( raw, pos ); + pos += size; + valueArray[index++] = value; + } + catch ( IOException e ) + { + System.out.println( Strings.dumpBytes( raw ) ); + } + } + } + + isRaw = false; + } + } + + + /** + * @return The sub-btree offset + */ + /* No qualifier */long getOffset() + { + if ( isSubBtree ) + { + return valueBtree.getBtreeOffset(); + } + else + { + return -1L; + } + } + + + /** + * @see Object#toString() + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "ValueHolder[" ).append( valueSerializer.getClass().getSimpleName() ); + + if ( isRaw ) + { + sb.append( ", isRaw[" ).append( raw.length ).append( "]" ); + } + else + { + if ( isSubBtree ) + { + sb.append( ", SubBTree" ); + } + else + { + sb.append( ", array{" ); + + if ( valueArray == null ) + { + sb.append( "}" ); + } + else + { + boolean isFirst = true; + + for ( V value : valueArray ) + { + if ( isFirst ) + { + isFirst = false; + } + else + { + sb.append( "/" ); + } + + sb.append( value ); + } + + sb.append( "}" ); + } + } + } + + sb.append( "]" ); + + return sb.toString(); + } +} Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/BTree.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/BTree.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/BTree.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/BTree.java Sun Oct 27 08:48:07 2013 @@ -40,10 +40,10 @@ import net.sf.ehcache.config.CacheConfig import org.apache.directory.mavibot.btree.Addition; import org.apache.directory.mavibot.btree.BTreeHeader; -import org.apache.directory.mavibot.btree.Cursor; import org.apache.directory.mavibot.btree.Deletion; import org.apache.directory.mavibot.btree.Modification; import org.apache.directory.mavibot.btree.Tuple; +import org.apache.directory.mavibot.btree.TupleCursor; import org.apache.directory.mavibot.btree.exception.KeyNotFoundException; import org.apache.directory.mavibot.btree.serializer.BufferHandler; import org.apache.directory.mavibot.btree.serializer.ElementSerializer; @@ -935,14 +935,14 @@ public class BTree implements Clos * @return A cursor on the btree * @throws IOException */ - public Cursor browse() throws IOException + public TupleCursor browse() throws IOException { Transaction transaction = beginReadTransaction(); // Fetch the root page for this revision LinkedList> stack = new LinkedList>(); - Cursor cursor = rootPage.browse( transaction, stack ); + TupleCursor cursor = rootPage.browse( transaction, stack ); return cursor; } @@ -956,7 +956,7 @@ public class BTree implements Clos * @throws IOException If we had an issue while fetching data from the disk * @throws KeyNotFoundException If the key is not found in the BTree */ - public Cursor browse( long revision ) throws IOException, KeyNotFoundException + public TupleCursor browse( long revision ) throws IOException, KeyNotFoundException { Transaction transaction = beginReadTransaction(); @@ -965,7 +965,7 @@ public class BTree implements Clos // And get the cursor LinkedList> stack = new LinkedList>(); - Cursor cursor = revisionRootPage.browse( transaction, stack ); + TupleCursor cursor = revisionRootPage.browse( transaction, stack ); return cursor; } @@ -979,12 +979,12 @@ public class BTree implements Clos * @return A cursor on the btree * @throws IOException */ - public Cursor browseFrom( K key ) throws IOException + public TupleCursor browseFrom( K key ) throws IOException { Transaction transaction = beginReadTransaction(); // Fetch the root page for this revision - Cursor cursor = rootPage.browse( key, transaction, new LinkedList>() ); + TupleCursor cursor = rootPage.browse( key, transaction, new LinkedList>() ); return cursor; } @@ -1000,7 +1000,7 @@ public class BTree implements Clos * @throws IOException If wxe had an issue reading the BTree from disk * @throws KeyNotFoundException If we can't find a rootPage for this revision */ - public Cursor browseFrom( long revision, K key ) throws IOException, KeyNotFoundException + public TupleCursor browseFrom( long revision, K key ) throws IOException, KeyNotFoundException { Transaction transaction = beginReadTransaction(); @@ -1009,7 +1009,7 @@ public class BTree implements Clos // And get the cursor LinkedList> stack = new LinkedList>(); - Cursor cursor = revisionRootPage.browse( key, transaction, stack ); + TupleCursor cursor = revisionRootPage.browse( key, transaction, stack ); return cursor; } @@ -1222,7 +1222,7 @@ public class BTree implements Clos // Create a buffer containing 200 4Kb pages (around 1Mb) ByteBuffer bb = ByteBuffer.allocateDirect( writeBufferSize ); - Cursor cursor = browse(); + TupleCursor cursor = browse(); if ( keySerializer == null ) { Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/CursorImpl.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/CursorImpl.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/CursorImpl.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/CursorImpl.java Sun Oct 27 08:48:07 2013 @@ -28,8 +28,8 @@ import java.io.IOException; import java.util.LinkedList; import java.util.NoSuchElementException; -import org.apache.directory.mavibot.btree.Cursor; import org.apache.directory.mavibot.btree.Tuple; +import org.apache.directory.mavibot.btree.TupleCursor; import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException; @@ -44,7 +44,7 @@ import org.apache.directory.mavibot.btre * * @author Apache Directory Project */ -public class CursorImpl implements Cursor +public class CursorImpl implements TupleCursor { /** The transaction used for this cursor */ private Transaction transaction; Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/Node.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/Node.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/Node.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/memory/Node.java Sun Oct 27 08:48:07 2013 @@ -884,7 +884,6 @@ import org.apache.directory.mavibot.btre if ( page == null ) { System.out.println( "Page is null for pos = " + pos + ", children = " + children[pos] ); - System.out.println( "Key = " + key ); } return page.hasKey( key ); Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/AbstractElementSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/AbstractElementSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/AbstractElementSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/AbstractElementSerializer.java Sun Oct 27 08:48:07 2013 @@ -20,6 +20,8 @@ package org.apache.directory.mavibot.btree.serializer; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Comparator; @@ -60,9 +62,18 @@ public abstract class AbstractElementSer { Type[] argumentTypes = ( ( ParameterizedType ) types[0] ).getActualTypeArguments(); - if ( ( argumentTypes != null ) && ( argumentTypes.length > 0 ) && ( argumentTypes[0] instanceof Class ) ) + if ( ( argumentTypes != null ) && ( argumentTypes.length > 0 ) ) { - type = ( Class ) argumentTypes[0]; + if ( argumentTypes[0] instanceof Class ) + { + type = ( Class ) argumentTypes[0]; + } + else if ( argumentTypes[0] instanceof GenericArrayType ) + { + Class clazz = ( Class ) ( ( GenericArrayType ) argumentTypes[0] ).getGenericComponentType(); + + type = Array.newInstance( clazz, 0 ).getClass(); + } } } } Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/BooleanSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/BooleanSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/BooleanSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/BooleanSerializer.java Sun Oct 27 08:48:07 2013 @@ -114,6 +114,36 @@ public class BooleanSerializer extends A /** + * A method used to deserialize a Boolean from a byte array. + * + * @param in The byte array containing the boolean + * @return A boolean + */ + public Boolean fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize a Boolean from a byte array. + * + * @param in The byte array containing the boolean + * @param start the position in the byte[] we will deserialize the boolean from + * @return A boolean + */ + public Boolean fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 1 + start ) ) + { + throw new RuntimeException( "Cannot extract a Boolean from a buffer with not enough bytes" ); + } + + return in[start] == 0x01; + } + + + /** * {@inheritDoc} */ public Boolean deserialize( ByteBuffer buffer ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteArraySerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteArraySerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteArraySerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteArraySerializer.java Sun Oct 27 08:48:07 2013 @@ -221,6 +221,73 @@ public class ByteArraySerializer extends /** + * A method used to deserialize a byte array from a byte array. + * + * @param in The byte array containing the byte array + * @return A byte[] + */ + public byte[] fromBytes( byte[] in ) + { + if ( ( in == null ) || ( in.length < 4 ) ) + { + throw new RuntimeException( "Cannot extract a byte[] from a buffer with not enough bytes" ); + } + + int len = IntSerializer.deserialize( in ); + + switch ( len ) + { + case 0: + return new byte[] + {}; + + case -1: + return null; + + default: + byte[] result = new byte[len]; + System.arraycopy( in, 4, result, 0, len ); + + return result; + } + } + + + /** + * A method used to deserialize a byte array from a byte array. + * + * @param in The byte array containing the byte array + * @param start the position in the byte[] we will deserialize the byte[] from + * @return A byte[] + */ + public byte[] fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 4 + start ) ) + { + throw new RuntimeException( "Cannot extract a byte[] from a buffer with not enough bytes" ); + } + + int len = IntSerializer.deserialize( in, start ); + + switch ( len ) + { + case 0: + return new byte[] + {}; + + case -1: + return null; + + default: + byte[] result = new byte[len]; + System.arraycopy( in, 4 + start, result, 0, len ); + + return result; + } + } + + + /** * {@inheritDoc} */ public byte[] deserialize( BufferHandler bufferHandler ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ByteSerializer.java Sun Oct 27 08:48:07 2013 @@ -112,6 +112,34 @@ public class ByteSerializer extends Abst /** + * A method used to deserialize a Byte from a byte array. + * @param in The byte array containing the Byte + * @return A Byte + */ + public Byte fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize a Byte from a byte array. + * @param in The byte array containing the Byte + * @param start the position in the byte[] we will deserialize the byte from + * @return A Byte + */ + public Byte fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 1 + start ) ) + { + throw new RuntimeException( "Cannot extract a Byte from a buffer with not enough bytes" ); + } + + return in[start]; + } + + + /** * {@inheritDoc} */ public Byte deserialize( ByteBuffer buffer ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharArraySerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharArraySerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharArraySerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharArraySerializer.java Sun Oct 27 08:48:07 2013 @@ -137,6 +137,82 @@ public class CharArraySerializer extends /** + * A method used to deserialize a char array from a byte array. + * + * @param in The byte array containing the char array + * @return A char[] + */ + public char[] fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length - start < 4 ) ) + { + throw new RuntimeException( "Cannot extract a byte[] from a buffer with not enough bytes" ); + } + + int len = IntSerializer.deserialize( in, start ); + + switch ( len ) + { + case 0: + return new char[] + {}; + + case -1: + return null; + + default: + char[] result = new char[len]; + + for ( int i = 4; i < len * 2 + 4; i += 2 ) + { + result[i] = Character.valueOf( ( char ) ( ( in[i] << 8 ) + + ( in[i + 1] & 0xFF ) ) ); + } + + return result; + } + } + + + /** + * A method used to deserialize a char array from a byte array. + * + * @param in The byte array containing the char array + * @return A char[] + */ + public char[] fromBytes( byte[] in ) + { + if ( ( in == null ) || ( in.length < 4 ) ) + { + throw new RuntimeException( "Cannot extract a byte[] from a buffer with not enough bytes" ); + } + + int len = IntSerializer.deserialize( in ); + + switch ( len ) + { + case 0: + return new char[] + {}; + + case -1: + return null; + + default: + char[] result = new char[len]; + + for ( int i = 4; i < len * 2 + 4; i += 2 ) + { + result[i] = Character.valueOf( ( char ) ( ( in[i] << 8 ) + + ( in[i + 1] & 0xFF ) ) ); + } + + return result; + } + } + + + /** * {@inheritDoc} */ public char[] deserialize( BufferHandler bufferHandler ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/CharSerializer.java Sun Oct 27 08:48:07 2013 @@ -114,6 +114,35 @@ public class CharSerializer extends Abst /** + * A method used to deserialize a Character from a byte array. + * @param in The byte array containing the Character + * @return A Character + */ + public Character fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A static method used to deserialize a Character from a byte array. + * @param in The byte array containing the Character + * @param start the position in the byte[] we will deserialize the char from + * @return A Character + */ + public Character fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 2 + start ) ) + { + throw new RuntimeException( "Cannot extract a Character from a buffer with not enough bytes" ); + } + + return Character.valueOf( ( char ) ( ( in[start] << 8 ) + + ( in[start + 1] & 0xFF ) ) ); + } + + + /** * {@inheritDoc} */ public Character deserialize( ByteBuffer buffer ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ElementSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ElementSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ElementSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ElementSerializer.java Sun Oct 27 08:48:07 2013 @@ -64,6 +64,26 @@ public interface ElementSerializer /** + * Deserialize an element from a byte[] + * + * @param buffer The incoming byte[] + * @return The deserialized element + * @throws IOException If the deserialization failed + */ + T fromBytes( byte[] buffer ) throws IOException; + + + /** + * Deserialize an element from a byte[] + * + * @param buffer The incoming byte[] + * @return The deserialized element + * @throws IOException If the deserialization failed + */ + T fromBytes( byte[] buffer, int pos ) throws IOException; + + + /** * Returns the comparison of two types.
*
    *
  • If type1 < type2, return -1
  • Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/IntSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/IntSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/IntSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/IntSerializer.java Sun Oct 27 08:48:07 2013 @@ -74,6 +74,37 @@ public class IntSerializer extends Abstr /** + * A method used to deserialize an Integer from a byte array. + * @param in The byte array containing the Integer + * @return An Integer + */ + public Integer fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize an Integer from a byte array. + * @param in The byte array containing the Integer + * @param start the position in the byte[] we will deserialize the int from + * @return An Integer + */ + public Integer fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 4 + start ) ) + { + throw new RuntimeException( "Cannot extract a Integer from a buffer with not enough bytes" ); + } + + return ( in[start] << 24 ) + + ( ( in[start + 1] & 0xFF ) << 16 ) + + ( ( in[start + 2] & 0xFF ) << 8 ) + + ( in[start + 3] & 0xFF ); + } + + + /** * {@inheritDoc} */ public Integer deserialize( ByteBuffer buffer ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongArraySerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongArraySerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongArraySerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongArraySerializer.java Sun Oct 27 08:48:07 2013 @@ -256,4 +256,62 @@ public class LongArraySerializer extends } } } + + + @Override + public long[] fromBytes( byte[] buffer ) throws IOException + { + int len = IntSerializer.deserialize( buffer ); + int pos = 4; + + switch ( len ) + { + case 0: + return new long[] + {}; + + case -1: + return null; + + default: + long[] longs = new long[len]; + + for ( int i = 0; i < len; i++ ) + { + longs[i] = LongSerializer.deserialize( buffer, pos ); + pos += 8; + } + + return longs; + } + } + + + @Override + public long[] fromBytes( byte[] buffer, int pos ) throws IOException + { + int newPos = pos; + int len = IntSerializer.deserialize( buffer, newPos ); + + switch ( len ) + { + case 0: + return new long[] + {}; + + case -1: + return null; + + default: + long[] longs = new long[len]; + + for ( int i = 0; i < len; i++ ) + { + longs[i] = LongSerializer.deserialize( buffer, newPos ); + newPos += 8; + } + + return longs; + } + } } Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/LongSerializer.java Sun Oct 27 08:48:07 2013 @@ -127,6 +127,44 @@ public class LongSerializer extends Abst /** + * A method used to deserialize a Long from a byte array. + * @param in The byte array containing the Long + * @param start the position in the byte[] we will deserialize the long from + * @return A Long + */ + public Long fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize an Integer from a byte array. + * @param in The byte array containing the Integer + * @param start the position in the byte[] we will deserialize the long from + * @return An Integer + */ + public Long fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 8 + start ) ) + { + throw new RuntimeException( "Cannot extract a Long from a buffer with not enough bytes" ); + } + + long result = ( ( long ) in[start] << 56 ) + + ( ( in[start + 1] & 0xFFL ) << 48 ) + + ( ( in[start + 2] & 0xFFL ) << 40 ) + + ( ( in[start + 3] & 0xFFL ) << 32 ) + + ( ( in[start + 4] & 0xFFL ) << 24 ) + + ( ( in[start + 5] & 0xFFL ) << 16 ) + + ( ( in[start + 6] & 0xFFL ) << 8 ) + + ( in[start + 7] & 0xFFL ); + + return result; + } + + + /** * {@inheritDoc} */ public Long deserialize( BufferHandler bufferHandler ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ShortSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ShortSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ShortSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/ShortSerializer.java Sun Oct 27 08:48:07 2013 @@ -113,6 +113,34 @@ public class ShortSerializer extends Abs /** + * A method used to deserialize a Short from a byte array. + * @param in The byte array containing the Short + * @return A Short + */ + public Short fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize a Short from a byte array. + * @param in The byte array containing the Short + * @param start the position in the byte[] we will deserialize the short from + * @return A Short + */ + public Short fromBytes( byte[] in, int start ) + { + if ( ( in == null ) || ( in.length < 2 + start ) ) + { + throw new RuntimeException( "Cannot extract a Short from a buffer with not enough bytes" ); + } + + return ( short ) ( ( in[start] << 8 ) + ( in[start + 1] & 0xFF ) ); + } + + + /** * {@inheritDoc} */ public Short deserialize( ByteBuffer buffer ) throws IOException Modified: directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/StringSerializer.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/StringSerializer.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/StringSerializer.java (original) +++ directory/mavibot/trunk/mavibot/src/main/java/org/apache/directory/mavibot/btree/serializer/StringSerializer.java Sun Oct 27 08:48:07 2013 @@ -92,6 +92,40 @@ public class StringSerializer extends Ab /** + * A method used to deserialize a String from a byte array. + * @param in The byte array containing the String + * @return A String + */ + public String fromBytes( byte[] in ) + { + return deserialize( in, 0 ); + } + + + /** + * A method used to deserialize a String from a byte array. + * @param in The byte array containing the String + * @return A String + */ + public String fromBytes( byte[] in, int start ) + { + int length = IntSerializer.deserialize( in, start ); + + if ( length == 0xFFFFFFFF ) + { + return null; + } + + if ( in.length < length + start ) + { + throw new RuntimeException( "Cannot extract a String from a buffer with not enough bytes" ); + } + + return Strings.utf8ToString( in, start + 4, length ); + } + + + /** * Serialize a String. We store the length on 4 bytes, then the String * * @param buffer the Buffer that will contain the serialized value Modified: directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerFreePageTest.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerFreePageTest.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerFreePageTest.java (original) +++ directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerFreePageTest.java Sun Oct 27 08:48:07 2013 @@ -29,6 +29,7 @@ import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.directory.mavibot.btree.Tuple; +import org.apache.directory.mavibot.btree.TupleCursor; import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException; import org.apache.directory.mavibot.btree.serializer.LongSerializer; import org.apache.directory.mavibot.btree.serializer.StringSerializer; @@ -166,7 +167,7 @@ public class RecordManagerFreePageTest assertTrue( nbElems == btree.getNbElems() ); - CursorImpl cursor = btree.browse(); + TupleCursor cursor = btree.browse(); long i = 0; while ( cursor.hasNext() ) Modified: directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerTest.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerTest.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerTest.java (original) +++ directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/managed/RecordManagerTest.java Sun Oct 27 08:48:07 2013 @@ -35,6 +35,7 @@ import java.util.Set; import java.util.UUID; import org.apache.directory.mavibot.btree.Tuple; +import org.apache.directory.mavibot.btree.ValueCursor; import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException; import org.apache.directory.mavibot.btree.exception.KeyNotFoundException; import org.apache.directory.mavibot.btree.serializer.LongSerializer; @@ -845,28 +846,13 @@ public class RecordManagerTest dupsTree = recordManager1.getManagedTree( name ); - // Cursor cursor1 = dupsTree.browse(); - // while( cursor1.hasNext() ) - // { - // System.out.println( cursor1.next() ); - // } - // cursor1.close(); - for ( long i = 0; i < numKeys; i++ ) { - DuplicateKeyVal dupVal = dupsTree.getValues( i ); - // Cursor cursor = values.browse(); - // while( cursor.hasNext() ) - // { - // System.out.println( cursor.next() ); - // } - // cursor.close(); - - BTree values = dupVal.getSubTree(); + ValueCursor values = dupsTree.getValues( i ); for ( int k = 0; k < pageSize + 1; k++ ) { - assertTrue( values.hasKey( String.valueOf( k ) ) ); + assertTrue( values.next().equals( String.valueOf( k ) ) ); } } } Modified: directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/memory/BTreeBuilderTest.java URL: http://svn.apache.org/viewvc/directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/memory/BTreeBuilderTest.java?rev=1536066&r1=1536065&r2=1536066&view=diff ============================================================================== --- directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/memory/BTreeBuilderTest.java (original) +++ directory/mavibot/trunk/mavibot/src/test/java/org/apache/directory/mavibot/btree/memory/BTreeBuilderTest.java Sun Oct 27 08:48:07 2013 @@ -27,8 +27,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.apache.directory.mavibot.btree.Cursor; import org.apache.directory.mavibot.btree.Tuple; +import org.apache.directory.mavibot.btree.TupleCursor; import org.apache.directory.mavibot.btree.serializer.IntSerializer; import org.junit.Test; @@ -62,8 +62,9 @@ public class BTreeBuilderTest assertEquals( 1, btree.rootPage.findLeftMost().getKey().intValue() ); - Cursor cursor = btree.browse(); + TupleCursor cursor = btree.browse(); int i = 0; + while ( cursor.hasNext() ) { Tuple expected = sortedTuple.get( i++ );