Author: elecharny
Date: Mon Mar  4 14:33:10 2013
New Revision: 1452329

URL: http://svn.apache.org/r1452329
Log:
Partially working file. I commit it so that it gets visible to others.

Modified:
    
labs/mavibot/trunk/mavibot/src/main/java/org/apache/mavibot/btree/store/RecordManager.java

Modified: 
labs/mavibot/trunk/mavibot/src/main/java/org/apache/mavibot/btree/store/RecordManager.java
URL: 
http://svn.apache.org/viewvc/labs/mavibot/trunk/mavibot/src/main/java/org/apache/mavibot/btree/store/RecordManager.java?rev=1452329&r1=1452328&r2=1452329&view=diff
==============================================================================
--- 
labs/mavibot/trunk/mavibot/src/main/java/org/apache/mavibot/btree/store/RecordManager.java
 (original)
+++ 
labs/mavibot/trunk/mavibot/src/main/java/org/apache/mavibot/btree/store/RecordManager.java
 Mon Mar  4 14:33:10 2013
@@ -25,12 +25,15 @@ import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.mavibot.btree.BTree;
 import org.apache.mavibot.btree.exception.BTreeAlreadyManagedException;
+import org.apache.mavibot.btree.exception.EndOfFileExceededException;
 import org.apache.mavibot.btree.serializer.IntSerializer;
 import org.apache.mavibot.btree.serializer.LongArraySerializer;
+import org.apache.mavibot.btree.serializer.LongSerializer;
 import org.apache.mavinot.btree.utils.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -94,6 +97,9 @@ public class RecordManager
     private static final int HEADER_SIZE = NB_TREE_SIZE + PAGE_SIZE + 
FIRST_FREE_PAGE_SIZE + LAST_FREE_PAGE_SIZE;
     private static final ByteBuffer HEADER_BUFFER = ByteBuffer.allocate( 
HEADER_SIZE );
 
+    /** A Page used to flush data on disk */
+    private final ByteBuffer PAGE_BUFFER;
+
     /** The default page size */
     private static final int DEFAULT_PAGE_SIZE = 4 * 1024;
 
