Files
heritrix3/commons/src/main/java/org/archive/io/ReplayInputStream.java
T
gojomo cb8a374e5d [HER-1636] H3: Failed get of replay char sequence due the OutOfMemoryError: Map failed (port HER-1482)
(from nlevitt's H2 commit message:)
* InMemoryReplayCharSequence.java
    new, simple ReplayCharSequence that supports any encoding, keeping everything in memory
* GenericReplayCharSequence.java
    If the encoding supports random access, memory maps the backing file directly; otherwise decodes to UTF-16 and maps that. Supports the first Integer.MAX_VALUE bytes of the file. Maps up to 64M; moves the map around as necessary for larger files.
* Latin1ByteReplayCharSequence.java
    removed, functionality split between InMemoryReplayCharSequence and GenericReplayCharSequence
* ReplayCharSequenceTest.java
    xestHugeReplayCharSequence() - uncomment to test a huge replay char sequence
    testReplayCharSequenceByteToStringOverflow() - test both UTF-8 and windows-1252
* RecordingOutputStream.java
    use the new ReplayCharSequences
2009-07-07 21:53:57 +00:00

283 lines
7.5 KiB
Java

/*
* This file is part of the Heritrix web crawler (crawler.archive.org).
*
* Licensed to the Internet Archive (IA) by one or more individual
* contributors.
*
* The IA 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.archive.io;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Replays the bytes recorded from a RecordingInputStream or
* RecordingOutputStream.
*
* This InputStream supports mark and reset.
*
* @author gojomo
*/
public class ReplayInputStream extends SeekInputStream
{
private BufferedSeekInputStream diskStream;
private byte[] buffer;
private long position;
/**
* Total size of stream content.
*
* Size of data to replay.
*/
private long size = -1;
/**
* Where the response body starts, if marked
*/
protected long responseBodyStart = -1;
/**
* Constructor.
*
* @param buffer Buffer to read from.
* @param size Size of data to replay.
* @param responseBodyStart Start of the response body.
* @param backingFilename Backing file that sits behind the buffer. If
* <code>size<code> > than buffer then we go to backing file to read
* data that is beyond buffer.length.
*
* @throws IOException If we fail to open an input stream on
* backing file.
*/
public ReplayInputStream(byte[] buffer, long size, long responseBodyStart,
String backingFilename)
throws IOException
{
this(buffer, size, backingFilename);
this.responseBodyStart = responseBodyStart;
}
/**
* Constructor.
*
* @param buffer Buffer to read from.
* @param size Size of data to replay.
* @param backingFilename Backing file that sits behind the buffer. If
* <code>size<code> > than buffer then we go to backing file to read
* data that is beyond buffer.length.
* @throws IOException If we fail to open an input stream on
* backing file.
*/
public ReplayInputStream(byte[] buffer, long size, String backingFilename)
throws IOException
{
this.buffer = buffer;
this.size = size;
if (size > buffer.length) {
RandomAccessInputStream rais = new RandomAccessInputStream(
new File(backingFilename));
diskStream = new BufferedSeekInputStream(rais, 4096);
}
}
public long setToResponseBodyStart() throws IOException {
position(responseBodyStart);
return this.position;
}
/* (non-Javadoc)
* @see java.io.InputStream#read()
*/
public int read() throws IOException {
if (position == size) {
return -1; // EOF
}
if (position < buffer.length) {
// Convert to unsigned int.
int c = buffer[(int) position] & 0xFF;
position++;
return c;
}
int c = diskStream.read();
if (c >= 0) {
position++;
}
return c;
}
/*
* (non-Javadoc)
*
* @see java.io.InputStream#read(byte[], int, int)
*/
public int read(byte[] b, int off, int len) throws IOException {
if (position == size) {
return -1; // EOF
}
if (position < buffer.length) {
int toCopy = (int)Math.min(size - position,
Math.min(len, buffer.length - position));
System.arraycopy(buffer, (int)position, b, off, toCopy);
if (toCopy > 0) {
position += toCopy;
}
return toCopy;
}
// into disk zone
int read = diskStream.read(b,off,len);
if(read>0) {
position += read;
}
return read;
}
public void readFullyTo(OutputStream os) throws IOException {
byte[] buf = new byte[4096];
int c = read(buf);
while (c != -1) {
os.write(buf,0,c);
c = read(buf);
}
}
/*
* Like 'readFullyTo', but only reads the header-part.
* Starts from the beginning each time it is called.
*/
public void readHeaderTo(OutputStream os) throws IOException {
position = 0;
byte[] buf = new byte[(int)responseBodyStart];
int c = read(buf,0,buf.length);
if(c != -1) {
os.write(buf,0,c);
}
}
/*
* Like 'readFullyTo', but only reads the content-part.
*/
public void readContentTo(OutputStream os) throws IOException {
setToResponseBodyStart();
byte[] buf = new byte[4096];
int c = read(buf);
while (c != -1) {
os.write(buf,0,c);
c = read(buf);
}
}
/**
* Convenience method to copy content out to target stream.
* @param os stream to write content to
* @param maxSize maximum count of bytes to copy
* @throws IOException
*/
public void readContentTo(OutputStream os, long maxSize) throws IOException {
setToResponseBodyStart();
byte[] buf = new byte[4096];
int c = read(buf);
long tot = 0;
while (c != -1 && tot < maxSize) {
os.write(buf,0,c);
c = read(buf);
tot += c;
}
}
/* (non-Javadoc)
* @see java.io.InputStream#close()
*/
public void close() throws IOException {
super.close();
if(diskStream != null) {
diskStream.close();
}
}
/**
* Total size of stream content.
* @return Returns the size.
*/
public long getSize()
{
return size;
}
/**
* Total size of header.
* @return the size of the header.
*/
public long getHeaderSize()
{
return responseBodyStart;
}
/**
* Total size of content.
* @return the size of the content.
*/
public long getContentSize()
{
return size - responseBodyStart;
}
/**
* @return Amount THEORETICALLY remaining (TODO: Its not theoretical
* seemingly. The class implemetentation depends on it being exact).
*/
public long remaining() {
return size - position;
}
/**
* Reposition the stream.
*
* @param p the new position for this stream
* @throws IOException if an IO error occurs
*/
public void position(long p) throws IOException {
if (p < 0) {
throw new IOException("Negative seek offset.");
}
if (p > size) {
throw new IOException("Desired position exceeds size.");
}
if (p < buffer.length) {
// Only seek file if necessary
if (position > buffer.length) {
diskStream.position(0);
}
} else {
diskStream.position(p - buffer.length);
}
this.position = p;
}
public long position() throws IOException {
return position;
}
// package private
byte[] getBuffer() {
return buffer;
}
}