17899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/*
27899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Copyright (C) 2012 The Android Open Source Project
37899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
47899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
57899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * you may not use this file except in compliance with the License.
67899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * You may obtain a copy of the License at
77899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
87899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
97899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Unless required by applicable law or agreed to in writing, software
117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * See the License for the specific language governing permissions and
147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * limitations under the License.
157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal;
187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.ByteArrayOutputStream;
207899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.Closeable;
217899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.EOFException;
227899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.IOException;
237899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStream;
247899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.nio.charset.Charset;
257899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
2654cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.ISO_8859_1;
2754cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.US_ASCII;
2854cf3446000fdcf88a9e62724f7deb0282e98da1jwilsonimport static com.squareup.okhttp.internal.Util.UTF_8;
2954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/**
317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Buffers input from an {@link InputStream} for reading lines.
327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class is used for buffered reading of lines. For purposes of this class, a line ends with
347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated line at
357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}
367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * to detect it after catching the {@code EOFException}.
377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class is intended for reading input that strictly consists of lines, such as line-based
397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with
407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * {@link InputStreamReader} provides similar functionality, this class uses different
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * end-of-input reporting and a more restrictive definition of a line.
427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * and 10, respectively, and the representation of no other character contains these values.
457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * The default charset is US_ASCII.
477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathpublic class StrictLineReader implements Closeable {
4954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final byte CR = (byte) '\r';
5054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final byte LF = (byte) '\n';
5154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final InputStream in;
5354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final Charset charset;
5454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
5554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
5654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // and the data in the range [pos, end) is buffered for reading. At end of input, if there is
5754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // an unterminated line, we set end == -1, otherwise end == pos. If the underlying
5854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  // {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
5954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private byte[] buf;
6054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int pos;
6154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int end;
6254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
6354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
6454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Constructs a new {@code StrictLineReader} with the default capacity and charset.
6554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
6654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param in the {@code InputStream} to read data from.
6754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws NullPointerException if {@code in} is null.
6854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
6954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public StrictLineReader(InputStream in) {
7054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this(in, 8192);
7154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
7254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
7354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
7454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Constructs a new {@code LineReader} with the specified capacity and the default charset.
7554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
7654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param in the {@code InputStream} to read data from.
7754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param capacity the capacity of the buffer.
7854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws NullPointerException if {@code in} is null.
7954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IllegalArgumentException for negative or zero {@code capacity}.
8054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
8154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public StrictLineReader(InputStream in, int capacity) {
8254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this(in, capacity, US_ASCII);
8354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
8454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
8554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
8654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Constructs a new {@code LineReader} with the specified charset and the default capacity.
8754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
8854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param in the {@code InputStream} to read data from.
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param charset the charset used to decode data.
9054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
9154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws NullPointerException if {@code in} or {@code charset} is null.
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IllegalArgumentException if the specified charset is not supported.
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public StrictLineReader(InputStream in, Charset charset) {
9554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this(in, 8192, charset);
9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Constructs a new {@code LineReader} with the specified capacity and charset.
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
10154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param in the {@code InputStream} to read data from.
10254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param capacity the capacity of the buffer.
10354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @param charset the charset used to decode data.
10454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
10554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws NullPointerException if {@code in} or {@code charset} is null.
10654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IllegalArgumentException if {@code capacity} is negative or zero
10754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * or the specified charset is not supported.
10854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
10954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public StrictLineReader(InputStream in, int capacity, Charset charset) {
11054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (in == null || charset == null) {
11154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new NullPointerException();
1127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
11354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (capacity < 0) {
11454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalArgumentException("capacity <= 0");
1157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
11654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (!(charset.equals(US_ASCII) || charset.equals(UTF_8) || charset.equals(ISO_8859_1))) {
11754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalArgumentException("Unsupported encoding");
1187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
1197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
12054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.in = in;
12154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.charset = charset;
12254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    buf = new byte[capacity];
12354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
12454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
12554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
12654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Closes the reader by closing the underlying {@code InputStream} and
12754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * marking this reader as closed.
12854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
12954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IOException for errors when closing the underlying {@code InputStream}.
13054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
13154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  @Override
13254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void close() throws IOException {
13354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    synchronized (in) {
13454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (buf != null) {
13554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        buf = null;
13654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        in.close();
13754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
1387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
13954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
14154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * this end of line marker is not included in the result.
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
14554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @return the next line from the input.
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IOException for underlying {@code InputStream} errors.
14754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws EOFException for the end of source stream.
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public String readLine() throws IOException {
15054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    synchronized (in) {
15154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (buf == null) {
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw new IOException("LineReader is closed");
15354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
15554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Read more data if we are at the end of the buffered data.
15654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Though it's an error to read after an exception, we will let {@code fillBuf()}
15754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
15854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (pos >= end) {
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        fillBuf();
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Try to find LF in the buffered data and return the line if successful.
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = pos; i != end; ++i) {
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (buf[i] == LF) {
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          String res = new String(buf, pos, lineEnd - pos, charset);
16654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          pos = i + 1;
16754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return res;
1687899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
16954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
17154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      // Let's anticipate up to 80 characters on top of those already read.
17254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
17354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        @Override
17454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        public String toString() {
17554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
17654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return new String(buf, 0, length, charset);
17754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
17854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      };
17954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
18054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      while (true) {
18154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        out.write(buf, pos, end - pos);
18254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // Mark unterminated line in case fillBuf throws EOFException or IOException.
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        end = -1;
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        fillBuf();
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        // Try to find LF in the buffered data and return the line if successful.
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = pos; i != end; ++i) {
18754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          if (buf[i] == LF) {
18854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            if (i != pos) {
18954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson              out.write(buf, pos, i - pos);
1907899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            pos = i + 1;
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson            return out.toString();
19354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          }
1947899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
1967899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
19754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
20054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Read an {@code int} from a line containing its decimal representation.
20154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
20254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @return the value of the {@code int} from the next line.
20354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IOException for underlying {@code InputStream} errors or conversion error.
20454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws EOFException for the end of source stream.
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
20654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public int readInt() throws IOException {
20754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    String intString = readLine();
20854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
20954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return Integer.parseInt(intString);
21054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } catch (NumberFormatException e) {
21154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IOException("expected an int but was \"" + intString + "\"");
2127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
21354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
21454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
21554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
21654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Reads new input data into the buffer. Call only with pos == end or end == -1,
21754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * depending on the desired outcome if the function throws.
21854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws IOException for underlying {@code InputStream} errors.
22054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @throws EOFException for the end of source stream.
22154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
22254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void fillBuf() throws IOException {
22354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    int result = in.read(buf, 0, buf.length);
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (result == -1) {
22554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new EOFException();
2267899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
22754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    pos = 0;
22854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    end = result;
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
2307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath}
2317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
232