1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.util.Log;
21 import android.util.Printer;
22 
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.io.PrintWriter;
26 import java.io.UnsupportedEncodingException;
27 import java.io.Writer;
28 import java.nio.ByteBuffer;
29 import java.nio.CharBuffer;
30 import java.nio.charset.Charset;
31 import java.nio.charset.CharsetEncoder;
32 import java.nio.charset.CoderResult;
33 import java.nio.charset.CodingErrorAction;
34 
35 public class FastPrintWriter extends PrintWriter {
36     private static class DummyWriter extends Writer {
37         @Override
close()38         public void close() throws IOException {
39             UnsupportedOperationException ex
40                     = new UnsupportedOperationException("Shouldn't be here");
41             throw ex;
42         }
43 
44         @Override
flush()45         public void flush() throws IOException {
46             close();
47         }
48 
49         @Override
write(char[] buf, int offset, int count)50         public void write(char[] buf, int offset, int count) throws IOException {
51             close();
52         }
53     };
54 
55     private final int mBufferLen;
56     private final char[] mText;
57     private int mPos;
58 
59     final private OutputStream mOutputStream;
60     final private boolean mAutoFlush;
61     final private String mSeparator;
62 
63     final private Writer mWriter;
64     final private Printer mPrinter;
65 
66     private CharsetEncoder mCharset;
67     final private ByteBuffer mBytes;
68 
69     private boolean mIoError;
70 
71     /**
72      * Constructs a new {@code PrintWriter} with {@code out} as its target
73      * stream. By default, the new print writer does not automatically flush its
74      * contents to the target stream when a newline is encountered.
75      *
76      * @param out
77      *            the target output stream.
78      * @throws NullPointerException
79      *             if {@code out} is {@code null}.
80      */
81     @UnsupportedAppUsage
FastPrintWriter(OutputStream out)82     public FastPrintWriter(OutputStream out) {
83         this(out, false, 8192);
84     }
85 
86     /**
87      * Constructs a new {@code PrintWriter} with {@code out} as its target
88      * stream. The parameter {@code autoFlush} determines if the print writer
89      * automatically flushes its contents to the target stream when a newline is
90      * encountered.
91      *
92      * @param out
93      *            the target output stream.
94      * @param autoFlush
95      *            indicates whether contents are flushed upon encountering a
96      *            newline sequence.
97      * @throws NullPointerException
98      *             if {@code out} is {@code null}.
99      */
FastPrintWriter(OutputStream out, boolean autoFlush)100     public FastPrintWriter(OutputStream out, boolean autoFlush) {
101         this(out, autoFlush, 8192);
102     }
103 
104     /**
105      * Constructs a new {@code PrintWriter} with {@code out} as its target
106      * stream and a custom buffer size. The parameter {@code autoFlush} determines
107      * if the print writer automatically flushes its contents to the target stream
108      * when a newline is encountered.
109      *
110      * @param out
111      *            the target output stream.
112      * @param autoFlush
113      *            indicates whether contents are flushed upon encountering a
114      *            newline sequence.
115      * @param bufferLen
116      *            specifies the size of the FastPrintWriter's internal buffer; the
117      *            default is 8192.
118      * @throws NullPointerException
119      *             if {@code out} is {@code null}.
120      */
FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen)121     public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
122         super(new DummyWriter(), autoFlush);
123         if (out == null) {
124             throw new NullPointerException("out is null");
125         }
126         mBufferLen = bufferLen;
127         mText = new char[bufferLen];
128         mBytes = ByteBuffer.allocate(mBufferLen);
129         mOutputStream = out;
130         mWriter = null;
131         mPrinter = null;
132         mAutoFlush = autoFlush;
133         mSeparator = System.lineSeparator();
134         initDefaultEncoder();
135     }
136 
137     /**
138      * Constructs a new {@code PrintWriter} with {@code wr} as its target
139      * writer. By default, the new print writer does not automatically flush its
140      * contents to the target writer when a newline is encountered.
141      *
142      * <p>NOTE: Unlike PrintWriter, this version will still do buffering inside of
143      * FastPrintWriter before sending data to the Writer.  This means you must call
144      * flush() before retrieving any data from the Writer.</p>
145      *
146      * @param wr
147      *            the target writer.
148      * @throws NullPointerException
149      *             if {@code wr} is {@code null}.
150      */
FastPrintWriter(Writer wr)151     public FastPrintWriter(Writer wr) {
152         this(wr, false, 8192);
153     }
154 
155     /**
156      * Constructs a new {@code PrintWriter} with {@code wr} as its target
157      * writer. The parameter {@code autoFlush} determines if the print writer
158      * automatically flushes its contents to the target writer when a newline is
159      * encountered.
160      *
161      * @param wr
162      *            the target writer.
163      * @param autoFlush
164      *            indicates whether to flush contents upon encountering a
165      *            newline sequence.
166      * @throws NullPointerException
167      *             if {@code out} is {@code null}.
168      */
FastPrintWriter(Writer wr, boolean autoFlush)169     public FastPrintWriter(Writer wr, boolean autoFlush) {
170         this(wr, autoFlush, 8192);
171     }
172 
173     /**
174      * Constructs a new {@code PrintWriter} with {@code wr} as its target
175      * writer and a custom buffer size. The parameter {@code autoFlush} determines
176      * if the print writer automatically flushes its contents to the target writer
177      * when a newline is encountered.
178      *
179      * @param wr
180      *            the target writer.
181      * @param autoFlush
182      *            indicates whether to flush contents upon encountering a
183      *            newline sequence.
184      * @param bufferLen
185      *            specifies the size of the FastPrintWriter's internal buffer; the
186      *            default is 8192.
187      * @throws NullPointerException
188      *             if {@code wr} is {@code null}.
189      */
FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen)190     public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) {
191         super(new DummyWriter(), autoFlush);
192         if (wr == null) {
193             throw new NullPointerException("wr is null");
194         }
195         mBufferLen = bufferLen;
196         mText = new char[bufferLen];
197         mBytes = null;
198         mOutputStream = null;
199         mWriter = wr;
200         mPrinter = null;
201         mAutoFlush = autoFlush;
202         mSeparator = System.lineSeparator();
203         initDefaultEncoder();
204     }
205 
206     /**
207      * Constructs a new {@code PrintWriter} with {@code pr} as its target
208      * printer and the default buffer size.  Because a {@link Printer} is line-base,
209      * autoflush is always enabled.
210      *
211      * @param pr
212      *            the target writer.
213      * @throws NullPointerException
214      *             if {@code pr} is {@code null}.
215      */
FastPrintWriter(Printer pr)216     public FastPrintWriter(Printer pr) {
217         this(pr, 512);
218     }
219 
220     /**
221      * Constructs a new {@code PrintWriter} with {@code pr} as its target
222      * printer and a custom buffer size.  Because a {@link Printer} is line-base,
223      * autoflush is always enabled.
224      *
225      * @param pr
226      *            the target writer.
227      * @param bufferLen
228      *            specifies the size of the FastPrintWriter's internal buffer; the
229      *            default is 512.
230      * @throws NullPointerException
231      *             if {@code pr} is {@code null}.
232      */
FastPrintWriter(Printer pr, int bufferLen)233     public FastPrintWriter(Printer pr, int bufferLen) {
234         super(new DummyWriter(), true);
235         if (pr == null) {
236             throw new NullPointerException("pr is null");
237         }
238         mBufferLen = bufferLen;
239         mText = new char[bufferLen];
240         mBytes = null;
241         mOutputStream = null;
242         mWriter = null;
243         mPrinter = pr;
244         mAutoFlush = true;
245         mSeparator = System.lineSeparator();
246         initDefaultEncoder();
247     }
248 
initEncoder(String csn)249     private final void initEncoder(String csn) throws UnsupportedEncodingException {
250         try {
251             mCharset = Charset.forName(csn).newEncoder();
252         } catch (Exception e) {
253             throw new UnsupportedEncodingException(csn);
254         }
255         mCharset.onMalformedInput(CodingErrorAction.REPLACE);
256         mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
257     }
258 
259     /**
260      * Flushes this writer and returns the value of the error flag.
261      *
262      * @return {@code true} if either an {@code IOException} has been thrown
263      *         previously or if {@code setError()} has been called;
264      *         {@code false} otherwise.
265      * @see #setError()
266      */
checkError()267     public boolean checkError() {
268         flush();
269         synchronized (lock) {
270             return mIoError;
271         }
272     }
273 
274     /**
275      * Sets the error state of the stream to false.
276      * @since 1.6
277      */
clearError()278     protected void clearError() {
279         synchronized (lock) {
280             mIoError = false;
281         }
282     }
283 
284     /**
285      * Sets the error flag of this writer to true.
286      */
setError()287     protected void setError() {
288         synchronized (lock) {
289             mIoError = true;
290         }
291     }
292 
initDefaultEncoder()293     private final void initDefaultEncoder() {
294         mCharset = Charset.defaultCharset().newEncoder();
295         mCharset.onMalformedInput(CodingErrorAction.REPLACE);
296         mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
297     }
298 
appendLocked(char c)299     private void appendLocked(char c) throws IOException {
300         int pos = mPos;
301         if (pos >= (mBufferLen-1)) {
302             flushLocked();
303             pos = mPos;
304         }
305         mText[pos] = c;
306         mPos = pos+1;
307     }
308 
appendLocked(String str, int i, final int length)309     private void appendLocked(String str, int i, final int length) throws IOException {
310         final int BUFFER_LEN = mBufferLen;
311         if (length > BUFFER_LEN) {
312             final int end = i + length;
313             while (i < end) {
314                 int next = i + BUFFER_LEN;
315                 appendLocked(str, i, next < end ? BUFFER_LEN : (end - i));
316                 i = next;
317             }
318             return;
319         }
320         int pos = mPos;
321         if ((pos+length) > BUFFER_LEN) {
322             flushLocked();
323             pos = mPos;
324         }
325         str.getChars(i, i + length, mText, pos);
326         mPos = pos + length;
327     }
328 
appendLocked(char[] buf, int i, final int length)329     private void appendLocked(char[] buf, int i, final int length) throws IOException {
330         final int BUFFER_LEN = mBufferLen;
331         if (length > BUFFER_LEN) {
332             final int end = i + length;
333             while (i < end) {
334                 int next = i + BUFFER_LEN;
335                 appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i));
336                 i = next;
337             }
338             return;
339         }
340         int pos = mPos;
341         if ((pos+length) > BUFFER_LEN) {
342             flushLocked();
343             pos = mPos;
344         }
345         System.arraycopy(buf, i, mText, pos, length);
346         mPos = pos + length;
347     }
348 
flushBytesLocked()349     private void flushBytesLocked() throws IOException {
350         if (!mIoError) {
351             int position;
352             if ((position = mBytes.position()) > 0) {
353                 mBytes.flip();
354                 mOutputStream.write(mBytes.array(), 0, position);
355                 mBytes.clear();
356             }
357         }
358     }
359 
flushLocked()360     private void flushLocked() throws IOException {
361         //Log.i("PackageManager", "flush mPos=" + mPos);
362         if (mPos > 0) {
363             if (mOutputStream != null) {
364                 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
365                 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
366                 while (!mIoError) {
367                     if (result.isError()) {
368                         throw new IOException(result.toString());
369                     } else if (result.isOverflow()) {
370                         flushBytesLocked();
371                         result = mCharset.encode(charBuffer, mBytes, true);
372                         continue;
373                     }
374                     break;
375                 }
376                 if (!mIoError) {
377                     flushBytesLocked();
378                     mOutputStream.flush();
379                 }
380             } else if (mWriter != null) {
381                 if (!mIoError) {
382                     mWriter.write(mText, 0, mPos);
383                     mWriter.flush();
384                 }
385             } else {
386                 int nonEolOff = 0;
387                 final int sepLen = mSeparator.length();
388                 final int len = sepLen < mPos ? sepLen : mPos;
389                 while (nonEolOff < len && mText[mPos-1-nonEolOff]
390                         == mSeparator.charAt(mSeparator.length()-1-nonEolOff)) {
391                     nonEolOff++;
392                 }
393                 if (nonEolOff >= mPos) {
394                     mPrinter.println("");
395                 } else {
396                     mPrinter.println(new String(mText, 0, mPos-nonEolOff));
397                 }
398             }
399             mPos = 0;
400         }
401     }
402 
403     /**
404      * Ensures that all pending data is sent out to the target. It also
405      * flushes the target. If an I/O error occurs, this writer's error
406      * state is set to {@code true}.
407      */
408     @Override
409     public void flush() {
410         synchronized (lock) {
411             try {
412                 flushLocked();
413                 if (!mIoError) {
414                     if (mOutputStream != null) {
415                         mOutputStream.flush();
416                     } else if (mWriter != null) {
417                         mWriter.flush();
418                     }
419                 }
420             } catch (IOException e) {
421                 Log.w("FastPrintWriter", "Write failure", e);
422                 setError();
423             }
424         }
425     }
426 
427     @Override
428     public void close() {
429         synchronized (lock) {
430             try {
431                 flushLocked();
432                 if (mOutputStream != null) {
433                     mOutputStream.close();
434                 } else if (mWriter != null) {
435                     mWriter.close();
436                 }
437             } catch (IOException e) {
438                 Log.w("FastPrintWriter", "Write failure", e);
439                 setError();
440             }
441         }
442     }
443 
444     /**
445      * Prints the string representation of the specified character array
446      * to the target.
447      *
448      * @param charArray
449      *            the character array to print to the target.
450      * @see #print(String)
451      */
452     public void print(char[] charArray) {
453         synchronized (lock) {
454             try {
455                 appendLocked(charArray, 0, charArray.length);
456             } catch (IOException e) {
457                 Log.w("FastPrintWriter", "Write failure", e);
458                 setError();
459             }
460         }
461     }
462 
463     /**
464      * Prints the string representation of the specified character to the
465      * target.
466      *
467      * @param ch
468      *            the character to print to the target.
469      * @see #print(String)
470      */
471     public void print(char ch) {
472         synchronized (lock) {
473             try {
474                 appendLocked(ch);
475             } catch (IOException e) {
476                 Log.w("FastPrintWriter", "Write failure", e);
477                 setError();
478             }
479         }
480     }
481 
482     /**
483      * Prints a string to the target. The string is converted to an array of
484      * bytes using the encoding chosen during the construction of this writer.
485      * The bytes are then written to the target with {@code write(int)}.
486      * <p>
487      * If an I/O error occurs, this writer's error flag is set to {@code true}.
488      *
489      * @param str
490      *            the string to print to the target.
491      * @see #write(int)
492      */
493     public void print(String str) {
494         if (str == null) {
495             str = String.valueOf((Object) null);
496         }
497         synchronized (lock) {
498             try {
499                 appendLocked(str, 0, str.length());
500             } catch (IOException e) {
501                 Log.w("FastPrintWriter", "Write failure", e);
502                 setError();
503             }
504         }
505     }
506 
507 
508     @Override
509     public void print(int inum) {
510         if (inum == 0) {
511             print("0");
512         } else {
513             super.print(inum);
514         }
515     }
516 
517     @Override
518     public void print(long lnum) {
519         if (lnum == 0) {
520             print("0");
521         } else {
522             super.print(lnum);
523         }
524     }
525 
526     /**
527      * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}.
528      */
529     public void println() {
530         synchronized (lock) {
531             try {
532                 appendLocked(mSeparator, 0, mSeparator.length());
533                 if (mAutoFlush) {
534                     flushLocked();
535                 }
536             } catch (IOException e) {
537                 Log.w("FastPrintWriter", "Write failure", e);
538                 setError();
539             }
540         }
541     }
542 
543     @Override
544     public void println(int inum) {
545         if (inum == 0) {
546             println("0");
547         } else {
548             super.println(inum);
549         }
550     }
551 
552     @Override
553     public void println(long lnum) {
554         if (lnum == 0) {
555             println("0");
556         } else {
557             super.println(lnum);
558         }
559     }
560 
561     /**
562      * Prints the string representation of the character array {@code chars} followed by a newline.
563      * Flushes this writer if the autoFlush flag is set to {@code true}.
564      */
565     public void println(char[] chars) {
566         print(chars);
567         println();
568     }
569 
570     /**
571      * Prints the string representation of the char {@code c} followed by a newline.
572      * Flushes this writer if the autoFlush flag is set to {@code true}.
573      */
574     public void println(char c) {
575         print(c);
576         println();
577     }
578 
579     /**
580      * Writes {@code count} characters from {@code buffer} starting at {@code
581      * offset} to the target.
582      * <p>
583      * This writer's error flag is set to {@code true} if this writer is closed
584      * or an I/O error occurs.
585      *
586      * @param buf
587      *            the buffer to write to the target.
588      * @param offset
589      *            the index of the first character in {@code buffer} to write.
590      * @param count
591      *            the number of characters in {@code buffer} to write.
592      * @throws IndexOutOfBoundsException
593      *             if {@code offset < 0} or {@code count < 0}, or if {@code
594      *             offset + count} is greater than the length of {@code buf}.
595      */
596     @Override
597     public void write(char[] buf, int offset, int count) {
598         synchronized (lock) {
599             try {
600                 appendLocked(buf, offset, count);
601             } catch (IOException e) {
602                 Log.w("FastPrintWriter", "Write failure", e);
603                 setError();
604             }
605         }
606     }
607 
608     /**
609      * Writes one character to the target. Only the two least significant bytes
610      * of the integer {@code oneChar} are written.
611      * <p>
612      * This writer's error flag is set to {@code true} if this writer is closed
613      * or an I/O error occurs.
614      *
615      * @param oneChar
616      *            the character to write to the target.
617      */
618     @Override
619     public void write(int oneChar) {
620         synchronized (lock) {
621             try {
622                 appendLocked((char) oneChar);
623             } catch (IOException e) {
624                 Log.w("FastPrintWriter", "Write failure", e);
625                 setError();
626             }
627         }
628     }
629 
630     /**
631      * Writes the characters from the specified string to the target.
632      *
633      * @param str
634      *            the non-null string containing the characters to write.
635      */
636     @Override
637     public void write(String str) {
638         synchronized (lock) {
639             try {
640                 appendLocked(str, 0, str.length());
641             } catch (IOException e) {
642                 Log.w("FastPrintWriter", "Write failure", e);
643                 setError();
644             }
645         }
646     }
647 
648     /**
649      * Writes {@code count} characters from {@code str} starting at {@code
650      * offset} to the target.
651      *
652      * @param str
653      *            the non-null string containing the characters to write.
654      * @param offset
655      *            the index of the first character in {@code str} to write.
656      * @param count
657      *            the number of characters from {@code str} to write.
658      * @throws IndexOutOfBoundsException
659      *             if {@code offset < 0} or {@code count < 0}, or if {@code
660      *             offset + count} is greater than the length of {@code str}.
661      */
662     @Override
663     public void write(String str, int offset, int count) {
664         synchronized (lock) {
665             try {
666                 appendLocked(str, offset, count);
667             } catch (IOException e) {
668                 Log.w("FastPrintWriter", "Write failure", e);
669                 setError();
670             }
671         }
672     }
673 
674     /**
675      * Appends a subsequence of the character sequence {@code csq} to the
676      * target. This method works the same way as {@code
677      * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
678      * csq} is {@code null}, then the specified subsequence of the string "null"
679      * will be written to the target.
680      *
681      * @param csq
682      *            the character sequence appended to the target.
683      * @param start
684      *            the index of the first char in the character sequence appended
685      *            to the target.
686      * @param end
687      *            the index of the character following the last character of the
688      *            subsequence appended to the target.
689      * @return this writer.
690      * @throws StringIndexOutOfBoundsException
691      *             if {@code start > end}, {@code start < 0}, {@code end < 0} or
692      *             either {@code start} or {@code end} are greater or equal than
693      *             the length of {@code csq}.
694      */
695     @Override
696     public PrintWriter append(CharSequence csq, int start, int end) {
697         if (csq == null) {
698             csq = "null";
699         }
700         String output = csq.subSequence(start, end).toString();
701         write(output, 0, output.length());
702         return this;
703     }
704 }
705