You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2010/12/23 09:02:50 UTC

svn commit: r1052194 - in /poi/trunk/src: java/org/apache/poi/poifs/filesystem/ java/org/apache/poi/poifs/storage/ testcases/org/apache/poi/poifs/filesystem/

Author: nick
Date: Thu Dec 23 08:02:50 2010
New Revision: 1052194

URL: http://svn.apache.org/viewvc?rev=1052194&view=rev
Log:
Add NPOIFSFileSystem support for identifying free blocks, along with partial unit tests for it

Modified:
    poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java
    poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java
    poi/trunk/src/java/org/apache/poi/poifs/storage/BATBlock.java
    poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java
    poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java

Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java?rev=1052194&r1=1052193&r2=1052194&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java Thu Dec 23 08:02:50 2010
@@ -272,7 +272,9 @@ public class NPOIFSFileSystem
        for(int fatAt : _header.getBATArray()) {
           loopDetector.claim(fatAt);
           ByteBuffer fatData = getBlockAt(fatAt);
-          _bat_blocks.add(BATBlock.createBATBlock(bigBlockSize, fatData));
+          BATBlock bat = BATBlock.createBATBlock(bigBlockSize, fatData);
+          bat.setOurBlockIndex(fatAt);
+          _bat_blocks.add(bat);
        }
        
        // Now read the XFAT blocks
@@ -282,6 +284,7 @@ public class NPOIFSFileSystem
           loopDetector.claim(nextAt);
           ByteBuffer fatData = getBlockAt(nextAt);
           xfat = BATBlock.createBATBlock(bigBlockSize, fatData);
+          xfat.setOurBlockIndex(nextAt);
           nextAt = xfat.getValueAt(bigBlockSize.getNextXBATChainOffset());
           
           _bat_blocks.add(xfat);
@@ -296,8 +299,6 @@ public class NPOIFSFileSystem
      * Load the block at the given offset.
      */
     protected ByteBuffer getBlockAt(final int offset) throws IOException {
-       ByteBuffer data = ByteBuffer.allocate(bigBlockSize.getBigBlockSize());
-       
        // The header block doesn't count, so add one
        long startAt = (offset+1) * bigBlockSize.getBigBlockSize();
        return _data.read(bigBlockSize.getBigBlockSize(), startAt);
@@ -336,9 +337,79 @@ public class NPOIFSFileSystem
      * This method will extend the file if needed, and if doing
      *  so, allocate new FAT blocks to address the extra space.
      */
-    protected int getFreeBlock() {
-       // TODO
-       return -1;
+    protected int getFreeBlock() throws IOException {
+       // First up, do we have any spare ones?
+       int offset = 0;
+       for(int i=0; i<_bat_blocks.size(); i++) {
+          boolean isXBAT = (i >= _header.getBATCount());
+          
+          int numSectors = bigBlockSize.getBATEntriesPerBlock();
+          if(isXBAT) {
+             numSectors = bigBlockSize.getXBATEntriesPerBlock();
+          }
+
+          // Check this one
+          BATBlock bat = _bat_blocks.get(i);
+          if(bat.hasFreeSectors()) {
+             // Claim one of them and return it
+             for(int j=0; j<numSectors; j++) {
+                int batValue = bat.getValueAt(j);
+                if(batValue == POIFSConstants.UNUSED_BLOCK) {
+                   // Bingo
+                   return offset + j;
+                }
+             }
+          }
+          
+          // Move onto the next BAT/XBAT
+          offset += numSectors;
+       }
+       
+       // If we get here, then there aren't any
+       //  free sectors in any of the BATs or XBATs
+       // So, we need to extend the file and add another
+       boolean isBAT = true;
+       if(_header.getBATCount() >= 109) {
+          isBAT = false;
+       }
+       
+       // Create a new BATBlock
+       BATBlock newBAT = BATBlock.createEmptyBATBlock(bigBlockSize, !isBAT);
+       newBAT.setOurBlockIndex(offset);
+       // Ensure there's a spot in the file for it
+       ByteBuffer buffer = ByteBuffer.allocate(bigBlockSize.getBigBlockSize());
+       int writeTo = (1+offset) * bigBlockSize.getBigBlockSize(); // Header isn't in BATs
+       _data.write(buffer, writeTo);
+       
+       // Allocate ourself within ourselves, at the first point
+       if(isBAT) {
+          newBAT.setValueAt(0, POIFSConstants.FAT_SECTOR_BLOCK);
+       } else {
+          newBAT.setValueAt(0, POIFSConstants.DIFAT_SECTOR_BLOCK);
+       }
+
+       // Store us
+       _bat_blocks.add(newBAT);
+       if(isBAT) {
+          // Put it in the BAT array in the header
+          int[] newBATs = new int[_header.getBATCount()+1];
+          System.arraycopy(_header.getBATArray(), 0, newBATs, 0, newBATs.length-1);
+          newBATs[newBATs.length-1] = offset;
+          _header.setBATArray(newBATs);
+          _header.setBATCount(newBATs.length);
+       } else if(_header.getXBATCount() == 0) {
+          // Store our first XBAT offset in the header
+          _header.setXBATStart(offset);
+          _header.setXBATCount(1);
+       } else {
+          // Chain it off the last XBAT
+          BATBlock lastXBAT = _bat_blocks.get(_bat_blocks.size()-1);
+          lastXBAT.setValueAt(bigBlockSize.getNextXBATChainOffset(), offset);
+          _header.setXBATCount(_header.getXBATCount()+1);
+       }
+       
+       // The first offset stores us, but the 2nd is free
+       return offset+1;
     }
 
     /**

Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java?rev=1052194&r1=1052193&r2=1052194&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/NPOIFSStream.java Thu Dec 23 08:02:50 2010
@@ -40,7 +40,7 @@ import org.apache.poi.poifs.storage.Head
  *  handle small block ones.
  * This uses the new NIO code
  * 
- * TODO Add loop checking on read and on write
+ * TODO Implement a streaming write method
  */
 
 public class NPOIFSStream implements Iterable<ByteBuffer>
@@ -137,13 +137,18 @@ public class NPOIFSStream implements Ite
       }
       
       // If we're overwriting, free any remaining blocks
-      // TODO
+      while(nextBlock != POIFSConstants.END_OF_CHAIN) {
+         int thisBlock = nextBlock;
+         loopDetector.claim(thisBlock);
+         nextBlock = filesystem.getNextBlock(thisBlock);
+         filesystem.setNextBlock(thisBlock, POIFSConstants.UNUSED_BLOCK);
+      }
       
       // Mark the end of the stream
       filesystem.setNextBlock(nextBlock, POIFSConstants.END_OF_CHAIN);
    }
    
