1163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey/* 2163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Copyright (C) 2011 The Android Open Source Project 3163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * 4163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * you may not use this file except in compliance with the License. 6163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * You may obtain a copy of the License at 7163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * 8163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * 10163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * See the License for the specific language governing permissions and 14163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * limitations under the License. 15163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 16163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 17163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeypackage com.android.internal.util; 18163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 19163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeyimport java.io.Closeable; 20163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeyimport java.io.IOException; 21163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeyimport java.io.InputStream; 22163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeyimport java.nio.charset.Charsets; 23163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 24163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey/** 25163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Reader that specializes in parsing {@code /proc/} files quickly. Walks 26163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * through the stream using a single space {@code ' '} as token separator, and 27163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * requires each line boundary to be explicitly acknowledged using 28163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * {@link #finishLine()}. Assumes {@link Charsets#US_ASCII} encoding. 29163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * <p> 30163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Currently doesn't support formats based on {@code \0}, tabs, or repeated 31163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * delimiters. 32163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 33163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeypublic class ProcFileReader implements Closeable { 34163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private final InputStream mStream; 35163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private final byte[] mBuffer; 36163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 37163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** Write pointer in {@link #mBuffer}. */ 38163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private int mTail; 39163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** Flag when last read token finished current line. */ 40163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private boolean mLineFinished; 41163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 42163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public ProcFileReader(InputStream stream) throws IOException { 43163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey this(stream, 4096); 44163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 45163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 46163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public ProcFileReader(InputStream stream, int bufferSize) throws IOException { 47163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mStream = stream; 48163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mBuffer = new byte[bufferSize]; 49163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 50163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // read enough to answer hasMoreData 51163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey fillBuf(); 52163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 53163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 54163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 55163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Read more data from {@link #mStream} into internal buffer. 56163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 57163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private int fillBuf() throws IOException { 58163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final int length = mBuffer.length - mTail; 59163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (length == 0) { 60163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw new IOException("attempting to fill already-full buffer"); 61163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 62163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 63163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final int read = mStream.read(mBuffer, mTail, length); 64163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (read != -1) { 65163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mTail += read; 66163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 67163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return read; 68163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 69163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 70163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 71163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Consume number of bytes from beginning of internal buffer. If consuming 72163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * all remaining bytes, will attempt to {@link #fillBuf()}. 73163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 74163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private void consumeBuf(int count) throws IOException { 75163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // TODO: consider moving to read pointer, but for now traceview says 76163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // these copies aren't a bottleneck. 77163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count); 78163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mTail -= count; 79163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (mTail == 0) { 80163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey fillBuf(); 81163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 82163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 83163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 84163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 85163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Find buffer index of next token delimiter, usually space or newline. Will 86163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * fill buffer as needed. 87163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 88163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private int nextTokenIndex() throws IOException { 89163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (mLineFinished) { 90163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw new IOException("no tokens remaining on current line"); 91163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 92163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 93163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey int i = 0; 94163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey do { 95163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // scan forward for token boundary 96163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey for (; i < mTail; i++) { 97163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final byte b = mBuffer[i]; 98163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (b == '\n') { 99163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mLineFinished = true; 100163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return i; 101163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 102163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (b == ' ') { 103163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return i; 104163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 105163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 106163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } while (fillBuf() > 0); 107163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 108163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw new IOException("end of stream while looking for token boundary"); 109163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 110163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 111163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 112163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Check if stream has more data to be parsed. 113163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 114163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public boolean hasMoreData() { 115163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return mTail > 0; 116163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 117163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 118163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 119163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Finish current line, skipping any remaining data. 120163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 121163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public void finishLine() throws IOException { 122163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // last token already finished line; reset silently 123163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (mLineFinished) { 124163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mLineFinished = false; 125163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return; 126163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 127163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 128163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey int i = 0; 129163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey do { 130163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // scan forward for line boundary and consume 131163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey for (; i < mTail; i++) { 132163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (mBuffer[i] == '\n') { 133163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey consumeBuf(i + 1); 134163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return; 135163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 136163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 137163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } while (fillBuf() > 0); 138163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 139163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw new IOException("end of stream while looking for line boundary"); 140163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 141163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 142163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 143163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Parse and return next token as {@link String}. 144163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 145163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public String nextString() throws IOException { 146163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final int tokenIndex = nextTokenIndex(); 147163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final String s = new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII); 148163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey consumeBuf(tokenIndex + 1); 149163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return s; 150163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 151163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 152163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 153163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Parse and return next token as base-10 encoded {@code long}. 154163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 155163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public long nextLong() throws IOException { 156163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final int tokenIndex = nextTokenIndex(); 157163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final boolean negative = mBuffer[0] == '-'; 158163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 159163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // TODO: refactor into something like IntegralToString 160163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey long result = 0; 161163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey for (int i = negative ? 1 : 0; i < tokenIndex; i++) { 162163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final int digit = mBuffer[i] - '0'; 163163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (digit < 0 || digit > 9) { 164163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw invalidLong(tokenIndex); 165163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 166163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 167163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // always parse as negative number and apply sign later; this 168163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey // correctly handles MIN_VALUE which is "larger" than MAX_VALUE. 169163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final long next = result * 10 - digit; 170163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (next > result) { 171163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw invalidLong(tokenIndex); 172163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 173163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey result = next; 174163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 175163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 176163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey consumeBuf(tokenIndex + 1); 177163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return negative ? result : -result; 178163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 179163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 180163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey private NumberFormatException invalidLong(int tokenIndex) { 181163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return new NumberFormatException( 182163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey "invalid long: " + new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII)); 183163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 184163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 185163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey /** 186163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Parse and return next token as base-10 encoded {@code int}. 187163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */ 188163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public int nextInt() throws IOException { 189163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey final long value = nextLong(); 190163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) { 191163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey throw new NumberFormatException("parsed value larger than integer"); 192163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 193163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey return (int) value; 194163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 195163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey 196163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey public void close() throws IOException { 197163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey mStream.close(); 198163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey } 199163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey} 200