/*
*
* 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.bookkeeper.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* Provides a buffering layer in front of a FileChannel.
*/
public class BufferedChannel
{
ByteBuffer writeBuffer;
ByteBuffer readBuffer;
private FileChannel bc;
long position;
int capacity;
long readBufferStartPosition;
long writeBufferStartPosition;
BufferedChannel(FileChannel bc, int capacity) throws IOException {
this.bc = bc;
this.capacity = capacity;
position = bc.position();
writeBufferStartPosition = position;
}
/* public void close() throws IOException {
bc.close();
}
*/
// public boolean isOpen() {
// return bc.isOpen();
// }
synchronized public int write(ByteBuffer src) throws IOException {
int copied = 0;
if (writeBuffer == null) {
writeBuffer = ByteBuffer.allocateDirect(capacity);
}
while(src.remaining() > 0) {
int truncated = 0;
if (writeBuffer.remaining() < src.remaining()) {
truncated = src.remaining() - writeBuffer.remaining();
src.limit(src.limit()-truncated);
}
copied += src.remaining();
writeBuffer.put(src);
src.limit(src.limit()+truncated);
if (writeBuffer.remaining() == 0) {
writeBuffer.flip();
bc.write(writeBuffer);
writeBuffer.clear();
writeBufferStartPosition = bc.position();
}
}
position += copied;
return copied;
}
public long position() {
return position;
}
public void flush(boolean sync) throws IOException {
synchronized(this) {
if (writeBuffer == null) {
return;
}
writeBuffer.flip();
bc.write(writeBuffer);
writeBuffer.clear();
writeBufferStartPosition = bc.position();
}
if (sync) {
bc.force(false);
}
}
/*public Channel getInternalChannel() {
return bc;
}*/
synchronized public int read(ByteBuffer buff, long pos) throws IOException {
if (readBuffer == null) {
readBuffer = ByteBuffer.allocateDirect(capacity);
readBufferStartPosition = Long.MIN_VALUE;
}
int rc = buff.remaining();
while(buff.remaining() > 0) {
// check if it is in the write buffer
if (writeBuffer != null && writeBufferStartPosition <= pos) {
long positionInBuffer = pos - writeBufferStartPosition;
long bytesToCopy = writeBuffer.position()-positionInBuffer;
if (bytesToCopy > buff.remaining()) {
bytesToCopy = buff.remaining();
}
if (bytesToCopy == 0) {
throw new IOException("Read past EOF");
}
ByteBuffer src = writeBuffer.duplicate();
src.position((int) positionInBuffer);
src.limit((int) (positionInBuffer+bytesToCopy));
buff.put(src);
pos+= bytesToCopy;
// first check if there is anything we can grab from the readBuffer
} else if (readBufferStartPosition <= pos && pos < readBufferStartPosition+readBuffer.capacity()) {
long positionInBuffer = pos - readBufferStartPosition;
long bytesToCopy = readBuffer.capacity()-positionInBuffer;
if (bytesToCopy > buff.remaining()) {
bytesToCopy = buff.remaining();
}
ByteBuffer src = readBuffer.duplicate();
src.position((int) positionInBuffer);
src.limit((int) (positionInBuffer+bytesToCopy));
buff.put(src);
pos += bytesToCopy;
// let's read it
} else {
readBufferStartPosition = pos;
readBuffer.clear();
// make sure that we don't overlap with the write buffer
if (readBufferStartPosition + readBuffer.capacity() >= writeBufferStartPosition) {
readBufferStartPosition = writeBufferStartPosition - readBuffer.capacity();
if (readBufferStartPosition < 0) {
readBuffer.put(LedgerEntryPage.zeroPage, 0, (int)-readBufferStartPosition);
}
}
while(readBuffer.remaining() > 0) {
if (bc.read(readBuffer, readBufferStartPosition+readBuffer.position()) <= 0) {
throw new IOException("Short read");
}
}
readBuffer.put(LedgerEntryPage.zeroPage, 0, readBuffer.remaining());
readBuffer.clear();
}
}
return rc;
}
}