class RawParser {



    private final BufferPool pool;







    RawParser(final int poolSize, final int bufferSize) {
        super();
        this.pool = new BufferPool(poolSize, bufferSize);
    }






    public void parse(final String document, final IRawHandler handler)
            throws RawParseException {
        if (document == null) {
            throw new IllegalArgumentException("Document cannot be null");
        }
        parse(new StringReader(document), handler);
    }




    public void parse(
            final Reader reader, final IRawHandler handler)
            throws RawParseException {

        if (reader == null) {
            throw new IllegalArgumentException("Reader cannot be null");
        }

        if (handler == null) {
            throw new IllegalArgumentException("Handler cannot be null");
        }

        parseDocument(reader, this.pool.poolBufferSize, handler);

    }





    /*
     * This method receiving the buffer size with package visibility allows
     * testing different buffer sizes.
     */
    void parseDocument(final Reader reader, final int suggestedBufferSize, final IRawHandler handler)
            throws RawParseException {<FILL_FUNCTION_BODY>}



    private static int[] computeLastLineCol(final char[] buffer, final int bufferContentSize) {

        if (bufferContentSize == 0) {
            return new int[] {1, 1};
        }

        int line = 1;
        int col = 1;

        char c;

        int lastLineFeed = 0;

        int n = bufferContentSize;
        int i = 0;

        while (n-- != 0) {
            c = buffer[i];
            if (c == '\n') {
                line++;
                lastLineFeed = i;
            }
            i++;
        }

        col = bufferContentSize - lastLineFeed;

        return new int[] {line, col};

    }


    




    /*
     * This class models a pool of buffers, used to keep the amount of
     * large char[] buffer objects required to operate to a minimum.
     *
     * Note this pool never blocks, so if a new buffer is needed and all
     * are currently allocated, a new char[] object is created and returned.
     *
     */
    private static final class BufferPool {

        private final char[][] pool;
        private final boolean[] allocated;
        private final int poolBufferSize;

        private BufferPool(final int poolSize, final int poolBufferSize) {

            super();

            this.pool = new char[poolSize][];
            this.allocated = new boolean[poolSize];
            this.poolBufferSize = poolBufferSize;

            for (int i = 0; i < this.pool.length; i++) {
                this.pool[i] = new char[this.poolBufferSize];
            }
            Arrays.fill(this.allocated, false);

        }

        private synchronized char[] allocateBuffer(final int bufferSize) {
            if (bufferSize != this.poolBufferSize) {
                // We will only pool buffers of the default size. If a different size is required, we just
                // create it without pooling.
                return new char[bufferSize];
            }
            for (int i = 0; i < this.pool.length; i++) {
                if (!this.allocated[i]) {
                    this.allocated[i] = true;
                    return this.pool[i];
                }
            }
            return new char[bufferSize];
        }

        private synchronized void releaseBuffer(final char[] buffer) {
            if (buffer == null) {
                return;
            }
            if (buffer.length != this.poolBufferSize) {
                // This buffer cannot be part of the pool - only buffers with a specific size are contained
                return;
            }
            for (int i = 0; i < this.pool.length; i++) {
                if (this.pool[i] == buffer) {
                    // Found it. Mark it as non-allocated
                    this.allocated[i] = false;
                    return;
                }
            }
            // The buffer wasn't part of our pool. Just return.
        }


    }


}


        // We are trying to read the entire resource into the buffer. If it's not big enough,
        // then it will have to be grown. Note grown buffers are not maintained by the pool,
        // so template cache should play a more important role for this, allowing the system to
        // not require the creation of large buffers each time.

        final long parsingStartTimeNanos = System.nanoTime();

        char[] buffer = null;

        try {

            handler.handleDocumentStart(parsingStartTimeNanos, 1, 1);

            int bufferSize = suggestedBufferSize;
            buffer = this.pool.allocateBuffer(bufferSize);

            int bufferContentSize = reader.read(buffer);

            boolean cont = (bufferContentSize != -1);

            while (cont) {

                if (bufferContentSize == bufferSize) {
                    // Buffer is not big enough, double it!

                    char[] newBuffer = null;
                    try {

                        bufferSize *= 2;

                        newBuffer = this.pool.allocateBuffer(bufferSize);
                        System.arraycopy(buffer, 0, newBuffer, 0, bufferContentSize);

                        this.pool.releaseBuffer(buffer);

                        buffer = newBuffer;

                    } catch (final Exception ignored) {
                        this.pool.releaseBuffer(newBuffer);
                    }

                }

                final int read = reader.read(buffer, bufferContentSize, (bufferSize - bufferContentSize));
                if (read != -1) {
                    bufferContentSize += read;
                } else {
                    cont = false;
                }

            }

            handler.handleText(buffer, 0, bufferContentSize, 1, 1);

            final int[] lastLineCol = computeLastLineCol(buffer, bufferContentSize);

            final long parsingEndTimeNanos = System.nanoTime();
            handler.handleDocumentEnd(parsingEndTimeNanos, (parsingEndTimeNanos - parsingStartTimeNanos), lastLineCol[0], lastLineCol[1]);

        } catch (final RawParseException e) {
            throw e;
        } catch (final Exception e) {
            throw new RawParseException(e);
        } finally {
            this.pool.releaseBuffer(buffer);
            try {
                reader.close();
            } catch (final Throwable ignored) {
                // This exception can be safely ignored
            }
        }

    
<no_super_class>