BufferedOutputStream.java revision f33eae7e84eb6d3b0f4e86b59605bb3de73009f3
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.io; 19 20import org.apache.harmony.luni.util.Msg; 21 22// BEGIN android-added 23import java.util.logging.Logger; 24// END android-added 25 26/** 27 * Wraps an existing {@link OutputStream} and <em>buffers</em> the output. 28 * Expensive interaction with the underlying input stream is minimized, since 29 * most (smaller) requests can be satisfied by accessing the buffer alone. The 30 * drawback is that some extra space is required to hold the buffer and that 31 * copying takes place when flushing that buffer, but this is usually outweighed 32 * by the performance benefits. 33 * 34 * <p/>A typical application pattern for the class looks like this:<p/> 35 * 36 * <pre> 37 * BufferedOutputStream buf = new BufferedOutputStream(new FileOutputStream("file.java")); 38 * </pre> 39 * 40 * @see BufferedInputStream 41 */ 42public class BufferedOutputStream extends FilterOutputStream { 43 /** 44 * The buffer containing the bytes to be written to the target stream. 45 */ 46 protected byte[] buf; 47 48 /** 49 * The total number of bytes inside the byte array {@code buf}. 50 */ 51 protected int count; 52 53 /** 54 * Constructs a new {@code BufferedOutputStream}, providing {@code out} with a buffer 55 * of 8192 bytes. 56 * 57 * @param out the {@code OutputStream} the buffer writes to. 58 */ 59 public BufferedOutputStream(OutputStream out) { 60 this(out, 8192); 61 } 62 63 /** 64 * Constructs a new {@code BufferedOutputStream}, providing {@code out} with {@code size} bytes 65 * of buffer. 66 * 67 * @param out the {@code OutputStream} the buffer writes to. 68 * @param size the size of buffer in bytes. 69 * @throws IllegalArgumentException if {@code size <= 0}. 70 */ 71 public BufferedOutputStream(OutputStream out, int size) { 72 super(out); 73 if (size <= 0) { 74 // K0058=size must be > 0 75 throw new IllegalArgumentException(Msg.getString("K0058")); 76 } 77 buf = new byte[size]; 78 } 79 80 /** 81 * Flushes this stream to ensure all pending data is written out to the 82 * target stream. In addition, the target stream is flushed. 83 * 84 * @throws IOException 85 * if an error occurs attempting to flush this stream. 86 */ 87 @Override 88 public synchronized void flush() throws IOException { 89 if (buf == null) { 90 throw new IOException(Msg.getString("K0059")); 91 } 92 93 flushInternal(); 94 out.flush(); 95 } 96 97 /** 98 * Writes {@code count} bytes from the byte array {@code buffer} starting at 99 * {@code offset} to this stream. If there is room in the buffer to hold the 100 * bytes, they are copied in. If not, the buffered bytes plus the bytes in 101 * {@code buffer} are written to the target stream, the target is flushed, 102 * and the buffer is cleared. 103 * 104 * @param buffer 105 * the buffer to be written. 106 * @param offset 107 * the start position in {@code buffer} from where to get bytes. 108 * @param length 109 * the number of bytes from {@code buffer} to write to this 110 * stream. 111 * @throws IndexOutOfBoundsException 112 * if {@code offset < 0} or {@code length < 0}, or if 113 * {@code offset + length} is greater than the size of 114 * {@code buffer}. 115 * @throws IOException 116 * if an error occurs attempting to write to this stream. 117 * @throws NullPointerException 118 * if {@code buffer} is {@code null}. 119 * @throws ArrayIndexOutOfBoundsException 120 * If offset or count is outside of bounds. 121 */ 122 @Override 123 public synchronized void write(byte[] buffer, int offset, int length) 124 throws IOException { 125 byte[] internalBuffer = buf; 126 if (internalBuffer == null) { 127 throw new IOException(Msg.getString("K0059")); 128 } 129 130 if (buffer == null) { 131 // K0047=buffer is null 132 throw new NullPointerException(Msg.getString("K0047")); 133 } 134 135 if (length >= internalBuffer.length) { 136 flushInternal(); 137 out.write(buffer, offset, length); 138 return; 139 } 140 141 if (offset < 0 || offset > buffer.length - length) { 142 // K002e=Offset out of bounds \: {0} 143 throw new ArrayIndexOutOfBoundsException(Msg.getString("K002e", offset)); 144 145 } 146 if (length < 0) { 147 // K0031=Length out of bounds \: {0} 148 throw new ArrayIndexOutOfBoundsException(Msg.getString("K0031", length)); 149 } 150 151 // flush the internal buffer first if we have not enough space left 152 if (length >= (internalBuffer.length - count)) { 153 flushInternal(); 154 } 155 156 // the length is always less than (internalBuffer.length - count) here so arraycopy is safe 157 System.arraycopy(buffer, offset, internalBuffer, count, length); 158 count += length; 159 } 160 161 @Override public synchronized void close() throws IOException { 162 if (buf == null) { 163 return; 164 } 165 166 try { 167 super.close(); 168 } finally { 169 buf = null; 170 } 171 } 172 173 /** 174 * Writes one byte to this stream. Only the low order byte of the integer 175 * {@code oneByte} is written. If there is room in the buffer, the byte is 176 * copied into the buffer and the count incremented. Otherwise, the buffer 177 * plus {@code oneByte} are written to the target stream, the target is 178 * flushed, and the buffer is reset. 179 * 180 * @param oneByte 181 * the byte to be written. 182 * @throws IOException 183 * if an error occurs attempting to write to this stream. 184 */ 185 @Override 186 public synchronized void write(int oneByte) throws IOException { 187 byte[] internalBuffer = buf; 188 if (internalBuffer == null) { 189 throw new IOException(Msg.getString("K0059")); 190 } 191 192 if (count == internalBuffer.length) { 193 out.write(internalBuffer, 0, count); 194 count = 0; 195 } 196 internalBuffer[count++] = (byte) oneByte; 197 } 198 199 /** 200 * Flushes only internal buffer. 201 */ 202 private void flushInternal() throws IOException { 203 if (count > 0) { 204 out.write(buf, 0, count); 205 count = 0; 206 } 207 } 208} 209