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