Change how the NIO block read works, to re-use the byte array for the from-InputStream case. Also start on reading the FAT blocks for NPOIFSFileSystem

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1050775 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2010-12-19 08:53:36 +00:00
parent 302df8aa4c
commit aaddcfc655
7 changed files with 145 additions and 78 deletions

View File

@ -184,12 +184,18 @@ public class NPOIFSFileSystem
_header = new HeaderBlock(headerBuffer); _header = new HeaderBlock(headerBuffer);
// We need to buffer the whole file into memory when // We need to buffer the whole file into memory when
// working with an InputStream. Do so now // working with an InputStream.
int maxSize = _header.getBATCount() * // The max possible size is when each BAT block entry is used
int maxSize =
_header.getBATCount() *
_header.getBigBlockSize().getBATEntriesPerBlock() * _header.getBigBlockSize().getBATEntriesPerBlock() *
_header.getBigBlockSize().getBigBlockSize(); _header.getBigBlockSize().getBigBlockSize()
;
ByteBuffer data = ByteBuffer.allocate(maxSize); ByteBuffer data = ByteBuffer.allocate(maxSize);
// Copy in the header
data.put(headerBuffer); data.put(headerBuffer);
data.position(_header.getBigBlockSize().getBigBlockSize());
// Now read the rest of the stream
IOUtils.readFully(channel, data); IOUtils.readFully(channel, data);
success = true; success = true;
@ -260,14 +266,46 @@ public class NPOIFSFileSystem
// Grab the block size // Grab the block size
bigBlockSize = _header.getBigBlockSize(); bigBlockSize = _header.getBigBlockSize();
// Read the properties
// TODO
// Read the FAT blocks // Read the FAT blocks
// TODO for(int fatAT : _header.getBATArray()) {
ByteBuffer fatData = getBlockAt(fatAT);
_blocks.add(BATBlock.createBATBlock(bigBlockSize, fatData));
}
// Now read the XFAT blocks // Now read the XFAT blocks
// TODO Corrupt / Loop checking
BATBlock xfat;
int nextAt = _header.getXBATIndex();
for(int i=0; i<_header.getXBATCount(); i++) {
ByteBuffer fatData = getBlockAt(nextAt);
xfat = BATBlock.createBATBlock(bigBlockSize, fatData);
nextAt = xfat.getValueAt(bigBlockSize.getNextXBATChainOffset());
_blocks.add(xfat);
}
// We're now able to load steams
// Use this to read in the properties
// TODO // TODO
// TODO With loop checking
}
/**
* 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);
}
/**
* Works out what block follows the specified one.
*/
protected int getNextBlock(final int offset) {
// TODO
return -1;
} }
/** /**

View File

@ -34,16 +34,16 @@ public class ByteArrayBackedDataSource extends DataSource {
this(data, data.length); this(data, data.length);
} }
public void read(ByteBuffer dst, long position) { public ByteBuffer read(int length, long position) {
if(position >= size) { if(position >= size) {
throw new IndexOutOfBoundsException( throw new IndexOutOfBoundsException(
"Unable to read " + dst.capacity() + " bytes from " + "Unable to read " + length + " bytes from " +
position + " in stream of length " + size position + " in stream of length " + size
); );
} }
int toRead = (int)Math.min(dst.capacity(), size - position); int toRead = (int)Math.min(length, size - position);
dst.put(buffer, (int)position, toRead); return ByteBuffer.wrap(buffer, (int)position, toRead);
} }
public void write(ByteBuffer src, long position) { public void write(ByteBuffer src, long position) {

View File

@ -24,8 +24,8 @@ import java.nio.ByteBuffer;
* Common definition of how we read and write bytes * Common definition of how we read and write bytes
*/ */
public abstract class DataSource { public abstract class DataSource {
abstract void read(ByteBuffer dst, long position) throws IOException; public abstract ByteBuffer read(int length, long position) throws IOException;
abstract void write(ByteBuffer src, long position) throws IOException; public abstract void write(ByteBuffer src, long position) throws IOException;
abstract long size() throws IOException; public abstract long size() throws IOException;
abstract void close() throws IOException; public abstract void close() throws IOException;
} }

View File

