More NPOIFSFileSystem and NPOIFSStream read unit tests, along with details of a few more tests still to be written
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1052186 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
39f533b4d0
commit
e911c35421
@ -653,12 +653,21 @@ public class NPOIFSFileSystem
|
|||||||
* we can bail out with an error rather than
|
* we can bail out with an error rather than
|
||||||
* spinning away for ever...
|
* spinning away for ever...
|
||||||
*/
|
*/
|
||||||
private class ChainLoopDetector {
|
protected class ChainLoopDetector {
|
||||||
private boolean[] used_blocks;
|
private boolean[] used_blocks;
|
||||||
private ChainLoopDetector() throws IOException {
|
protected ChainLoopDetector() throws IOException {
|
||||||
used_blocks = new boolean[(int)(_data.size()/bigBlockSize.getBigBlockSize())];
|
int numBlocks = (int)Math.ceil(_data.size()/bigBlockSize.getBigBlockSize());
|
||||||
|
used_blocks = new boolean[numBlocks];
|
||||||
}
|
}
|
||||||
private void claim(int offset) {
|
protected void claim(int offset) {
|
||||||
|
if(offset >= used_blocks.length) {
|
||||||
|
// They're writing, and have had new blocks requested
|
||||||
|
// for the write to proceed. That means they're into
|
||||||
|
// blocks we've allocated for them, so are safe
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claiming an existing block, ensure there's no loop
|
||||||
if(used_blocks[offset]) {
|
if(used_blocks[offset]) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Potential loop detected - Block " + offset +
|
"Potential loop detected - Block " + offset +
|
||||||
|
@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.apache.poi.poifs.common.POIFSConstants;
|
import org.apache.poi.poifs.common.POIFSConstants;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem.ChainLoopDetector;
|
||||||
import org.apache.poi.poifs.property.Property;
|
import org.apache.poi.poifs.property.Property;
|
||||||
import org.apache.poi.poifs.storage.HeaderBlock;
|
import org.apache.poi.poifs.storage.HeaderBlock;
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ import org.apache.poi.poifs.storage.HeaderBlock;
|
|||||||
* This only works on big block streams, it doesn't
|
* This only works on big block streams, it doesn't
|
||||||
* handle small block ones.
|
* handle small block ones.
|
||||||
* This uses the new NIO code
|
* This uses the new NIO code
|
||||||
|
*
|
||||||
|
* TODO Add loop checking on read and on write
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class NPOIFSStream implements Iterable<ByteBuffer>
|
public class NPOIFSStream implements Iterable<ByteBuffer>
|
||||||
@ -100,11 +103,16 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
// How many blocks are we going to need?
|
// How many blocks are we going to need?
|
||||||
int blocks = (int)Math.ceil(contents.length / filesystem.getBigBlockSize());
|
int blocks = (int)Math.ceil(contents.length / filesystem.getBigBlockSize());
|
||||||
|
|
||||||
|
// Make sure we don't encounter a loop whilst overwriting
|
||||||
|
// the existing blocks
|
||||||
|
ChainLoopDetector loopDetector = filesystem.new ChainLoopDetector();
|
||||||
|
|
||||||
// Start writing
|
// Start writing
|
||||||
int prevBlock = POIFSConstants.END_OF_CHAIN;
|
int prevBlock = POIFSConstants.END_OF_CHAIN;
|
||||||
int nextBlock = startBlock;
|
int nextBlock = startBlock;
|
||||||
for(int i=0; i<blocks; i++) {
|
for(int i=0; i<blocks; i++) {
|
||||||
int thisBlock = nextBlock;
|
int thisBlock = nextBlock;
|
||||||
|
loopDetector.claim(thisBlock);
|
||||||
|
|
||||||
// Allocate a block if needed, otherwise figure
|
// Allocate a block if needed, otherwise figure
|
||||||
// out what the next block will be
|
// out what the next block will be
|
||||||
@ -128,6 +136,9 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
prevBlock = thisBlock;
|
prevBlock = thisBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're overwriting, free any remaining blocks
|
||||||
|
// TODO
|
||||||
|
|
||||||
// Mark the end of the stream
|
// Mark the end of the stream
|
||||||
filesystem.setNextBlock(nextBlock, POIFSConstants.END_OF_CHAIN);
|
filesystem.setNextBlock(nextBlock, POIFSConstants.END_OF_CHAIN);
|
||||||
}
|
}
|
||||||
@ -138,9 +149,16 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
* Class that handles a streaming read of one stream
|
* Class that handles a streaming read of one stream
|
||||||
*/
|
*/
|
||||||
protected class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
|
protected class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
|
||||||
|
private ChainLoopDetector loopDetector;
|
||||||
private int nextBlock;
|
private int nextBlock;
|
||||||
|
|
||||||
protected StreamBlockByteBufferIterator(int firstBlock) {
|
protected StreamBlockByteBufferIterator(int firstBlock) {
|
||||||
nextBlock = firstBlock;
|
this.nextBlock = firstBlock;
|
||||||
|
try {
|
||||||
|
this.loopDetector = filesystem.new ChainLoopDetector();
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
@ -156,6 +174,7 @@ public class NPOIFSStream implements Iterable<ByteBuffer>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
loopDetector.claim(nextBlock);
|
||||||
ByteBuffer data = filesystem.getBlockAt(nextBlock);
|
ByteBuffer data = filesystem.getBlockAt(nextBlock);
|
||||||
nextBlock = filesystem.getNextBlock(nextBlock);
|
nextBlock = filesystem.getNextBlock(nextBlock);
|
||||||
return data;
|
return data;
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
@ -116,6 +118,32 @@ public final class TestNPOIFSFileSystem extends TestCase {
|
|||||||
* Check we get the right data back for each block
|
* Check we get the right data back for each block
|
||||||
*/
|
*/
|
||||||
public void testGetBlock() throws Exception {
|
public void testGetBlock() throws Exception {
|
||||||
// TODO
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
ByteBuffer b;
|
||||||
|
|
||||||
|
// The 0th block is the first data block
|
||||||
|
b = fs.getBlockAt(0);
|
||||||
|
assertEquals((byte)0x9e, b.get());
|
||||||
|
assertEquals((byte)0x75, b.get());
|
||||||
|
assertEquals((byte)0x97, b.get());
|
||||||
|
assertEquals((byte)0xf6, b.get());
|
||||||
|
|
||||||
|
// And the next block
|
||||||
|
b = fs.getBlockAt(1);
|
||||||
|
assertEquals((byte)0x86, b.get());
|
||||||
|
assertEquals((byte)0x09, b.get());
|
||||||
|
assertEquals((byte)0x22, b.get());
|
||||||
|
assertEquals((byte)0xfb, b.get());
|
||||||
|
|
||||||
|
// Check the final block too
|
||||||
|
b = fs.getBlockAt(99);
|
||||||
|
assertEquals((byte)0x01, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x02, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,281 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.poi.POIDataSamples;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link NPOIFSStream}
|
||||||
|
*/
|
||||||
|
public final class TestNPOIFSStream extends TestCase {
|
||||||
|
private static final POIDataSamples _inst = POIDataSamples.getPOIFSInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a single block stream
|
||||||
|
*/
|
||||||
|
public void testReadTinyStream() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// 98 is actually the last block in a two block stream...
|
||||||
|
NPOIFSStream stream = new NPOIFSStream(fs, 98);
|
||||||
|
Iterator<ByteBuffer> i = stream.getBlockIterator();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b = i.next();
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
|
||||||
|
// Check the contents
|
||||||
|
assertEquals((byte)0x81, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x82, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
assertEquals((byte)0x00, b.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a stream with only two blocks in it
|
||||||
|
*/
|
||||||
|
public void testReadShortStream() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// 97 -> 98 -> end
|
||||||
|
NPOIFSStream stream = new NPOIFSStream(fs, 97);
|
||||||
|
Iterator<ByteBuffer> i = stream.getBlockIterator();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b97 = i.next();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b98 = i.next();
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
|
||||||
|
// Check the contents of the 1st block
|
||||||
|
assertEquals((byte)0x01, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
assertEquals((byte)0x02, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
assertEquals((byte)0x00, b97.get());
|
||||||
|
|
||||||
|
// Check the contents of the 2nd block
|
||||||
|
assertEquals((byte)0x81, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
assertEquals((byte)0x82, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
assertEquals((byte)0x00, b98.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a stream with many blocks
|
||||||
|
*/
|
||||||
|
public void testReadLongerStream() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
ByteBuffer b0 = null;
|
||||||
|
ByteBuffer b1 = null;
|
||||||
|
ByteBuffer b22 = null;
|
||||||
|
|
||||||
|
// The stream at 0 has 23 blocks in it
|
||||||
|
NPOIFSStream stream = new NPOIFSStream(fs, 0);
|
||||||
|
Iterator<ByteBuffer> i = stream.getBlockIterator();
|
||||||
|
int count = 0;
|
||||||
|
while(i.hasNext()) {
|
||||||
|
ByteBuffer b = i.next();
|
||||||
|
if(count == 0) {
|
||||||
|
b0 = b;
|
||||||
|
}
|
||||||
|
if(count == 1) {
|
||||||
|
b1 = b;
|
||||||
|
}
|
||||||
|
if(count == 22) {
|
||||||
|
b22 = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assertEquals(23, count);
|
||||||
|
|
||||||
|
// Check the contents
|
||||||
|
// 1st block is at 0
|
||||||
|
assertEquals((byte)0x9e, b0.get());
|
||||||
|
assertEquals((byte)0x75, b0.get());
|
||||||
|
assertEquals((byte)0x97, b0.get());
|
||||||
|
assertEquals((byte)0xf6, b0.get());
|
||||||
|
|
||||||
|
// 2nd block is at 1
|
||||||
|
assertEquals((byte)0x86, b1.get());
|
||||||
|
assertEquals((byte)0x09, b1.get());
|
||||||
|
assertEquals((byte)0x22, b1.get());
|
||||||
|
assertEquals((byte)0xfb, b1.get());
|
||||||
|
|
||||||
|
// last block is at 89
|
||||||
|
assertEquals((byte)0xfe, b22.get());
|
||||||
|
assertEquals((byte)0xff, b22.get());
|
||||||
|
assertEquals((byte)0x00, b22.get());
|
||||||
|
assertEquals((byte)0x00, b22.get());
|
||||||
|
assertEquals((byte)0x05, b22.get());
|
||||||
|
assertEquals((byte)0x01, b22.get());
|
||||||
|
assertEquals((byte)0x02, b22.get());
|
||||||
|
assertEquals((byte)0x00, b22.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a stream with several blocks in a 4096 byte block file
|
||||||
|
*/
|
||||||
|
public void testReadStream4096() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
||||||
|
|
||||||
|
// 0 -> 1 -> 2 -> end
|
||||||
|
NPOIFSStream stream = new NPOIFSStream(fs, 0);
|
||||||
|
Iterator<ByteBuffer> i = stream.getBlockIterator();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b0 = i.next();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b1 = i.next();
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
assertEquals(true, i.hasNext());
|
||||||
|
ByteBuffer b2 = i.next();
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
assertEquals(false, i.hasNext());
|
||||||
|
|
||||||
|
// Check the contents of the 1st block
|
||||||
|
assertEquals((byte)0x9E, b0.get());
|
||||||
|
assertEquals((byte)0x75, b0.get());
|
||||||
|
assertEquals((byte)0x97, b0.get());
|
||||||
|
assertEquals((byte)0xF6, b0.get());
|
||||||
|
assertEquals((byte)0xFF, b0.get());
|
||||||
|
assertEquals((byte)0x21, b0.get());
|
||||||
|
assertEquals((byte)0xD2, b0.get());
|
||||||
|
assertEquals((byte)0x11, b0.get());
|
||||||
|
|
||||||
|
// Check the contents of the 2nd block
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x03, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
assertEquals((byte)0x00, b1.get());
|
||||||
|
|
||||||
|
// Check the contents of the 3rd block
|
||||||
|
assertEquals((byte)0x6D, b2.get());
|
||||||
|
assertEquals((byte)0x00, b2.get());
|
||||||
|
assertEquals((byte)0x00, b2.get());
|
||||||
|
assertEquals((byte)0x00, b2.get());
|
||||||
|
assertEquals((byte)0x03, b2.get());
|
||||||
|
assertEquals((byte)0x00, b2.get());
|
||||||
|
assertEquals((byte)0x46, b2.get());
|
||||||
|
assertEquals((byte)0x00, b2.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Craft a nasty file with a loop, and ensure we don't get stuck
|
||||||
|
*/
|
||||||
|
public void testReadFailsOnLoop() throws Exception {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writing the same amount of data as before
|
||||||
|
*/
|
||||||
|
public void testReplaceStream() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes less data than before, some blocks will need
|
||||||
|
* to be freed
|
||||||
|
*/
|
||||||
|
public void testReplaceStreamWithLess() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes more data than before, new blocks will be needed
|
||||||
|
*/
|
||||||
|
public void testReplaceStreamWithMore() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to a new stream in the file
|
||||||
|
*/
|
||||||
|
public void testWriteNewStream() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to a new stream in the file, where we've not enough
|
||||||
|
* free blocks so new FAT segments will need to be allocated
|
||||||
|
* to support this
|
||||||
|
*/
|
||||||
|
public void testWriteNewStreamExtraFATs() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize512.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces data in an existing stream, with a bit
|
||||||
|
* more data than before, in a 4096 byte block file
|
||||||
|
*/
|
||||||
|
public void testWriteStream4096() throws Exception {
|
||||||
|
NPOIFSFileSystem fs = new NPOIFSFileSystem(_inst.getFile("BlockSize4096.zvi"));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Craft a nasty file with a loop, and ensure we don't get stuck
|
||||||
|
*/
|
||||||
|
public void testWriteFailsOnLoop() throws Exception {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user