// 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); } }