@ -42,17 +42,26 @@ public class FileBackedDataSource extends DataSource {
this.channel = channel; this.channel = channel;
} }
public void read(ByteBuffer dst, long position) throws IOException { public ByteBuffer read(int length, long position) throws IOException {
if(position >= size()) { if(position >= size()) {
throw new IllegalArgumentException("Position " + position + " past the end of the file"); throw new IllegalArgumentException("Position " + position + " past the end of the file");
} }
// Read
channel.position(position); channel.position(position);
ByteBuffer dst = ByteBuffer.allocate(length);
int worked = IOUtils.readFully(channel, dst); int worked = IOUtils.readFully(channel, dst);
// Check
if(worked == -1) { if(worked == -1) {
throw new IllegalArgumentException("Position " + position + " past the end of the file"); throw new IllegalArgumentException("Position " + position + " past the end of the file");
} }
// Ready it for reading
dst.position(0);
// All done
return dst;
} }
public void write(ByteBuffer src, long position) throws IOException { public void write(ByteBuffer src, long position) throws IOException {

View File

@ -56,4 +56,29 @@ public final class TestNPOIFSFileSystem extends TestCase {
} }
} }
public void testPropertiesAndFatOnRead() throws Exception {
NPOIFSFileSystem fsA, fsB;
// With a simple 512 block file
fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize512.zvi"));
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
// Check the FAT was properly processed
// TODO
// Check the properties
// TODO
}
// Now with a simple 4096 block file
fsA = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
fsB = new NPOIFSFileSystem(_inst.openResourceAsStream("BlockSize4096.zvi"));
for(NPOIFSFileSystem fs : new NPOIFSFileSystem[] {fsA,fsB}) {
// Check the FAT was properly processed
// TODO
// Check the properties
// TODO
}
}
} }

View File

