1 /*
  2  * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.io;
 27 
 28 import java.util.Arrays;
 29 import java.util.Objects;
 30 import jdk.internal.misc.InternalLock;
 31 import jdk.internal.misc.VM;
 32 
 33 /**
 34  * Writes text to a character-output stream, buffering characters so as to
 35  * provide for the efficient writing of single characters, arrays, and strings.
 36  *
 37  * <p> The buffer size may be specified, or the default size may be accepted.
 38  * The default is large enough for most purposes.
 39  *
 40  * <p> A newLine() method is provided, which uses the platform's own notion of
 41  * line separator as defined by the system property {@code line.separator}.
 42  * Not all platforms use the newline character ('\n') to terminate lines.
 43  * Calling this method to terminate each output line is therefore preferred to
 44  * writing a newline character directly.
 45  *
 46  * <p> In general, a Writer sends its output immediately to the underlying
 47  * character or byte stream.  Unless prompt output is required, it is advisable
 48  * to wrap a BufferedWriter around any Writer whose write() operations may be
 49  * costly, such as FileWriters and OutputStreamWriters.  For example,
 50  *
 51  * <pre>
 52  * PrintWriter out
 53  *   = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
 54  * </pre>
 55  *
 56  * will buffer the PrintWriter's output to the file.  Without buffering, each
 57  * invocation of a print() method would cause characters to be converted into
 58  * bytes that would then be written immediately to the file, which can be very
 59  * inefficient.
 60  *
 61  * @see PrintWriter
 62  * @see FileWriter
 63  * @see OutputStreamWriter
 64  * @see java.nio.file.Files#newBufferedWriter
 65  *
 66  * @author      Mark Reinhold
 67  * @since       1.1
 68  */
 69 
 70 public class BufferedWriter extends Writer {
 71     private static final int DEFAULT_INITIAL_BUFFER_SIZE = 512;
 72     private static final int DEFAULT_MAX_BUFFER_SIZE = 8192;
 73 
 74     private Writer out;
 75 
 76     private char cb[];
 77     private int nChars, nextChar;
 78     private final int maxChars;  // maximum number of buffers chars
 79 
 80     /**
 81      * Returns the buffer size to use when no output buffer size specified
 82      */
 83     private static int initialBufferSize() {
 84         if (VM.isBooted() && Thread.currentThread().isVirtual()) {
 85             return DEFAULT_INITIAL_BUFFER_SIZE;
 86         } else {
 87             return DEFAULT_MAX_BUFFER_SIZE;
 88         }
 89     }
 90 
 91     /**
 92      * Creates a buffered character-output stream.
 93      */
 94     private BufferedWriter(Writer out, int initialSize, int maxSize) {
 95         Objects.requireNonNull(out);
 96         if (initialSize <= 0) {
 97             throw new IllegalArgumentException("Buffer size <= 0");
 98         }
 99 
100         this.out = out;
101         this.cb = new char[initialSize];
102         this.nChars = initialSize;
103         this.maxChars = maxSize;
104     }
105 
106     /**
107      * Creates a buffered character-output stream that uses a default-sized
108      * output buffer.
109      *
110      * @param  out  A Writer
111      */
112     public BufferedWriter(Writer out) {
113         this(out, initialBufferSize(), DEFAULT_MAX_BUFFER_SIZE);
114     }
115 
116     /**
117      * Creates a new buffered character-output stream that uses an output
118      * buffer of the given size.
119      *
120      * @param  out  A Writer
121      * @param  sz   Output-buffer size, a positive integer
122      *
123      * @throws     IllegalArgumentException  If {@code sz <= 0}
124      */
125     public BufferedWriter(Writer out, int sz) {
126         this(out, sz, sz);
127     }
128 
129     /** Checks to make sure that the stream has not been closed */
130     private void ensureOpen() throws IOException {
131         if (out == null)
132             throw new IOException("Stream closed");
133     }
134 
135     /**
136      * Grow char array to fit an additional len characters if needed.
137      * If possible, it grows by len+1 to avoid flushing when len chars
138      * are added.
139      */
140     private void growIfNeeded(int len) {
141         int neededSize = nextChar + len + 1;
142         if (neededSize > nChars && nChars < maxChars) {
143             int newSize = min(neededSize, maxChars);
144             cb = Arrays.copyOf(cb, newSize);
145             nChars = newSize;
146         }
147     }
148 
149     /**
150      * Flushes the output buffer to the underlying character stream, without
151      * flushing the stream itself.  This method is non-private only so that it
152      * may be invoked by PrintStream.
153      */
154     void flushBuffer() throws IOException {
155         Object lock = this.lock;
156         if (lock instanceof InternalLock locker) {
157             locker.lock();
158             try {
159                 lockedFlushBuffer();
160             } finally {
161                 locker.unlock();
162             }
163         } else {
164             synchronized (lock) {
165                 lockedFlushBuffer();
166             }
167         }
168     }
169 
170     private void lockedFlushBuffer() throws IOException {
171         ensureOpen();
172         if (nextChar == 0)
173             return;
174         out.write(cb, 0, nextChar);
175         nextChar = 0;
176     }
177 
178     /**
179      * Writes a single character.
180      *
181      * @throws     IOException  If an I/O error occurs
182      */
183     public void write(int c) throws IOException {
184         Object lock = this.lock;
185         if (lock instanceof InternalLock locker) {
186             locker.lock();
187             try {
188                 lockedWrite(c);
189             } finally {
190                 locker.unlock();
191             }
192         } else {
193             synchronized (lock) {
194                 lockedWrite(c);
195             }
196         }
197     }
198 
199     private void lockedWrite(int c) throws IOException {
200         ensureOpen();
201         growIfNeeded(1);
202         if (nextChar >= nChars)
203             flushBuffer();
204         cb[nextChar++] = (char) c;
205     }
206 
207     /**
208      * Our own little min method, to avoid loading java.lang.Math if we've run
209      * out of file descriptors and we're trying to print a stack trace.
210      */
211     private int min(int a, int b) {
212         if (a < b) return a;
213         return b;
214     }
215 
216     /**
217      * Writes a portion of an array of characters.
218      *
219      * <p> Ordinarily this method stores characters from the given array into
220      * this stream's buffer, flushing the buffer to the underlying stream as
221      * needed.  If the requested length is at least as large as the buffer,
222      * however, then this method will flush the buffer and write the characters
223      * directly to the underlying stream.  Thus redundant
224      * {@code BufferedWriter}s will not copy data unnecessarily.
225      *
226      * @param  cbuf  A character array
227      * @param  off   Offset from which to start reading characters
228      * @param  len   Number of characters to write
229      *
230      * @throws  IndexOutOfBoundsException
231      *          If {@code off} is negative, or {@code len} is negative,
232      *          or {@code off + len} is negative or greater than the length
233      *          of the given array
234      *
235      * @throws  IOException  If an I/O error occurs
236      */
237     public void write(char[] cbuf, int off, int len) throws IOException {
238         Object lock = this.lock;
239         if (lock instanceof InternalLock locker) {
240             locker.lock();
241             try {
242                 lockedWrite(cbuf, off, len);
243             } finally {
244                 locker.unlock();
245             }
246         } else {
247             synchronized (lock) {
248                 lockedWrite(cbuf, off, len);
249             }
250         }
251     }
252 
253     private void lockedWrite(char[] cbuf, int off, int len) throws IOException {
254         ensureOpen();
255         Objects.checkFromIndexSize(off, len, cbuf.length);
256         if (len == 0) {
257             return;
258         }
259 
260         if (len >= maxChars) {
261             /* If the request length exceeds the max size of the output buffer,
262                flush the buffer and then write the data directly.  In this
263                way buffered streams will cascade harmlessly. */
264             flushBuffer();
265             out.write(cbuf, off, len);
266             return;
267         }
268 
269         growIfNeeded(len);
270         int b = off, t = off + len;
271         while (b < t) {
272             int d = min(nChars - nextChar, t - b);
273             System.arraycopy(cbuf, b, cb, nextChar, d);
274             b += d;
275             nextChar += d;
276             if (nextChar >= nChars) {
277                 flushBuffer();
278             }
279         }
280     }
281 
282     /**
283      * Writes a portion of a String.
284      *
285      * @implSpec
286      * While the specification of this method in the
287      * {@linkplain java.io.Writer#write(java.lang.String,int,int) superclass}
288      * recommends that an {@link IndexOutOfBoundsException} be thrown
289      * if {@code len} is negative or {@code off + len} is negative,
290      * the implementation in this class does not throw such an exception in
291      * these cases but instead simply writes no characters.
292      *
293      * @param  s     String to be written
294      * @param  off   Offset from which to start reading characters
295      * @param  len   Number of characters to be written
296      *
297      * @throws  IndexOutOfBoundsException
298      *          If {@code off} is negative,
299      *          or {@code off + len} is greater than the length
300      *          of the given string
301      *
302      * @throws  IOException  If an I/O error occurs
303      */
304     public void write(String s, int off, int len) throws IOException {
305         Object lock = this.lock;
306         if (lock instanceof InternalLock locker) {
307             locker.lock();
308             try {
309                 lockedWrite(s, off, len);
310             } finally {
311                 locker.unlock();
312             }
313         } else {
314             synchronized (lock) {
315                 lockedWrite(s, off, len);
316             }
317         }
318     }
319 
320     private void lockedWrite(String s, int off, int len) throws IOException {
321         ensureOpen();
322         growIfNeeded(len);
323         int b = off, t = off + len;
324         while (b < t) {
325             int d = min(nChars - nextChar, t - b);
326             s.getChars(b, b + d, cb, nextChar);
327             b += d;
328             nextChar += d;
329             if (nextChar >= nChars)
330                 flushBuffer();
331         }
332     }
333 
334     /**
335      * Writes a line separator.  The line separator string is defined by the
336      * system property {@code line.separator}, and is not necessarily a single
337      * newline ('\n') character.
338      *
339      * @throws     IOException  If an I/O error occurs
340      */
341     public void newLine() throws IOException {
342         write(System.lineSeparator());
343     }
344 
345     /**
346      * Flushes the stream.
347      *
348      * @throws     IOException  If an I/O error occurs
349      */
350     public void flush() throws IOException {
351         Object lock = this.lock;
352         if (lock instanceof InternalLock locker) {
353             locker.lock();
354             try {
355                 lockedFlush();
356             } finally {
357                 locker.unlock();
358             }
359         } else {
360             synchronized (lock) {
361                 lockedFlush();
362             }
363         }
364     }
365 
366     private void lockedFlush() throws IOException {
367         flushBuffer();
368         out.flush();
369     }
370 
371     public void close() throws IOException {
372         Object lock = this.lock;
373         if (lock instanceof InternalLock locker) {
374             locker.lock();
375             try {
376                 lockedClose();
377             } finally {
378                 locker.unlock();
379             }
380         } else {
381             synchronized (lock) {
382                 lockedClose();
383             }
384         }
385     }
386 
387     @SuppressWarnings("try")
388     private void lockedClose() throws IOException {
389         if (out == null) {
390             return;
391         }
392         try (Writer w = out) {
393             flushBuffer();
394         } finally {
395             out = null;
396             cb = null;
397         }
398     }
399 }