|  | // Copyright 2016 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | package com.google.devtools.build.lib.runtime; | 
|  |  | 
|  | import java.io.IOException; | 
|  | import java.io.OutputStream; | 
|  |  | 
|  | /** | 
|  | * A decorator output stream that does line buffering. | 
|  | */ | 
|  | public class LineBufferedOutputStream extends OutputStream { | 
|  | private static final int DEFAULT_BUFFER_SIZE = 1024; | 
|  |  | 
|  | private final OutputStream wrapped; | 
|  | private final byte[] buffer; | 
|  | private int pos; | 
|  |  | 
|  | public LineBufferedOutputStream(OutputStream wrapped) { | 
|  | this(wrapped, DEFAULT_BUFFER_SIZE); | 
|  | } | 
|  |  | 
|  | public LineBufferedOutputStream(OutputStream wrapped, int bufferSize) { | 
|  | this.wrapped = wrapped; | 
|  | this.buffer = new byte[bufferSize]; | 
|  | this.pos = 0; | 
|  | } | 
|  |  | 
|  | private void flushBuffer() throws IOException { | 
|  | int oldPos = pos; | 
|  | // Set pos to zero first so that if the write below throws, we are still in a consistent state. | 
|  | pos = 0; | 
|  | wrapped.write(buffer, 0, oldPos); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void write(byte[] b, int off, int inlen) throws IOException { | 
|  | if (inlen > buffer.length * 2) { | 
|  | // Do not buffer large writes | 
|  | if (pos > 0) { | 
|  | flushBuffer(); | 
|  | } | 
|  | wrapped.write(b, off, inlen); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int next = off; | 
|  | while (next < off + inlen) { | 
|  | buffer[pos++] = b[next]; | 
|  | if (b[next] == '\n' || pos == buffer.length) { | 
|  | flushBuffer(); | 
|  | } | 
|  |  | 
|  | next++; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void write(int byteAsInt) throws IOException { | 
|  | byte b = (byte) byteAsInt; // make sure we work with bytes in comparisons | 
|  | write(new byte[] {b}, 0, 1); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void flush() throws IOException { | 
|  | if (pos != 0) { | 
|  | wrapped.write(buffer, 0, pos); | 
|  | pos = 0; | 
|  | } | 
|  | wrapped.flush(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void close() throws IOException { | 
|  | flush(); | 
|  | wrapped.close(); | 
|  | } | 
|  | } |