@ -20,6 +20,7 @@
package org.apache.poi.poifs.nio; package org.apache.poi.poifs.nio;
import java.io.File; import java.io.File;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
@ -40,20 +41,23 @@ public class TestDataSource extends TestCase
assertEquals(8192, ds.size()); assertEquals(8192, ds.size());
// Start of file // Start of file
ByteBuffer bs = ByteBuffer.allocate(4); ByteBuffer bs;
ds.read(bs, 0); bs = ds.read(4, 0);
assertEquals(4, bs.capacity()); assertEquals(4, bs.capacity());
assertEquals(4, bs.position()); assertEquals(0, bs.position());
assertEquals(0xd0-256, bs.get(0)); assertEquals(0xd0-256, bs.get(0));
assertEquals(0xcf-256, bs.get(1)); assertEquals(0xcf-256, bs.get(1));
assertEquals(0x11-000, bs.get(2)); assertEquals(0x11-000, bs.get(2));
assertEquals(0xe0-256, bs.get(3)); assertEquals(0xe0-256, bs.get(3));
assertEquals(0xd0-256, bs.get());
assertEquals(0xcf-256, bs.get());
assertEquals(0x11-000, bs.get());
assertEquals(0xe0-256, bs.get());
// Mid way through // Mid way through
bs = ByteBuffer.allocate(8); bs = ds.read(8, 0x400);
ds.read(bs, 0x400);
assertEquals(8, bs.capacity()); assertEquals(8, bs.capacity());
assertEquals(8, bs.position()); assertEquals(0, bs.position());
assertEquals((byte)'R', bs.get(0)); assertEquals((byte)'R', bs.get(0));
assertEquals(0, bs.get(1)); assertEquals(0, bs.get(1));
assertEquals((byte)'o', bs.get(2)); assertEquals((byte)'o', bs.get(2));
@ -64,14 +68,12 @@ public class TestDataSource extends TestCase
assertEquals(0, bs.get(7)); assertEquals(0, bs.get(7));
// Can go to the end, but not past it // Can go to the end, but not past it
bs.clear(); bs = ds.read(8, 8190);
ds.read(bs, 8190); assertEquals(0, bs.position()); // TODO How best to warn of a short read?
assertEquals(2, bs.position());
// Can't go off the end // Can't go off the end
try { try {
bs.clear(); bs = ds.read(4, 8192);
ds.read(bs, 8192);
fail("Shouldn't be able to read off the end of the file"); fail("Shouldn't be able to read off the end of the file");
} catch(IllegalArgumentException e) {} } catch(IllegalArgumentException e) {}
} }
@ -87,70 +89,64 @@ public class TestDataSource extends TestCase
ByteArrayBackedDataSource ds = new ByteArrayBackedDataSource(data); ByteArrayBackedDataSource ds = new ByteArrayBackedDataSource(data);
// Start // Start
ByteBuffer bs = ByteBuffer.allocate(4); ByteBuffer bs;
ds.read(bs, 0); bs = ds.read(4, 0);
assertEquals(4, bs.capacity()); assertEquals(0, bs.position());
assertEquals(4, bs.position()); assertEquals(0x00, bs.get());
assertEquals(0x00, bs.get(0)); assertEquals(0x01, bs.get());
assertEquals(0x01, bs.get(1)); assertEquals(0x02, bs.get());
assertEquals(0x02, bs.get(2)); assertEquals(0x03, bs.get());
assertEquals(0x03, bs.get(3));
// Middle // Middle
bs.clear(); bs = ds.read(4, 100);
ds.read(bs, 100); assertEquals(100, bs.position());
assertEquals(4, bs.capacity()); assertEquals(100, bs.get());
assertEquals(4, bs.position()); assertEquals(101, bs.get());
assertEquals(100, bs.get(0)); assertEquals(102, bs.get());
assertEquals(101, bs.get(1)); assertEquals(103, bs.get());
assertEquals(102, bs.get(2));
assertEquals(103, bs.get(3));
// End // End
bs.clear(); bs = ds.read(4, 252);
ds.read(bs, 252); assertEquals(-4, bs.get());
assertEquals(4, bs.capacity()); assertEquals(-3, bs.get());
assertEquals(4, bs.position()); assertEquals(-2, bs.get());
assertEquals(-4, bs.get(0)); assertEquals(-1, bs.get());
assertEquals(-3, bs.get(1));
assertEquals(-2, bs.get(2));
assertEquals(-1, bs.get(3));
// Off the end // Off the end
bs.clear(); bs = ds.read(4, 254);
ds.read(bs, 254); assertEquals(-2, bs.get());
assertEquals(4, bs.capacity()); assertEquals(-1, bs.get());
assertEquals(2, bs.position()); try {
assertEquals(-2, bs.get(0)); bs.get();
assertEquals(-1, bs.get(1)); fail("Shouldn't be able to read off the end");
} catch(BufferUnderflowException e) {}
// Past the end // Past the end
bs.clear();
try { try {
ds.read(bs, 256); bs = ds.read(4, 256);
fail("Shouldn't be able to read off the end"); fail("Shouldn't be able to read off the end");
} catch(IndexOutOfBoundsException e) {} } catch(IndexOutOfBoundsException e) {}
// Overwrite // Overwrite
bs.clear(); bs = ByteBuffer.allocate(4);
bs.put(0, (byte)-55); bs.put(0, (byte)-55);
bs.put(1, (byte)-54); bs.put(1, (byte)-54);
bs.put(2, (byte)-53); bs.put(2, (byte)-53);
bs.put(3, (byte)-52); bs.put(3, (byte)-52);
assertEquals(256, ds.size());
ds.write(bs, 40); ds.write(bs, 40);
bs.clear(); assertEquals(256, ds.size());
ds.read(bs, 40); bs = ds.read(4, 40);
assertEquals(4, bs.position()); assertEquals(-55, bs.get());
assertEquals(-55, bs.get(0)); assertEquals(-54, bs.get());
assertEquals(-54, bs.get(1)); assertEquals(-53, bs.get());
assertEquals(-53, bs.get(2)); assertEquals(-52, bs.get());
assertEquals(-52, bs.get(3));
// Append // Append
bs.clear(); bs = ByteBuffer.allocate(4);
bs.put(0, (byte)-55); bs.put(0, (byte)-55);
bs.put(1, (byte)-54); bs.put(1, (byte)-54);
bs.put(2, (byte)-53); bs.put(2, (byte)-53);
@ -160,12 +156,11 @@ public class TestDataSource extends TestCase
ds.write(bs, 256); ds.write(bs, 256);
assertEquals(260, ds.size()); assertEquals(260, ds.size());
bs.clear(); bs = ds.read(4, 256);
ds.read(bs, 256); assertEquals(256, bs.position());
assertEquals(4, bs.position()); assertEquals(-55, bs.get());
assertEquals(-55, bs.get(0)); assertEquals(-54, bs.get());
assertEquals(-54, bs.get(1)); assertEquals(-53, bs.get());
assertEquals(-53, bs.get(2)); assertEquals(-52, bs.get());
assertEquals(-52, bs.get(3));
} }
} }