Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db4e33f1 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Faster PrintWriter.

Change-Id: I4e1ae7e0357214deb7e19ae65f8701b69b347c6e
parent f35f56a1
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.os;

import android.util.Log;
import com.android.internal.util.FastPrintWriter;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -288,7 +289,7 @@ public class Binder implements IBinder {
     */
    public void dump(FileDescriptor fd, String[] args) {
        FileOutputStream fout = new FileOutputStream(fd);
        PrintWriter pw = new PrintWriter(fout);
        PrintWriter pw = new FastPrintWriter(fout);
        try {
            final String disabled;
            synchronized (Binder.class) {
@@ -310,7 +311,7 @@ public class Binder implements IBinder {
     */
    public void dumpAsync(final FileDescriptor fd, final String[] args) {
        final FileOutputStream fout = new FileOutputStream(fd);
        final PrintWriter pw = new PrintWriter(fout);
        final PrintWriter pw = new FastPrintWriter(fout);
        Thread thr = new Thread("Binder.dumpAsync") {
            public void run() {
                try {
+478 −0
Original line number Diff line number Diff line
package com.android.internal.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;

public class FastPrintWriter extends PrintWriter {
    private static final int BUFFER_LEN = 8192;

    private final char[] mText = new char[BUFFER_LEN];
    private int mPos;

    final private OutputStream mOutputStream;
    private CharsetEncoder mCharset;
    final private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * stream. By default, the new print writer does not automatically flush its
     * contents to the target stream when a newline is encountered.
     *
     * @param out
     *            the target output stream.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    public FastPrintWriter(OutputStream out) {
        super(out);
        mOutputStream = out;
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * stream. The parameter {@code autoFlush} determines if the print writer
     * automatically flushes its contents to the target stream when a newline is
     * encountered.
     *
     * @param out
     *            the target output stream.
     * @param autoFlush
     *            indicates whether contents are flushed upon encountering a
     *            newline sequence.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    public FastPrintWriter(OutputStream out, boolean autoFlush) {
        super(out, autoFlush);
        mOutputStream = out;
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code wr} as its target
     * writer. By default, the new print writer does not automatically flush its
     * contents to the target writer when a newline is encountered.
     *
     * @param wr
     *            the target writer.
     * @throws NullPointerException
     *             if {@code wr} is {@code null}.
     */
    public FastPrintWriter(Writer wr) {
        super(wr);
        mOutputStream = null;
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code out} as its target
     * writer. The parameter {@code autoFlush} determines if the print writer
     * automatically flushes its contents to the target writer when a newline is
     * encountered.
     *
     * @param wr
     *            the target writer.
     * @param autoFlush
     *            indicates whether to flush contents upon encountering a
     *            newline sequence.
     * @throws NullPointerException
     *             if {@code out} is {@code null}.
     */
    public FastPrintWriter(Writer wr, boolean autoFlush) {
        super(wr, autoFlush);
        mOutputStream = null;
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code file} as its target. The
     * VM's default character set is used for character encoding.
     * The print writer does not automatically flush its contents to the target
     * file when a newline is encountered. The output to the file is buffered.
     *
     * @param file
     *            the target file. If the file already exists, its contents are
     *            removed, otherwise a new file is created.
     * @throws java.io.FileNotFoundException
     *             if an error occurs while opening or creating the target file.
     */
    public FastPrintWriter(File file) throws FileNotFoundException {
        super(file);
        mOutputStream = null;
        initDefaultEncoder();
    }

    /**
     * Constructs a new {@code PrintWriter} with {@code file} as its target. The
     * character set named {@code csn} is used for character encoding.
     * The print writer does not automatically flush its contents to the target
     * file when a newline is encountered. The output to the file is buffered.
     *
     * @param file
     *            the target file. If the file already exists, its contents are
     *            removed, otherwise a new file is created.
     * @param csn
     *            the name of the character set used for character encoding.
     * @throws FileNotFoundException
     *             if an error occurs while opening or creating the target file.
     * @throws NullPointerException
     *             if {@code csn} is {@code null}.
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding specified by {@code csn} is not supported.
     */
    public FastPrintWriter(File file, String csn) throws FileNotFoundException,
            UnsupportedEncodingException {
        super(file, csn);
        mOutputStream = null;
        initEncoder(csn);
    }

    /**
     * Constructs a new {@code PrintWriter} with the file identified by {@code
     * fileName} as its target. The VM's default character set is
     * used for character encoding. The print writer does not automatically
     * flush its contents to the target file when a newline is encountered. The
     * output to the file is buffered.
     *
     * @param fileName
     *            the target file's name. If the file already exists, its
     *            contents are removed, otherwise a new file is created.
     * @throws FileNotFoundException
     *             if an error occurs while opening or creating the target file.
     */
    public FastPrintWriter(String fileName) throws FileNotFoundException {
        super(fileName);
        mOutputStream = null;
        initDefaultEncoder();
    }

     /**
     * Constructs a new {@code PrintWriter} with the file identified by {@code
     * fileName} as its target. The character set named {@code csn} is used for
     * character encoding. The print writer does not automatically flush its
     * contents to the target file when a newline is encountered. The output to
     * the file is buffered.
     *
     * @param fileName
     *            the target file's name. If the file already exists, its
     *            contents are removed, otherwise a new file is created.
     * @param csn
     *            the name of the character set used for character encoding.
     * @throws FileNotFoundException
     *             if an error occurs while opening or creating the target file.
     * @throws NullPointerException
     *             if {@code csn} is {@code null}.
     * @throws UnsupportedEncodingException
     *             if the encoding specified by {@code csn} is not supported.
     */
    public FastPrintWriter(String fileName, String csn)
            throws FileNotFoundException, UnsupportedEncodingException {
        super(fileName, csn);
        mOutputStream = null;
        initEncoder(csn);
    }

    private final void initEncoder(String csn) throws UnsupportedEncodingException {
        try {
            mCharset = Charset.forName(csn).newEncoder();
        } catch (Exception e) {
            throw new UnsupportedEncodingException(csn);
        }
        mCharset.onMalformedInput(CodingErrorAction.REPLACE);
        mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
    }

    private final void initDefaultEncoder() {
        mCharset = Charset.defaultCharset().newEncoder();
        mCharset.onMalformedInput(CodingErrorAction.REPLACE);
        mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
    }

    private void appendInner(char c) throws IOException {
        int pos = mPos;
        if (pos >= (BUFFER_LEN-1)) {
            flush();
            pos = mPos;
        }
        mText[pos] = c;
        mPos = pos+1;
    }

    private void appendInner(String str, int i, final int length) throws IOException {
        if (length > BUFFER_LEN) {
            final int end = i + length;
            while (i < end) {
                int next = i + BUFFER_LEN;
                appendInner(str, i, next<end ? BUFFER_LEN : (end-i));
                i = next;
            }
            return;
        }
        int pos = mPos;
        if ((pos+length) > BUFFER_LEN) {
            flush();
            pos = mPos;
        }
        str.getChars(i, i + length, mText, pos);
        mPos = pos + length;
    }

    private void appendInner(char[] buf, int i, final int length) throws IOException {
        if (length > BUFFER_LEN) {
            final int end = i + length;
            while (i < end) {
                int next = i + BUFFER_LEN;
                appendInner(buf, i, next < end ? BUFFER_LEN : (end - i));
                i = next;
            }
            return;
        }
        int pos = mPos;
        if ((pos+length) > BUFFER_LEN) {
            flush();
            pos = mPos;
        }
        System.arraycopy(buf, i, mText, pos, length);
        mPos = pos + length;
    }

    private void flushBytesInner() throws IOException {
        int position;
        if ((position = mBytes.position()) > 0) {
            mBytes.flip();
            mOutputStream.write(mBytes.array(), 0, position);
            mBytes.clear();
        }
    }

    private void flushInner() throws IOException {
        //Log.i("PackageManager", "flush mPos=" + mPos);
        if (mPos > 0) {
            if (mOutputStream != null) {
                CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
                CoderResult result = mCharset.encode(charBuffer, mBytes, true);
                while (true) {
                    if (result.isError()) {
                        throw new IOException(result.toString());
                    } else if (result.isOverflow()) {
                        flushBytesInner();
                        result = mCharset.encode(charBuffer, mBytes, true);
                        continue;
                    }
                    break;
                }
                flushBytesInner();
                mOutputStream.flush();
            } else {
                out.write(mText, 0, mPos);
                out.flush();
            }
            mPos = 0;
        }
    }

    /**
     * Ensures that all pending data is sent out to the target. It also
     * flushes the target. If an I/O error occurs, this writer's error
     * state is set to {@code true}.
     */
    @Override
    public void flush() {
        try {
            flushInner();
        } catch (IOException e) {
        }
        super.flush();
    }

    /**
     * Prints the string representation of the specified character array
     * to the target.
     *
     * @param charArray
     *            the character array to print to the target.
     * @see #print(String)
     */
    public void print(char[] charArray) {
        try {
            appendInner(charArray, 0, charArray.length);
        } catch (IOException e) {
        }
    }

    /**
     * Prints the string representation of the specified character to the
     * target.
     *
     * @param ch
     *            the character to print to the target.
     * @see #print(String)
     */
    public void print(char ch) {
        try {
            appendInner(ch);
        } catch (IOException e) {
        }
    }

    /**
     * Prints a string to the target. The string is converted to an array of
     * bytes using the encoding chosen during the construction of this writer.
     * The bytes are then written to the target with {@code write(int)}.
     * <p>
     * If an I/O error occurs, this writer's error flag is set to {@code true}.
     *
     * @param str
     *            the string to print to the target.
     * @see #write(int)
     */
    public void print(String str) {
        if (str == null) {
            str = String.valueOf((Object) null);
        }
        try {
            appendInner(str, 0, str.length());
        } catch (IOException e) {
        }
    }

    /**
     * Prints the string representation of the character array {@code chars} followed by a newline.
     * Flushes this writer if the autoFlush flag is set to {@code true}.
     */
    public void println(char[] chars) {
        print(chars);
        println();
    }

    /**
     * Prints the string representation of the char {@code c} followed by a newline.
     * Flushes this writer if the autoFlush flag is set to {@code true}.
     */
    public void println(char c) {
        print(c);
        println();
    }

    /**
     * Writes {@code count} characters from {@code buffer} starting at {@code
     * offset} to the target.
     * <p>
     * This writer's error flag is set to {@code true} if this writer is closed
     * or an I/O error occurs.
     *
     * @param buf
     *            the buffer to write to the target.
     * @param offset
     *            the index of the first character in {@code buffer} to write.
     * @param count
     *            the number of characters in {@code buffer} to write.
     * @throws IndexOutOfBoundsException
     *             if {@code offset < 0} or {@code count < 0}, or if {@code
     *             offset + count} is greater than the length of {@code buf}.
     */
    @Override
    public void write(char[] buf, int offset, int count) {
        try {
            appendInner(buf, offset, count);
        } catch (IOException e) {
        }
    }

    /**
     * Writes one character to the target. Only the two least significant bytes
     * of the integer {@code oneChar} are written.
     * <p>
     * This writer's error flag is set to {@code true} if this writer is closed
     * or an I/O error occurs.
     *
     * @param oneChar
     *            the character to write to the target.
     */
    @Override
    public void write(int oneChar) {
        try {
            appendInner((char) oneChar);
        } catch (IOException e) {
        }
    }

    /**
     * Writes the characters from the specified string to the target.
     *
     * @param str
     *            the non-null string containing the characters to write.
     */
    @Override
    public void write(String str) {
        try {
            appendInner(str, 0, str.length());
        } catch (IOException e) {
        }
    }

    /**
     * Writes {@code count} characters from {@code str} starting at {@code
     * offset} to the target.
     *
     * @param str
     *            the non-null string containing the characters to write.
     * @param offset
     *            the index of the first character in {@code str} to write.
     * @param count
     *            the number of characters from {@code str} to write.
     * @throws IndexOutOfBoundsException
     *             if {@code offset < 0} or {@code count < 0}, or if {@code
     *             offset + count} is greater than the length of {@code str}.
     */
    @Override
    public void write(String str, int offset, int count) {
        try {
            appendInner(str, offset, count);
        } catch (IOException e) {
        }
    }

    /**
     * Appends a subsequence of the character sequence {@code csq} to the
     * target. This method works the same way as {@code
     * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
     * csq} is {@code null}, then the specified subsequence of the string "null"
     * will be written to the target.
     *
     * @param csq
     *            the character sequence appended to the target.
     * @param start
     *            the index of the first char in the character sequence appended
     *            to the target.
     * @param end
     *            the index of the character following the last character of the
     *            subsequence appended to the target.
     * @return this writer.
     * @throws StringIndexOutOfBoundsException
     *             if {@code start > end}, {@code start < 0}, {@code end < 0} or
     *             either {@code start} or {@code end} are greater or equal than
     *             the length of {@code csq}.
     */
    @Override
    public PrintWriter append(CharSequence csq, int start, int end) {
        if (csq == null) {
            csq = "null";
        }
        String output = csq.subSequence(start, end).toString();
        write(output, 0, output.length());
        return this;
    }
}