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(&quot;file.java&quot;));
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