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