/*
* Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
* is available at http://www.opensource.org/licenses/lgpl-2.1.php.
*
* THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
* ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
* OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE. IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
* RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
* ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
* DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
* BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
* FOREGOING.
*/
package htsjdk.tribble.readers;
import htsjdk.samtools.util.LocationAware;
import htsjdk.tribble.TribbleException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
/**
* A simple class that provides {@link #readLine()} functionality around a PositionalBufferedStream
*
* {@link BufferedReader} and its {@link java.io.BufferedReader#readLine()} method should be used in preference to this class (when the
* {@link htsjdk.samtools.util.LocationAware} functionality is not required) because it offers greater performance.
*
* @author jrobinso
*/
public class AsciiLineReader implements LineReader, LocationAware {
private static final int BUFFER_OVERFLOW_INCREASE_FACTOR = 2;
private static final byte LINEFEED = (byte) ('\n' & 0xff);
private static final byte CARRIAGE_RETURN = (byte) ('\r' & 0xff);
PositionalBufferedStream is;
char[] lineBuffer;
public AsciiLineReader(final InputStream is){
this(new PositionalBufferedStream(is));
}
public AsciiLineReader(final PositionalBufferedStream is) {
this.is = is;
// Allocate this only once, even though it is essentially a local variable of
// readLine. This makes a huge difference in performance
lineBuffer = new char[10000];
}
/**
* @return The position of the InputStream
*/
public long getPosition(){
if(is == null){
throw new TribbleException("getPosition() called but no default stream was provided to the class on creation");
}
return is.getPosition();
}
/**
* Read a line of text. A line is considered to be terminated by any one
* of a line feed ('\n'), a carriage return ('\r'), or a carriage return
* followed immediately by a linefeed.
*
* @param stream the stream to read the next line from
* @return A String containing the contents of the line or null if the
* end of the stream has been reached
*/
public final String readLine(final PositionalBufferedStream stream) throws IOException{
int linePosition = 0;
while (true) {
final int b = stream.read();
if (b == -1) {
// eof reached. Return the last line, or null if this is a new line
if (linePosition > 0) {
return new String(lineBuffer, 0, linePosition);
} else {
return null;
}
}
final char c = (char) (b & 0xFF);
if (c == LINEFEED || c == CARRIAGE_RETURN) {
if (c == CARRIAGE_RETURN && stream.peek() == LINEFEED) {
stream.read(); // <= skip the trailing \n in case of \r\n termination
}
return new String(lineBuffer, 0, linePosition);
} else {
// Expand line buffer size if neccessary. Reserve at least 2 characters
// for potential line-terminators in return string
if (linePosition > (lineBuffer.length - 3)) {
final char[] temp = new char[BUFFER_OVERFLOW_INCREASE_FACTOR * lineBuffer.length];
System.arraycopy(lineBuffer, 0, temp, 0, lineBuffer.length);
lineBuffer = temp;
}
lineBuffer[linePosition++] = c;
}
}
}
/**
* Same as {@link #readLine(PositionalBufferedStream)} but uses the stream provided in the constructor
*
* @return
*/
public final String readLine() throws IOException{
if ( is == null ){
throw new TribbleException("readLine() called without an explicit stream argument but no default stream was provided to the class on creation");
}
return readLine(is);
}
@Override
public void close() {
if ( is != null ) is.close();
lineBuffer = null;
}
public static void main(final String[] args) throws Exception {
final File testFile = new File(args[0]);
final int iterations = Integer.valueOf(args[1]);
final boolean includeBufferedReader = Boolean.valueOf(args[2]);
long t0, lineCount, dt;
double rate;
System.out.printf("Testing %s%n", args[0]);
for (int i = 0; i < iterations; i++) {
if ( includeBufferedReader ) {
final BufferedReader reader2 = new BufferedReader(new FileReader(testFile));
t0 = System.currentTimeMillis();
lineCount = 0;
while (reader2.readLine() != null) {
lineCount++;
}
dt = System.currentTimeMillis() - t0;
rate = ((double) lineCount) / dt;
printStatus("BufferedReader", lineCount, rate, dt);
reader2.close();
}
if ( includeBufferedReader ) {
final LongLineBufferedReader longLineBufferedReader = new LongLineBufferedReader(new BufferedReader(new FileReader(testFile)));
t0 = System.currentTimeMillis();
lineCount = 0;
while (longLineBufferedReader.readLine() != null) {
lineCount++;
}
dt = System.currentTimeMillis() - t0;
rate = ((double) lineCount) / dt;
printStatus("BufferedReader", lineCount, rate, dt);
longLineBufferedReader.close();
}
final PositionalBufferedStream pbs = new PositionalBufferedStream(new FileInputStream(testFile));
final LineReader reader = new AsciiLineReader(pbs);
t0 = System.currentTimeMillis();
lineCount = 0;
while (reader.readLine() != null) {
lineCount++;
}
dt = System.currentTimeMillis() - t0;
rate = ((double) lineCount) / dt;
printStatus("PositionalBufferedStream", lineCount, rate, dt);
pbs.close();
}
}
private static final void printStatus(final String name, final long lineCount, final double rate, final long dt) {
System.out.printf("%30s: %d lines read. Rate = %.2e lines per second. DT = %d%n", name, lineCount, rate, dt);
System.out.flush();
}
}