// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.sdk.internal.transport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/**
* A helper reader class that allows to read LF-terminated lines and fixed-sized blocks of bytes
* in turn. To keep it fast it stores the data inside its internal buffer.
*/
class LineReader {
private static final byte LF_BYTE = '\n';
private static final byte CR_BYTE = '\r';
// A source stream.
private final InputStream inputStream;
// Internal main buffer. Should be kept in 'read' (flipped) state.
private final ByteBuffer buffer;
{
buffer = ByteBuffer.allocate(1024);
buffer.flip();
}
// A cached buffer instance used for constructing a string.
private ByteBuffer lineBuffer = ByteBuffer.allocate(20);
LineReader(InputStream inputStream) {
this.inputStream = inputStream;
}
/**
* Method has similar semantics to {@link BufferedReader#read(char[], int, int)} method.
*/
public int read(byte[] cbuf, int off, int len) throws IOException {
if (buffer.hasRemaining()) {
len = Math.min(len, buffer.remaining());
buffer.get(cbuf, off, len);
return len;
} else {
return inputStream.read(cbuf, off, len);
}
}
/**
* Method has similar semantics to {@link BufferedReader#readLine()} method.
*/
public String readLine(Charset charset) throws IOException {
lineBuffer.clear();
while (true) {
if (buffer.hasRemaining()) {
boolean lineEndFound = false;
int pos;
findingLineEnd:
for (pos = buffer.position(); pos < buffer.limit(); pos++) {
if (buffer.get(pos) == LF_BYTE) {
lineEndFound = true;
break findingLineEnd;
}
}
int chunkLen = pos - buffer.position();
if (chunkLen > 0) {
if (lineBuffer.remaining() < chunkLen) {
int newSize = Math.max(lineBuffer.capacity() * 2, lineBuffer.position() + chunkLen);
ByteBuffer newLineBuffer = ByteBuffer.allocate(newSize);
lineBuffer.flip();
newLineBuffer.put(lineBuffer);
lineBuffer = newLineBuffer;
}
buffer.get(lineBuffer.array(), lineBuffer.position(), chunkLen);
lineBuffer.position(lineBuffer.position() + chunkLen);
}
if (lineEndFound) {
// Shift position.
buffer.get();
break;
}
}
assert !buffer.hasRemaining();
buffer.clear();
int readRes = inputStream.read(buffer.array());
if (readRes <= 0) {
if (lineBuffer.position() == 0) {
return null;
} else {
throw new IOException("End of stream while expecting line end");
}
}
buffer.position(readRes);
buffer.flip();
}
if (lineBuffer.position() > 0 && lineBuffer.get(lineBuffer.position() - 1) == CR_BYTE) {
lineBuffer.position(lineBuffer.position() - 1);
}
return new String(lineBuffer.array(), 0, lineBuffer.position(), charset);
}
}