BufferedOutputStream.java revision a0881d052ee72e3f7e773374e9b1aa75fbd6be4c
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 *
42 * @since Android 1.0
43 */
44public class BufferedOutputStream extends FilterOutputStream {
45    /**
46     * The buffer containing the bytes to be written to the target stream.
47     *
48     * @since Android 1.0
49     */
50    protected byte[] buf;
51
52    /**
53     * The total number of bytes inside the byte array {@code buf}.
54     *
55     * @since Android 1.0
56     */
57    protected int count;
58
59    /**
60     * Constructs a new {@code BufferedOutputStream} on the {@link OutputStream}
61     * {@code out}. The buffer size is set to the default value of 8 KB.
62     *
63     * @param out
64     *            the {@code OutputStream} for which write operations are
65     *            buffered.
66     * @since Android 1.0
67     */
68    public BufferedOutputStream(OutputStream out) {
69        super(out);
70        buf = new byte[8192];
71
72        // BEGIN android-added
73        /*
74         * For Android, we want to discourage the use of this constructor (with
75         * its arguably too-large default), so we note its use in the log. We
76         * don't disable it, nor do we alter the default, however, because we
77         * still aim to behave compatibly, and the default value, though not
78         * documented, is established by convention.
79         */
80        Logger.global.info(
81                "Default buffer size used in BufferedOutputStream " +
82                "constructor. It would be " +
83                "better to be explicit if a 8k buffer is required.");
84        // END android-added
85    }
86
87    /**
88     * Constructs a new {@code BufferedOutputStream} on the {@link OutputStream}
89     * {@code out}. The buffer size is set to {@code size}.
90     *
91     * @param out
92     *            the output stream for which write operations are buffered.
93     * @param size
94     *            the size of the buffer in bytes.
95     * @throws IllegalArgumentException
96     *             if {@code size <= 0}.
97     * @since Android 1.0
98     */
99    public BufferedOutputStream(OutputStream out, int size) {
100        super(out);
101        if (size <= 0) {
102            // K0058=size must be > 0
103            throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
104        }
105        buf = new byte[size];
106    }
107
108    /**
109     * Flushes this stream to ensure all pending data is written out to the
110     * target stream. In addition, the target stream is flushed.
111     *
112     * @throws IOException
113     *             if an error occurs attempting to flush this stream.
114     * @since Android 1.0
115     */
116    @Override
117    public synchronized void flush() throws IOException {
118        if (count > 0) {
119            out.write(buf, 0, count);
120        }
121        count = 0;
122        out.flush();
123    }
124
125    /**
126     * Writes {@code count} bytes from the byte array {@code buffer} starting at
127     * {@code offset} to this stream. If there is room in the buffer to hold the
128     * bytes, they are copied in. If not, the buffered bytes plus the bytes in
129     * {@code buffer} are written to the target stream, the target is flushed,
130     * and the buffer is cleared.
131     *
132     * @param buffer
133     *            the buffer to be written.
134     * @param offset
135     *            the start position in {@code buffer} from where to get bytes.
136     * @param length
137     *            the number of bytes from {@code buffer} to write to this
138     *            stream.
139     * @throws IndexOutOfBoundsException
140     *             if {@code offset < 0} or {@code length < 0}, or if
141     *             {@code offset + length} is greater than the size of
142     *             {@code buffer}.
143     * @throws IOException
144     *             if an error occurs attempting to write to this stream.
145     * @throws NullPointerException
146     *             if {@code buffer} is {@code null}.
147     * @since Android 1.0
148     */
149    @Override
150    public synchronized void write(byte[] buffer, int offset, int length)
151            throws IOException {
152        if (buffer == null) {
153            // K0047=buffer is null
154            throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
155        }
156        // BEGIN android-changed
157        // Exception priorities (in case of multiple errors) differ from
158        // RI, but are spec-compliant.
159        // used (offset | length) < 0 instead of (offset < 0) || (length < 0)
160        // to safe one operation
161        if ((offset | length) < 0 || offset > buffer.length - length) {
162            // K002f=Arguments out of bounds
163            throw new ArrayIndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
164        }
165        // END android-changed
166        if (count == 0 && length >= buf.length) {
167            out.write(buffer, offset, length);
168            return;
169        }
170        int available = buf.length - count;
171        if (length < available) {
172            available = length;
173        }
174        if (available > 0) {
175            System.arraycopy(buffer, offset, buf, count, available);
176            count += available;
177        }
178        if (count == buf.length) {
179            out.write(buf, 0, buf.length);
180            count = 0;
181            if (length > available) {
182                offset += available;
183                available = length - available;
184                if (available >= buf.length) {
185                    out.write(buffer, offset, available);
186                } else {
187                    System.arraycopy(buffer, offset, buf, count, available);
188                    count += available;
189                }
190            }
191        }
192    }
193
194    /**
195     * Writes one byte to this stream. Only the low order byte of the integer
196     * {@code oneByte} is written. If there is room in the buffer, the byte is
197     * copied into the buffer and the count incremented. Otherwise, the buffer
198     * plus {@code oneByte} are written to the target stream, the target is
199     * flushed, and the buffer is reset.
200     *
201     * @param oneByte
202     *            the byte to be written.
203     * @throws IOException
204     *             if an error occurs attempting to write to this stream.
205     * @since Android 1.0
206     */
207    @Override
208    public synchronized void write(int oneByte) throws IOException {
209        if (count == buf.length) {
210            out.write(buf, 0, count);
211            count = 0;
212        }
213        buf[count++] = (byte) oneByte;
214    }
215}
216