@@ -119,7 +125,24 @@ public class RecordManager
      */
     public RecordManager( String fileName )
     {
+        this( fileName, DEFAULT_PAGE_SIZE );
+    }
+
+
+    /**
+     * Create a Record manager which will either create the underlying file
+     * or load an existing one. If a folder is provider, then we will create
+     * a file with a default name : mavibot.db
+     * 
+     * @param name The file name, or a folder name
+     * @param pageSize the size of a page on disk
+     */
+    public RecordManager( String fileName, int pageSize )
+    {
         this.fileName = fileName;
+        this.pageSize = pageSize;
+
+        PAGE_BUFFER = ByteBuffer.allocateDirect( pageSize );
 
         // Open the file or create it
         File tmpFile = new File( fileName );
@@ -149,7 +172,7 @@ public class RecordManager
         else
         {
             // It's a file. Let's see if it exists, otherwise create it
-            if ( !tmpFile.exists() )
+            if ( !tmpFile.exists() || ( tmpFile.length() == 0 ) )
             {
                 isNewFile = true;
 
@@ -208,12 +231,14 @@ public class RecordManager
         HEADER_BUFFER.putLong( NO_PAGE );
         lastFreePage = NO_PAGE;
 
-        // Now, initialize the Discarded Page BTree
+        // Now, initialize the Discarded Page BTree, which is a in-memory BTree
         copiedPageBTree = new BTree<Integer, long[]>( "copiedPageBTree", new 
IntSerializer(), new LongArraySerializer() );
 
         // Inject this BTree into the RecordManager
         try
         {
+            managedBTrees = new HashMap<String, BTree<?, ?>>();
+
             manage( copiedPageBTree );
         }
         catch ( BTreeAlreadyManagedException btame )
@@ -284,16 +309,172 @@ public class RecordManager
         String valueSerializerFqcn = 
btree.getKeySerializer().getClass().getName();
         byte[] valueSerializerBytes = Strings.getBytesUtf8( 
valueSerializerFqcn );
 
-        int bufferSize = btreeNameBytes.length + keySerializerBytes.length + 
valueSerializerBytes.length;
+        int bufferSize = btreeNameBytes.length + keySerializerBytes.length + 
valueSerializerBytes.length + 12;
 
         // Get the pageIOs we need to store the data. We may need more than 
one.
-        PageIO[] pageIos = fetchPageIOs( bufferSize );
+        PageIO[] pageIos = getFreePageIOs( bufferSize );
 
-        // Now store the data in the pages.
-        //storeData( pageIos, btreeNameBytes, keySerializerBytes, 
valueSerializerBytes );
+        // Now store the BTree data in the pages :
+        // - the BTree name
+        // - the keySerializer FQCN
+        // - the valueSerializer FQCN
+        // - the BTree page size
+        // - the BTree revision
+        // - the BTree number of elements
+        // - The RootPage offset
+        PageIO rootPageIo = fetchNewPage();
+
+        long position = storeBytes( pageIos, 0, btreeNameBytes );              
  // The tree name
+            btreeNameBytes,
+            IntSerializer.serialize( keySerializerBytes.length ), // The 
keySerializer name
+            keySerializerBytes,
+            IntSerializer.serialize( valueSerializerBytes.length ), // The 
valueSerialier name
+            valueSerializerBytes,
+            IntSerializer.serialize( btree.getPageSize() ), // The BTree page 
size
+            LongSerializer.serialize( btree.getRevision() ), // The BTree 
current revision
+            LongSerializer.serialize( btree.getNbElems() ), // The nb elems in 
the tree
+            LongSerializer.serialize( rootPageIo.getOffset() ) );
 
         // And flush the pages to disk now
-        //flushPages( pageIos );
+        flushPages( pageIos );
+        flushPages( rootPageIo );
+    }
+
+
+    private void flushPages( PageIO... pageIos ) throws IOException
+    {
+        for ( PageIO pageIo : pageIos )
+        {
+            PAGE_BUFFER.put( pageIo.getData() );
+            PAGE_BUFFER.position( 0 );
+
+            if ( fileChannel.size() <= ( pageIo.getOffset() + pageSize ) )
+            {
+                fileChannel.write( PAGE_BUFFER, pageIo.getOffset() );
+            }
+            else
+            {
+                // This is a page we have to add to the file
+                fileChannel.write( PAGE_BUFFER, fileChannel.size() );
+            }
+        }
+    }
+
+
+    /**
+     * Compute the page in which we will store data given an offset, when 
+     * we have a list of pages.
+     * 
+     * @param offset The position in the data
+     * @return The page number in which the offset will start
+     */
+    private int computePageNb( long offset )
+    {
+        long pageNb = 0;
+
+        offset -= pageSize - LINK_SIZE - PAGE_SIZE;
+
+        if ( offset < 0 )
+        {
+            return ( int ) pageNb;
+        }
+
+        pageNb = 1 + offset / ( pageSize - LINK_SIZE );
+
+        return ( int ) pageNb;
+    }
+
+
+    private long storeBytes( PageIO[] pageIos, long position, byte[] bytes )
+    {
+        if ( bytes != null )
+        {
+            int pageNb = computePageNb( position );
+            int currentNb = 0;
+
+            ByteBuffer pageData = pageIos[currentNb].getData();
+            int currentPos = LINK_SIZE + DATA_SIZE;
+
+            int remaining = pageData.capacity() - currentPos;
+
+            // First write the bytes length
+            if ( remaining < 4 )
+            {
+                // We  copy the serialized length on two ages
+                byte[] lengthBytes = IntSerializer.serialize( bytes.length );
+                pageData.put( lengthBytes, currentPos, remaining );
+                currentNb++;
+                pageData = pageIos[pageNb].getData();
+                currentPos = LINK_SIZE;
+                pageData.put( lengthBytes, currentPos, 4 - remaining );
+                currentPos += 4 - remaining;
+            }
+            else
+            {
+                // Store the bytes length first
+                pageData.putInt( currentPos, bytes.length );
+                currentPos += 4;
+            }
+
+            // Now deal with the bytes themselves
+            if ( bytes.length > remaining )
+            {
+                int bytesWritten = 0;
+
+                while ( bytesWritten < bytes.length )
+                {
+                    System.arraycopy( bytes, 0, pageData, currentPos, 
remaining );
+                    currentPos = LINK_SIZE;
+                    pageNb++;
+                    pageData = pageIos[pageNb].getData();
+                    bytesWritten += remaining;
+                    remaining = pageData.capacity() - LINK_SIZE;
+                }
+            }
+            else
+            {
+                System.arraycopy( bytes, 0, pageData, currentPos, bytes.length 
);
+                currentPos += bytes.length;
+            }
+        }
+
+        return position;
+    }
+
+
+    private void storeData1( PageIO[] pageIos, ByteBuffer... byteArrays )
+    {
+        if ( byteArrays != null )
+        {
+            int pageNb = 0;
+            ByteBuffer pageData = pageIos[0].getData();
+            int currentPos = LINK_SIZE + DATA_SIZE;
+
+            for ( byte[] bytes : byteArrays )
+            {
+                int remaining = pageData.capacity() - currentPos;
+
+                if ( bytes.length > remaining )
+                {
+                    int bytesWritten = 0;
+
+                    while ( bytesWritten < bytes.length )
+                    {
+                        System.arraycopy( bytes, 0, pageData, currentPos, 
remaining );
+                        currentPos = LINK_SIZE;
+                        pageNb++;
+                        pageData = pageIos[pageNb].getData();
+                        bytesWritten += remaining;
+                        remaining = pageData.capacity() - LINK_SIZE;
+                    }
+                }
+                else
+                {
+                    System.arraycopy( bytes, 0, pageData, currentPos, 
bytes.length );
+                    currentPos += bytes.length;
+                }
+            }
+        }
     }
 
 
