static final int splitlines(CBytePtr a, int len, CLinePtr lr) {
        // Copy to preserve pass by value semantics.
        a = a.copyPtr();
        // lr is passed by reference (**).

	int h, i;
        CBytePtr p = a.copyPtr();
        CBytePtr b = a.copyPtr();

        CBytePtr plast = a.copyPtrWithOffset(len - 1);

        Line l = new Line();

	/* count the lines */
	i = 1; /* extra line for sentinel */
        CBytePtr end = a.copyPtrWithOffset(len); // i.e. one past the last byte.
	for (/*NOP*/; !p.equals(end); p.plusPlus()) {
            if (p.deref() == '\n' || p.equals(plast)) {
                i++;
            }
        }

        try {
            lr.realloc(i); // Hinky
        }
        catch (OutOfMemoryError ome) {
            return -1;
        }

        lr = lr.copyPtr(); // Wacky. Point to the same rep, but don't modify the mPos for the caller.

        l = lr.deref();

	/* build the line array and calculate hashes */
	h = 0;
	for (p = a.copyPtr(); !p.equals(end); p.plusPlus()) {
            /* Leonid Yuriev's hash */
            h = (h * 1664525) + (0x000000FF & (int)(p.deref())) + 1013904223; // DCI: int math differences in Java?
            if (p.deref() == '\n' || p.equals(plast)) {
                l.h = h;
                h = 0;
                l.len = p.minus(b) + 1;
                l.l = b.copyPtr();
                l.n = Integer.MAX_VALUE;

                lr.plusPlus();
                l = lr.deref();
                b = p.copyPtrWithOffset(1);
            }
	}

	/* set up a sentinel */
	l.h = 0;
        l.len = 0;
	l.l = a.copyPtrWithOffset(len);
	return i - 1;
    }