src/it/unimi/dsi/fastutil/io/FastBufferedInputStream.java
public int readLine(final byte[] array, final int off, final int len, final EnumSet<LineTerminator> terminators) throws IOException {
		ByteArrays.ensureOffsetLength(array ,off, len);
		if (len == 0) return 0; // 0-length reads always return 0
		if (noMoreCharacters()) return -1;
		int i, k = 0, remaining = len, read = 0; // The number of bytes still to be read
		for(;;) {
			for(i = 0; i < avail && i < remaining && (k = buffer[pos + i]) != '\n' && k != '\r' ; i++);
			System.arraycopy(buffer, pos, array, off + read, i);
			pos += i;
			avail -= i;
			read += i;
			remaining -= i;
			if (remaining == 0) {
				readBytes += read;
				return read; // We did not stop because of a terminator
			}

			if (avail > 0) { // We met a terminator
				if (k == '\n') { // LF first
					pos++;
					avail--;
					if (terminators.contains(LineTerminator.LF)) {
						readBytes += read + 1;
						return read;
					}
					else {
						array[off + read++] = '\n';
						remaining--;
					}
				}
				else if (k == '\r') { // CR first
					pos++;
					avail--;

					if (terminators.contains(LineTerminator.CR_LF)) {
						if (avail > 0) {
							if (buffer[pos] == '\n') { // CR/LF with LF already in the buffer.
								pos ++;
								avail--;
								readBytes += read + 2;
								return read;
							}
						}
						else { // We must search for the LF.
							if (noMoreCharacters()) {
								// Not found a matching LF because of end of file, will return CR in buffer if not a terminator

								if (! terminators.contains(LineTerminator.CR)) {
									array[off + read++] = '\r';
									remaining--;
									readBytes += read;
								}
								else readBytes += read + 1;

								return read;
							}
							if (buffer[0] == '\n') {
								// Found matching LF, won't return terminators in the buffer
								pos++;
								avail--;
								readBytes += read + 2;
								return read;
							}
						}
					}

					if (terminators.contains(LineTerminator.CR)) {
						readBytes += read + 1;
						return read;
					}

					array[off + read++] = '\r';
					remaining--;
				}
			}
			else if (noMoreCharacters()) {
				readBytes += read;
				return read;
			}
		}
	}
public int readLine(final byte[] array, final int off, final int len, final EnumSet<LineTerminator> terminators) throws IOException {
		ByteArrays.ensureOffsetLength(array ,off, len);
		if (len == 0) return 0; // 0-length reads always return 0
		if (noMoreCharacters()) return -1;
		int i, k = 0, remaining = len, read = 0; // The number of bytes still to be read
		for(;;) {
			for(i = 0; i < avail && i < remaining && (k = buffer[pos + i]) != '\n' && k != '\r' ; i++);
			System.arraycopy(buffer, pos, array, off + read, i);
			pos += i;
			avail -= i;
			read += i;
			remaining -= i;
			if (remaining == 0) {
				readBytes += read;
				return read; // We did not stop because of a terminator
			}

			if (avail > 0) { // We met a terminator
				if (k == '\n') { // LF first
					pos++;
					avail--;
					if (terminators.contains(LineTerminator.LF)) {
						readBytes += read + 1;
						return read;
					}
					else {
						array[off + read++] = '\n';
						remaining--;
					}
				}
				else if (k == '\r') { // CR first
					pos++;
					avail--;

					if (terminators.contains(LineTerminator.CR_LF)) {
						if (avail > 0) {
							if (buffer[pos] == '\n') { // CR/LF with LF already in the buffer.
								pos ++;
								avail--;
								readBytes += read + 2;
								return read;
							}
						}
						else { // We must search for the LF.
							if (noMoreCharacters()) {
								// Not found a matching LF because of end of file, will return CR in buffer if not a terminator

								if (! terminators.contains(LineTerminator.CR)) {
									array[off + read++] = '\r';
									remaining--;
									readBytes += read;
								}
								else readBytes += read + 1;

								return read;
							}
							if (buffer[0] == '\n') {
								// Found matching LF, won't return terminators in the buffer
								pos++;
								avail--;
								readBytes += read + 2;
								return read;
							}
						}
					}

					if (terminators.contains(LineTerminator.CR)) {
						readBytes += read + 1;
						return read;
					}

					array[off + read++] = '\r';
					remaining--;
				}
			}
			else if (noMoreCharacters()) {
				readBytes += read;
				return read;
			}
		}
	}
Reads a line into the given byte-array fragment.

<p>Reading lines (i.e., characters) out of a byte stream is not always sensible
(methods available to that purpose in old versions of Java have been mercilessly deprecated).
Nonetheless, in several situations, such as when decoding network protocols or headers
known to be ASCII, it is very useful to be able to read a line from a byte stream.

<p>This method will attempt to read the next line into {@code array} starting at {@code off},
reading at most {@code len} bytes. The read, however, will be stopped by the end of file or
when meeting a {@linkplain LineTerminator <em>line terminator</em>}. Of course, for this operation
to be sensible the encoding of the text contained in the stream, if any, must not generate spurious
carriage returns or line feeds. Note that the termination detection uses a maximisation
criterion, so if you specify both {@link LineTerminator#CR} and
{@link LineTerminator#CR_LF} meeting a pair CR/LF will consider the whole pair a terminator.

<p>Terminators are <em>not</em> copied into <em>array</em> or included in the returned count. The
returned integer can be used to check whether the line is complete: if it is smaller than
{@code len}, then more bytes might be available, but note that this method (contrarily
to {@link #read(byte[], int, int)}) can legitimately return zero when {@code len}
is nonzero just because a terminator was found as the first character. Thus, the intended
usage of this method is to call it on a given array, check whether {@code len} bytes
have been read, and if so try again (possibly extending the array) until a number of read bytes
strictly smaller than {@code len} (possibly, -1) is returned.

<p>If you need to guarantee that a full line is read, use the following idiom:
<pre>
int start = off, len;
while((len = fbis.readLine(array, start, array.length - start, terminators)) == array.length - start) {
start += len;
array = ByteArrays.grow(array, array.length + 1);
}
</pre>

<p>At the end of the loop, the line will be placed in {@code array} starting at
{@code off} (inclusive) and ending at {@code start + Math.max(len, 0)} (exclusive).

@param array byte array where the next line will be stored.
@param off the first byte to use in {@code array}.
@param len the maximum number of bytes to read.
@param terminators a set containing the line termination sequences that we want
to consider as valid.
@return the number of bytes actually placed in {@code array}, or -1 at end of file.
Note that the returned number will be {@code len} if no line termination sequence
specified in {@code terminators} has been met before scanning {@code len} byte,
and if also we did not meet the end of file.