-   // TODO Streaming write too
+   // TODO Streaming write support too
    
    /**
     * Class that handles a streaming read of one stream

Modified: poi/trunk/src/java/org/apache/poi/poifs/storage/BATBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/storage/BATBlock.java?rev=1052194&r1=1052193&r2=1052194&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/storage/BATBlock.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/storage/BATBlock.java Thu Dec 23 08:02:50 2010
@@ -48,9 +48,13 @@ public final class BATBlock extends BigB
     private boolean _has_free_sectors;
     
     /**
+     * Where in the file are we?
+     */
+    private int ourBlockIndex;
+    
+    /**
      * Create a single instance initialized with default values
      */
-
     private BATBlock(POIFSBigBlockSize bigBlockSize)
     {
         super(bigBlockSize);
@@ -118,6 +122,17 @@ public final class BATBlock extends BigB
        // All done
        return block;
     }
+    
+    /**
+     * Creates a single BATBlock, with all the values set to empty.
+     */
+    public static BATBlock createEmptyBATBlock(final POIFSBigBlockSize bigBlockSize, boolean isXBAT) {
+       BATBlock block = new BATBlock(bigBlockSize);
+       if(isXBAT) {
+          block.setXBATChain(bigBlockSize, POIFSConstants.END_OF_CHAIN);
+       }
+       return block;
+    }
 
     /**
      * Create an array of BATBlocks from an array of int block
@@ -295,10 +310,24 @@ public final class BATBlock extends BigB
           recomputeFree();
        }
     }
+    
+    /**
+     * Record where in the file we live
+     */
+    public void setOurBlockIndex(int index) {
+       this.ourBlockIndex = index;
+    }
+    /**
+     * Retrieve where in the file we live 
+     */
+    public int getOurBlockIndex() {
+       return ourBlockIndex;
+    }
+
 
     /* ********** START extension of BigBlock ********** */
 
