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;
228e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkeyimport java.net.ProtocolException;
23d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
24163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
25163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey/**
26163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Reader that specializes in parsing {@code /proc/} files quickly. Walks
27163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * through the stream using a single space {@code ' '} as token separator, and
28163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * requires each line boundary to be explicitly acknowledged using
29d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes * {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.
30163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * <p>
31163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * Currently doesn't support formats based on {@code \0}, tabs, or repeated
32163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey * delimiters.
33163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey */
34163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkeypublic class ProcFileReader implements Closeable {
35163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private final InputStream mStream;
36163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private final byte[] mBuffer;
37163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
38163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /** Write pointer in {@link #mBuffer}. */
39163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private int mTail;
40163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /** Flag when last read token finished current line. */
41163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private boolean mLineFinished;
42163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
43163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public ProcFileReader(InputStream stream) throws IOException {
44163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        this(stream, 4096);
45163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
46163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
47163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public ProcFileReader(InputStream stream, int bufferSize) throws IOException {
48163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        mStream = stream;
49163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        mBuffer = new byte[bufferSize];
50163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
51163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        // read enough to answer hasMoreData
52163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        fillBuf();
53163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
54163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
55163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
56163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Read more data from {@link #mStream} into internal buffer.
57163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
58163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private int fillBuf() throws IOException {
59163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final int length = mBuffer.length - mTail;
60163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (length == 0) {
61163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            throw new IOException("attempting to fill already-full buffer");
62163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
63163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
64163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final int read = mStream.read(mBuffer, mTail, length);
65163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (read != -1) {
66163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            mTail += read;
67163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
68163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        return read;
69163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
70163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
71163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
72163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Consume number of bytes from beginning of internal buffer. If consuming
73163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * all remaining bytes, will attempt to {@link #fillBuf()}.
74163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
75163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private void consumeBuf(int count) throws IOException {
76163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        // TODO: consider moving to read pointer, but for now traceview says
77163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        // these copies aren't a bottleneck.
78163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
79163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        mTail -= count;
80163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (mTail == 0) {
81163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            fillBuf();
82163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
83163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
84163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
85163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
868e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     * Find buffer index of next token delimiter, usually space or newline.
878e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     * Fills buffer as needed.
888e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     *
898e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     * @return Index of next delimeter, otherwise -1 if no tokens remain on
908e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     *         current line.
91163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
92163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private int nextTokenIndex() throws IOException {
93163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (mLineFinished) {
948e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            return -1;
95163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
96163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
97163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        int i = 0;
98163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        do {
99163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            // scan forward for token boundary
100163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            for (; i < mTail; i++) {
101163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                final byte b = mBuffer[i];
102163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                if (b == '\n') {
103163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                    mLineFinished = true;
104163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                    return i;
105163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                }
106163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                if (b == ' ') {
107163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                    return i;
108163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                }
109163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            }
110163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        } while (fillBuf() > 0);
111163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
1128e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        throw new ProtocolException("End of stream while looking for token boundary");
113163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
114163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
115163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
116163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Check if stream has more data to be parsed.
117163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
118163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public boolean hasMoreData() {
119163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        return mTail > 0;
120163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
121163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
122163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
123163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Finish current line, skipping any remaining data.
124163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
125163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public void finishLine() throws IOException {
126163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        // last token already finished line; reset silently
127163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (mLineFinished) {
128163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            mLineFinished = false;
129163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            return;
130163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
131163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
132163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        int i = 0;
133163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        do {
134163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            // scan forward for line boundary and consume
135163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            for (; i < mTail; i++) {
136163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                if (mBuffer[i] == '\n') {
137163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                    consumeBuf(i + 1);
138163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                    return;
139163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                }
140163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            }
141163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        } while (fillBuf() > 0);
142163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
1438e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        throw new ProtocolException("End of stream while looking for line boundary");
144163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
145163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
146163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
147163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Parse and return next token as {@link String}.
148163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
149163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public String nextString() throws IOException {
150163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final int tokenIndex = nextTokenIndex();
1518e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        if (tokenIndex == -1) {
1528e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            throw new ProtocolException("Missing required string");
1538e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        } else {
1548e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            return parseAndConsumeString(tokenIndex);
1558e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        }
156163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
157163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
158163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
159163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Parse and return next token as base-10 encoded {@code long}.
160163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
161163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public long nextLong() throws IOException {
162163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final int tokenIndex = nextTokenIndex();
1638e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        if (tokenIndex == -1) {
1648e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            throw new ProtocolException("Missing required long");
1658e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        } else {
1668e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            return parseAndConsumeLong(tokenIndex);
1678e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        }
1688e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    }
1698e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey
1708e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    /**
1718e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     * Parse and return next token as base-10 encoded {@code long}, or return
1728e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     * the given default value if no remaining tokens on current line.
1738e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey     */
1748e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    public long nextOptionalLong(long def) throws IOException {
1758e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        final int tokenIndex = nextTokenIndex();
1768e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        if (tokenIndex == -1) {
1778e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            return def;
1788e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        } else {
1798e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey            return parseAndConsumeLong(tokenIndex);
1808e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        }
1818e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    }
1828e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey
1838e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    private String parseAndConsumeString(int tokenIndex) throws IOException {
184a920f25fe55fc9afc7640902a200f19ce278588bElliott Hughes        final String s = new String(mBuffer, 0, tokenIndex, StandardCharsets.US_ASCII);
1858e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        consumeBuf(tokenIndex + 1);
1868e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey        return s;
1878e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    }
1888e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey
1898e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    private long parseAndConsumeLong(int tokenIndex) throws IOException {
190163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final boolean negative = mBuffer[0] == '-';
191163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
192163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        // TODO: refactor into something like IntegralToString
193163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        long result = 0;
194163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
195163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            final int digit = mBuffer[i] - '0';
196163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            if (digit < 0 || digit > 9) {
197163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                throw invalidLong(tokenIndex);
198163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            }
199163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
200163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            // always parse as negative number and apply sign later; this
201163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            // correctly handles MIN_VALUE which is "larger" than MAX_VALUE.
202163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            final long next = result * 10 - digit;
203163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            if (next > result) {
204163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey                throw invalidLong(tokenIndex);
205163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            }
206163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            result = next;
207163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
208163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
209163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        consumeBuf(tokenIndex + 1);
210163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        return negative ? result : -result;
211163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
212163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
213163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    private NumberFormatException invalidLong(int tokenIndex) {
214163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        return new NumberFormatException(
215d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                "invalid long: " + new String(mBuffer, 0, tokenIndex, StandardCharsets.US_ASCII));
216163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
217163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
218163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    /**
219163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     * Parse and return next token as base-10 encoded {@code int}.
220163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey     */
221163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public int nextInt() throws IOException {
222163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        final long value = nextLong();
223163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
224163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey            throw new NumberFormatException("parsed value larger than integer");
225163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        }
226163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        return (int) value;
227163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
228163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey
2298e7b3b15f7a4f888d55f080d3ed61860cd5fab55Jeff Sharkey    @Override
230163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    public void close() throws IOException {
231163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey        mStream.close();
232163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey    }
233163e6443f27884a9bfcb9a48ef606dc635852c23Jeff Sharkey}
234