@@ -303,7 +484,7 @@ public class RecordManager
      * @param dataSize The data size
      * @return An array of pages, enough to store the full data
      */
-    private PageIO[] fetchPageIOs( int dataSize ) throws IOException
+    private PageIO[] getFreePageIOs( int dataSize ) throws IOException
     {
         // Compute the number of pages needed.
         // Considering that each page coan contain PageSize bytes,
@@ -329,9 +510,21 @@ public class RecordManager
 
         PageIO[] pageIOs = new PageIO[nbNeededPages];
 
-        for ( int i = 0; i < nbNeededPages; i++ )
+        // The first page : set the size
+        pageIOs[0] = fetchNewPage();
+        pageIOs[0].setSize( dataSize );
+        long offset = pageIOs[0].getOffset() + pageSize;
+
+        for ( int i = 1; i < nbNeededPages; i++ )
         {
             pageIOs[i] = fetchNewPage();
+
+            // Create the link
+            pageIOs[i - 1].setNextPage( pageIOs[i].getOffset() );
+
+            // Update the offset
+            pageIOs[i].setOffset( offset );
+            offset += pageSize;
         }
 
         return pageIOs;
@@ -339,10 +532,12 @@ public class RecordManager
 
 
     /**
-     * Return a new Page 
-     * @return
+     * Return a new Page. We take one of the existing free page, or we create
+     * a new page at the end of the file.
+     * 
+     * @return The fetched PageIO
      */
-    private PageIO fetchNewPage() throws IOException
+    private synchronized PageIO fetchNewPage() throws IOException
     {
         if ( firstFreePage == NO_PAGE )
         {
@@ -351,18 +546,54 @@ public class RecordManager
             long offset = fileChannel.size();
             PageIO newPage = new PageIO( offset );
 
-            byte[] data = new byte[pageSize];
+            ByteBuffer data = ByteBuffer.allocateDirect( pageSize );
 
             newPage.setData( data );
             newPage.setNextPage( NO_PAGE );
             newPage.setSize( -1 );
+
+            return newPage;
         }
         else
         {
             // We have some existing free page. Fetch one from there.
+            PageIO pageIo = fetchPage( firstFreePage );
+
+            // Point to the next free page
+            firstFreePage = pageIo.getNextPage();
+
+            return pageIo;
         }
+    }
+
 
-        return null;
+    /**
+     * fetch a page from disk, knowing its position in the file.
+     * 
+     * @param offset The position in the file
+     * @return The found page
+     */
+    private PageIO fetchPage( long offset ) throws IOException, 
EndOfFileExceededException
+    {
+        if ( fileChannel.size() <= offset + pageSize )
+        {
+            // Error : we are past the end of the file
+            throw new EndOfFileExceededException( "We are fetching a page on " 
+ offset +
+                " when the file's size is " + fileChannel.size() );
+        }
+        else
+        {
+            // Read the page
+            fileChannel.position( offset );
+
+            ByteBuffer data = ByteBuffer.allocate( pageSize );
+            fileChannel.read( data );
+
+            PageIO readPage = new PageIO( offset );
+            readPage.setData( data );
+
+            return readPage;
+        }
     }
 
 



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to