-    /**
+   /**
      * Write the block's data to an OutputStream
      *
      * @param stream the OutputStream to which the stored data should

Modified: poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java?rev=1052194&r1=1052193&r2=1052194&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java Thu Dec 23 08:02:50 2010
@@ -146,4 +146,84 @@ public final class TestNPOIFSFileSystem 
       assertEquals((byte)0x00, b.get());
       assertEquals((byte)0x00, b.get());
    }
+   
+   /**
+    * Ask for free blocks where there are some already
+    *  to be had from the FAT
+    */
+   public void testGetFreeBlockWithSpare() throws Exception {
+      NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
+      
+      // Our first BAT block has spares
+      assertEquals(true, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
+      
+      // First free one is 100
+      assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(100));
+      assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(101));
+      assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(102));
+      assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(103));
+      
+      // Ask, will get 100
+      assertEquals(100, fs.getFreeBlock());
+      
+      // Ask again, will still get 100 as not written to
+      assertEquals(100, fs.getFreeBlock());
+      
+      // Allocate it, then ask again
+      fs.setNextBlock(100, POIFSConstants.END_OF_CHAIN);
+      assertEquals(101, fs.getFreeBlock());
+   }
+
+   /**
+    * Ask for free blocks where no free ones exist, and so the
+    *  file needs to be extended and another BAT/XBAT added
+    */
+   public void testGetFreeBlockWithNoneSpare() throws Exception {
+      NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
+      
+      // We've spare ones from 100 to 128
+      for(int i=100; i<128; i++) {
+         assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(i));
+      }
+      
+      // Check our BAT knows it's free
+      assertEquals(true, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
+      
+      // Allocate all the spare ones
+      for(int i=100; i<128; i++) {
+         fs.setNextBlock(i, POIFSConstants.END_OF_CHAIN);
+      }
+      
+      // BAT is now full, but there's only the one
+      assertEquals(false, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
+      try {
+         assertEquals(false, fs.getBATBlockAndIndex(128).getBlock().hasFreeSectors());
+         fail("Should only be one BAT");
+      } catch(IndexOutOfBoundsException e) {}
+      
+      // Now ask for a free one, will need to extend the file
+      assertEquals(129, fs.getFreeBlock());
+      
+      assertEquals(false, fs.getBATBlockAndIndex(0).getBlock().hasFreeSectors());
+      assertEquals(true, fs.getBATBlockAndIndex(128).getBlock().hasFreeSectors());
+      assertEquals(POIFSConstants.FAT_SECTOR_BLOCK, fs.getNextBlock(128));
+      assertEquals(POIFSConstants.UNUSED_BLOCK, fs.getNextBlock(129));
+      
+      
+      // Fill up to hold 109 BAT blocks
+      // TODO
+      
+      // Ask for another, will get our first XBAT
+      // TODO
+      
+      // Fill the XBAT
+      // TODO
+      
+      // Ask for another, will get our 2nd XBAT
+      // TODO
+      
+      // Write it out and read it back in again
+      // Ensure it's correct
+      // TODO
+   }
 }

Modified: poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java?rev=1052194&r1=1052193&r2=1052194&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSStream.java Thu Dec 23 08:02:50 2010
@@ -26,6 +26,8 @@ import org.apache.poi.POIDataSamples;
 
 /**
  * Tests {@link NPOIFSStream}
+ * 
+ * TODO Write unit tests
  */
 public final class TestNPOIFSStream extends TestCase {
    private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance();
@@ -211,7 +213,38 @@ public final class TestNPOIFSStream exte
     * Craft a nasty file with a loop, and ensure we don't get stuck
     */
    public void testReadFailsOnLoop() throws Exception {
-      // TODO
+      NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
+      
+      // Hack the FAT so that it goes 0->1->2->0
+      fs.setNextBlock(0, 1);
+      fs.setNextBlock(1, 2);
+      fs.setNextBlock(2, 0);
+      
+      // Now try to read
+      NPOIFSStream stream = new NPOIFSStream(fs, 0);
+      Iterator<ByteBuffer> i = stream.getBlockIterator();
+      assertEquals(true, i.hasNext());
+      
+      // 1st read works
+      i.next();
+      assertEquals(true, i.hasNext());
+      
+      // 2nd read works
+      i.next();
+      assertEquals(true, i.hasNext());
+      
+      // 3rd read works
+      i.next();
+      assertEquals(true, i.hasNext());
+      
+      // 4th read blows up as it loops back to 0
+      try {
+         i.next();
+         fail("Loop should have been detected but wasn't!");
+      } catch(RuntimeException e) {
+         // Good, it was detected
+      }
+      assertEquals(true, i.hasNext());
    }
 
    /**



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org