/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008, 2009 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.io;
import java.io.IOException;
import java.util.EnumSet;
import java.util.logging.Level;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.config.SystemConfiguration;
import org.ccnx.ccn.impl.security.crypto.ContentKeys;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.PublisherPublicKeyDigest;
/**
* This input stream expects to do packet-oriented reading of
* fixed chunks. The chunks can be individually signed or authenticated
* using a Merkle Hash Tree, but read will return when it gets a single
* block of content, and will not fill buffers across content blocks.
* This will consume data written by either CCNBlockOutputStream,
* or by the C program ccnsendchunks.
* The intent is to read packet-oriented protocols; possibly a better
* abstraction is to move this to be a subclass of DatagramSocket.
*/
public class CCNBlockInputStream extends CCNAbstractInputStream {
public CCNBlockInputStream(ContentName baseName) throws IOException {
this(baseName, null, null, null);
}
public CCNBlockInputStream(ContentName baseName, CCNHandle handle) throws IOException {
this(baseName, null, null, handle);
}
public CCNBlockInputStream(ContentName baseName,
PublisherPublicKeyDigest publisher, CCNHandle handle) throws IOException {
this(baseName, null, publisher, handle);
}
public CCNBlockInputStream(ContentName baseName, Long segmentNumber, CCNHandle handle) throws IOException {
this(baseName, segmentNumber, null, handle);
}
public CCNBlockInputStream(ContentName baseName, Long startingSegmentNumber,
PublisherPublicKeyDigest publisher, CCNHandle handle) throws IOException {
super(baseName, startingSegmentNumber, publisher, null, null, handle);
setTimeout(SystemConfiguration.NO_TIMEOUT);
}
public CCNBlockInputStream(ContentName baseName, Long startingSegmentNumber,
PublisherPublicKeyDigest publisher, ContentKeys keys, CCNHandle handle) throws IOException {
super(baseName, startingSegmentNumber, publisher, keys, null, handle);
setTimeout(SystemConfiguration.NO_TIMEOUT);
}
public CCNBlockInputStream(ContentObject firstSegment, EnumSet<FlagTypes> flags, CCNHandle handle) throws IOException {
super(firstSegment, null, flags, handle);
}
public CCNBlockInputStream(ContentObject firstSegment, ContentKeys keys, EnumSet<FlagTypes> flags, CCNHandle handle) throws IOException {
super(firstSegment, keys, flags, handle);
}
/**
* Implement sequential reads of data quantized into segments. Will read the remainder
* of the current segment on each read(byte[], int, int) call, when a given
* segment runs out of bytes returns -1. Next read(byte[], int, int) call
* will retrieve the next segment. Meant for reading complete segments at a time.
*/
@Override
protected int readInternal(byte [] buf, int offset, int len) throws IOException {
if (Log.isLoggable(Log.FAC_IO, Level.INFO))
Log.info(Log.FAC_IO, "CCNBlockInputStream: reading " + len + " bytes into buffer of length " +
((null != buf) ? buf.length : "null") + " at offset " + offset);
// is this the first block?
if (null == _currentSegment) {
// This will throw an exception if no block found, which is what we want.
setFirstSegment(getFirstSegment());
}
// Now we have a block in place. Read from it. If we run out of block before
// we've read len bytes, return what we read. On next read, pull next block.
int remainingBytes = _segmentReadStream.available();
if (remainingBytes <= 0) {
if (!hasNextSegment()) {
return -1;
}
ContentObject nextSegment = getNextSegment();
if (null == nextSegment) {
// in socket implementation, this would be EAGAIN
return -1;
}
setCurrentSegment(nextSegment);
remainingBytes = _segmentReadStream.available();
}
// Read minimum of remainder of this block and available buffer.
long readCount = (remainingBytes > len) ? len : remainingBytes;
if (null != buf) { // use for skip
readCount = _segmentReadStream.read(buf, offset, len);
} else {
readCount = _segmentReadStream.skip(len);
}
if (Log.isLoggable(Log.FAC_IO, Level.INFO))
Log.info(Log.FAC_IO, "CCNBlockInputStream: read " + readCount + " bytes from block " + _currentSegment.name());
return (int)readCount;
}
}