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