Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

class FileOutputStream extends OutputStream {

private static final int INITIAL_BUFFER_SIZE = 8 * 1024;

private final File file;
private ByteBuffer buffer;
private boolean open = true;
Expand All @@ -30,7 +32,6 @@ class FileOutputStream extends OutputStream {

public FileOutputStream(final File file) {
this.file = file;
buffer = ByteBuffer.allocate(file.getChunkSize());
length = file.length;
chunks = file.chunks;
if (chunks > 0 && file.length % file.getChunkSize() != 0) {
Expand All @@ -39,17 +40,19 @@ public FileOutputStream(final File file) {
// are full except for the last chunk.
chunks--;
byte[] previousChunkData = file.getFileSystem().getChunk(file, chunks);
buffer = ByteBuffer.allocate(
Math.min(Math.max(INITIAL_BUFFER_SIZE, previousChunkData.length), file.getChunkSize()));
buffer.put(previousChunkData);
} else {
buffer = ByteBuffer.allocate(Math.min(INITIAL_BUFFER_SIZE, file.getChunkSize()));
}
}

@Override
public void write(final int b) throws IOException {
assertOpen();

if (buffer.remaining() == 0) {
flushBuffer();
}
ensureCapacity();

buffer.put((byte) b);
length++;
Expand All @@ -60,9 +63,7 @@ public void write(final byte[] b, int off, int len) throws IOException {
assertOpen();

while (len > 0) {
if (buffer.remaining() == 0) {
flushBuffer();
}
ensureCapacity();

final int min = Math.min(buffer.remaining(), len);
buffer.put(b, off, min);
Expand All @@ -85,6 +86,30 @@ public void close() throws IOException {
}
}

/**
* Makes room for at least one more byte. The buffer grows until it reaches the chunk size, and a
* chunk is written only once it is full.
*/
private void ensureCapacity() {
if (buffer.remaining() > 0) {
return;
}
if (buffer.capacity() < file.getChunkSize()) {
growBuffer();
} else {
flushBuffer();
}
}

private void growBuffer() {
int newCapacity =
Math.min(Math.max(buffer.capacity() * 2, INITIAL_BUFFER_SIZE), file.getChunkSize());
ByteBuffer larger = ByteBuffer.allocate(newCapacity);
buffer.flip();
larger.put(buffer);
buffer = larger;
}

private void flushBuffer() {
byte[] chunk = Arrays.copyOfRange(buffer.array(), buffer.arrayOffset(), buffer.position());
file.getFileSystem().putChunk(file, chunks++, chunk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,56 @@ public void testReadWriteBytes() throws Exception {
assertEquals(-1, is.read());
}

/**
* A test that every chunk except the last is full when a file is written in small pieces and
* then appended to one byte at a time.
*/
@Test
public void testChunksAreFullExceptLast() throws Exception {
File file = system.createFile("testFile");

byte[] data = getRandomBytes(FileSystem.CHUNK_SIZE * 2 + SMALL_CHUNK);
OutputStream outputStream = file.getOutputStream();
int offset = 0;
while (offset < data.length) {
int len = Math.min(SMALL_CHUNK, data.length - offset);
outputStream.write(data, offset, len);
offset += len;
}
outputStream.close();

assertEquals(data.length, file.getLength());
assertEquals(3, file.chunks);
assertEquals(FileSystem.CHUNK_SIZE, system.getChunk(file, 0).length);
assertEquals(FileSystem.CHUNK_SIZE, system.getChunk(file, 1).length);
assertEquals(SMALL_CHUNK, system.getChunk(file, 2).length);

byte[] appended = getRandomBytes(FileSystem.CHUNK_SIZE);
OutputStream appendStream = file.getOutputStream();
for (byte b : appended) {
appendStream.write(b);
}
appendStream.close();

assertEquals(data.length + appended.length, file.getLength());
assertEquals(4, file.chunks);
assertEquals(FileSystem.CHUNK_SIZE, system.getChunk(file, 2).length);
assertEquals(SMALL_CHUNK, system.getChunk(file, 3).length);

byte[] expected = new byte[data.length + appended.length];
System.arraycopy(data, 0, expected, 0, data.length);
System.arraycopy(appended, 0, expected, data.length, appended.length);
byte[] actual = new byte[expected.length];
InputStream is = file.getInputStream();
int read = 0;
int count;
while (read < actual.length && (count = is.read(actual, read, actual.length - read)) > 0) {
read += count;
}
is.close();
assertArrayEquals(expected, actual);
}

/**
* A test of cloning a a FileInputStream. The clone should start from where the original was
* positioned, but they should not hurt each other.
Expand Down
Loading