docs: public String readLine() throws IOException {
  		synchronized (in) {
  			if (buf == null) {
  				throw new IOException("LineReader is closed");
  			}
  
  			// Read more data if we are at the end of the buffered data.
  			// Though it's an error to read after an exception, we will let {@code fillBuf()}
  			// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
  			if (pos >= end) {
  				fillBuf();
  			}
  			// Try to find LF in the buffered data and return the line if successful.
  			for (int i = pos; i != end; ++i) {
  				if (buf[i] == LF) {
  					int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
  					String res = new String(buf, pos, lineEnd - pos, charset.name());
  					pos = i + 1;
  					return res;
  				}
  			}
  
  			// Let's anticipate up to 80 characters on top of those already read.
  			ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
  				@Override
  				public String toString() {
  					int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
  					try {
  						return new String(buf, 0, length, charset.name());
  					} catch (UnsupportedEncodingException e) {
  						throw new AssertionError(e); // Since we control the charset this will never happen.
  					}
  				}
  			};
  
  			while (true) {
  				out.write(buf, pos, end - pos);
  				// Mark unterminated line in case fillBuf throws EOFException or IOException.
  				end = -1;
  				fillBuf();
  				// Try to find LF in the buffered data and return the line if successful.
  				for (int i = pos; i != end; ++i) {
  					if (buf[i] == LF) {
  						if (i != pos) {
  							out.write(buf, pos, i - pos);
  						}
  						pos = i + 1;
  						return out.toString();
  					}
  				}
  			}
  		}
  	}