FileDocCategorySizeDatePackage
ProcFileReader.javaAPI DocAndroid 5.1 API7336Thu Mar 12 22:22:10 GMT 2015com.android.internal.util

ProcFileReader

public class ProcFileReader extends Object implements Closeable
Reader that specializes in parsing {@code /proc/} files quickly. Walks through the stream using a single space {@code ' '} as token separator, and requires each line boundary to be explicitly acknowledged using {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.

Currently doesn't support formats based on {@code \0}, tabs, or repeated delimiters.

Fields Summary
private final InputStream
mStream
private final byte[]
mBuffer
private int
mTail
Write pointer in {@link #mBuffer}.
private boolean
mLineFinished
Flag when last read token finished current line.
Constructors Summary
public ProcFileReader(InputStream stream)

        this(stream, 4096);
    
public ProcFileReader(InputStream stream, int bufferSize)

        mStream = stream;
        mBuffer = new byte[bufferSize];

        // read enough to answer hasMoreData
        fillBuf();
    
Methods Summary
public voidclose()

        mStream.close();
    
private voidconsumeBuf(int count)
Consume number of bytes from beginning of internal buffer. If consuming all remaining bytes, will attempt to {@link #fillBuf()}.

        // TODO: consider moving to read pointer, but for now traceview says
        // these copies aren't a bottleneck.
        System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
        mTail -= count;
        if (mTail == 0) {
            fillBuf();
        }
    
private intfillBuf()
Read more data from {@link #mStream} into internal buffer.

        final int length = mBuffer.length - mTail;
        if (length == 0) {
            throw new IOException("attempting to fill already-full buffer");
        }

        final int read = mStream.read(mBuffer, mTail, length);
        if (read != -1) {
            mTail += read;
        }
        return read;
    
public voidfinishLine()
Finish current line, skipping any remaining data.

        // last token already finished line; reset silently
        if (mLineFinished) {
            mLineFinished = false;
            return;
        }

        int i = 0;
        do {
            // scan forward for line boundary and consume
            for (; i < mTail; i++) {
                if (mBuffer[i] == '\n") {
                    consumeBuf(i + 1);
                    return;
                }
            }
        } while (fillBuf() > 0);

        throw new ProtocolException("End of stream while looking for line boundary");
    
public booleanhasMoreData()
Check if stream has more data to be parsed.

        return mTail > 0;
    
private java.lang.NumberFormatExceptioninvalidLong(int tokenIndex)

        return new NumberFormatException(
                "invalid long: " + new String(mBuffer, 0, tokenIndex, StandardCharsets.US_ASCII));
    
public intnextInt()
Parse and return next token as base-10 encoded {@code int}.

        final long value = nextLong();
        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
            throw new NumberFormatException("parsed value larger than integer");
        }
        return (int) value;
    
public longnextLong()
Parse and return next token as base-10 encoded {@code long}.

        final int tokenIndex = nextTokenIndex();
        if (tokenIndex == -1) {
            throw new ProtocolException("Missing required long");
        } else {
            return parseAndConsumeLong(tokenIndex);
        }
    
public longnextOptionalLong(long def)
Parse and return next token as base-10 encoded {@code long}, or return the given default value if no remaining tokens on current line.

        final int tokenIndex = nextTokenIndex();
        if (tokenIndex == -1) {
            return def;
        } else {
            return parseAndConsumeLong(tokenIndex);
        }
    
public java.lang.StringnextString()
Parse and return next token as {@link String}.

        final int tokenIndex = nextTokenIndex();
        if (tokenIndex == -1) {
            throw new ProtocolException("Missing required string");
        } else {
            return parseAndConsumeString(tokenIndex);
        }
    
private intnextTokenIndex()
Find buffer index of next token delimiter, usually space or newline. Fills buffer as needed.

return
Index of next delimeter, otherwise -1 if no tokens remain on current line.

        if (mLineFinished) {
            return -1;
        }

        int i = 0;
        do {
            // scan forward for token boundary
            for (; i < mTail; i++) {
                final byte b = mBuffer[i];
                if (b == '\n") {
                    mLineFinished = true;
                    return i;
                }
                if (b == ' ") {
                    return i;
                }
            }
        } while (fillBuf() > 0);

        throw new ProtocolException("End of stream while looking for token boundary");
    
private longparseAndConsumeLong(int tokenIndex)

        final boolean negative = mBuffer[0] == '-";

        // TODO: refactor into something like IntegralToString
        long result = 0;
        for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
            final int digit = mBuffer[i] - '0";
            if (digit < 0 || digit > 9) {
                throw invalidLong(tokenIndex);
            }

            // always parse as negative number and apply sign later; this
            // correctly handles MIN_VALUE which is "larger" than MAX_VALUE.
            final long next = result * 10 - digit;
            if (next > result) {
                throw invalidLong(tokenIndex);
            }
            result = next;
        }

        consumeBuf(tokenIndex + 1);
        return negative ? result : -result;
    
private java.lang.StringparseAndConsumeString(int tokenIndex)

        final String s = new String(mBuffer, 0, tokenIndex, StandardCharsets.US_ASCII);
        consumeBuf(tokenIndex + 1);
        return s;