<commit_before>/*
 *  Copyright (C) 2011-2017 Cojen.org
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.cojen.tupl.views;

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.util.Comparator;

import org.cojen.tupl.Cursor;
import org.cojen.tupl.LockResult;
import org.cojen.tupl.Ordering;
import org.cojen.tupl.Transaction;

import static org.cojen.tupl.views.BoundedView.*;

/**
 * Cursor implementation vended by {@link BoundedView}.
 *
 * @author <NAME>
 */
final class BoundedCursor implements ScannerCursor {
    final BoundedView mView;
    final Cursor mSource;

    private boolean mOutOfBounds;

    BoundedCursor(BoundedView view, Cursor source) {
        mView = view;
        mSource = source;
    }

    @Override
    public long valueLength() throws IOException {
        return mSource.valueLength();
    }

    @Override
    public void valueLength(long length) throws IOException {
        mSource.valueLength(length);
    }

    @Override
    public int valueRead(long pos, byte[] buf, int off, int len) throws IOException {
        return mSource.valueRead(pos, buf, off, len);
    }

    @Override
    public void valueWrite(long pos, byte[] buf, int off, int len) throws IOException {
        mSource.valueWrite(pos, buf, off, len);
    }

    @Override
    public void valueClear(long pos, long length) throws IOException {
        mSource.valueClear(pos, length);
    }

    @Override
    public InputStream newValueInputStream(long pos) throws IOException {
        return mSource.newValueInputStream(pos);
    }

    @Override
    public InputStream newValueInputStream(long pos, int bufferSize) throws IOException {
        return mSource.newValueInputStream(pos, bufferSize);
    }

    @Override
    public OutputStream newValueOutputStream(long pos) throws IOException {
        return mSource.newValueOutputStream(pos);
    }

    @Override
    public OutputStream newValueOutputStream(long pos, int bufferSize) throws IOException {
        return mSource.newValueOutputStream(pos, bufferSize);
    }

    @Override
    public Ordering ordering() {
        return mSource.ordering();
    }

    @Override
    public Comparator<byte[]> comparator() {
        return mSource.comparator();
    }

    @Override
    public Transaction link(Transaction txn) {
        return mSource.link(txn);
    }

    @Override
    public Transaction link() {
        return mSource.link();
    }

    @Override
    public byte[] key() {
        return mSource.key();
    }

    @Override
    public byte[] value() {
        return mOutOfBounds ? null : mSource.value();
    }

    @Override
    public boolean autoload(boolean mode) {
        return mSource.autoload(mode);
    }

    @Override
    public boolean autoload() {
        return mSource.autoload();
    }

    @Override
    public int compareKeyTo(byte[] rkey) {
        return mSource.compareKeyTo(rkey);
    }

    @Override
    public int compareKeyTo(byte[] rkey, int offset, int length) {
        return mSource.compareKeyTo(rkey, offset, length);
    }

    @Override
    public boolean register() throws IOException {
        return mSource.register();
    }

    @Override
    public void unregister() {
        mSource.unregister();
    }

    @Override
    public LockResult first() throws IOException {
        BoundedView view = mView;
        if (view.mEnd == null) {
            return toFirst();
        }

        // If first is after the source end, then no key must be locked. Switch
        // how the source cursor behaves for this to function correctly.

        final Cursor source = mSource;
        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            toFirst();

            byte[] key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    private LockResult toFirst() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] start = view.mStart;
        Cursor source = mSource;
        if (start == null) {
            result = source.first();
        } else if ((view.mMode & START_EXCLUSIVE) == 0) {
            result = source.findGe(start);
        } else {
            result = source.findGt(start);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult last() throws IOException {
        BoundedView view = mView;
        if (view.mStart == null) {
            return toLast();
        }

        // If last is before the source start, then no key must be locked.
        // Switch how the source cursor behaves for this to function correctly.

        final Cursor source = mSource;
        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            toLast();

            byte[] key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    private LockResult toLast() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] end = view.mEnd;
        Cursor source = mSource;
        if (end == null) {
            result = mSource.last();
        } else if ((view.mMode & END_EXCLUSIVE) == 0) {
            result = source.findLe(end);
        } else {
            result = source.findLt(end);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult skip(long amount) throws IOException {
        BoundedView view = mView;
        byte[] limitKey;
        boolean inclusive;
        if (amount >= 0) {
            limitKey = view.mEnd;
            inclusive = (view.mMode & END_EXCLUSIVE) == 0;
        } else {
            limitKey = view.mStart;
            inclusive = (view.mMode & START_EXCLUSIVE) == 0;
        }

        LockResult result = mSource.skip(amount, limitKey, inclusive);

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult skip(long amount, byte[] limitKey, boolean inclusive) throws IOException {
        LockResult result;

        if (amount == 0 || limitKey == null) {
            result = mSource.skip(0);
        } else {
            BoundedView view = mView;

            if (amount > 0) {
                if (view.endRangeCompare(limitKey) > 0) {
                    limitKey = view.mEnd;
                    inclusive = (view.mMode & END_EXCLUSIVE) == 0;
                }
            } else {
                if (view.startRangeCompare(limitKey) < 0) {
                    limitKey = view.mStart;
                    inclusive = (view.mMode & START_EXCLUSIVE) == 0;
                }
            }

            result = mSource.skip(amount, limitKey, inclusive);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult next() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] end = view.mEnd;
        Cursor source = mSource;
        if (end == null) {
            result = source.next();
        } else if ((view.mMode & END_EXCLUSIVE) == 0) {
            result = source.nextLe(end);
        } else {
            result = source.nextLt(end);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult nextLe(byte[] limitKey) throws IOException {
        if (mView.endRangeCompare(limitKey) <= 0) {
            LockResult result = mSource.nextLe(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return next();
        }
    }

    @Override
    public LockResult nextLt(byte[] limitKey) throws IOException {
        if (mView.endRangeCompare(limitKey) <= 0) {
            LockResult result = mSource.nextLt(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return next();
        }
    }

    @Override
    public LockResult previous() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] start = view.mStart;
        Cursor source = mSource;
        if (start == null) {
            result = source.previous();
        } else if ((view.mMode & START_EXCLUSIVE) == 0) {
            result = source.previousGe(start);
        } else {
            result = source.previousGt(start);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult previousGe(byte[] limitKey) throws IOException {
        if (mView.startRangeCompare(limitKey) >= 0) {
            LockResult result = mSource.previousGe(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return previous();
        }
    }

    @Override
    public LockResult previousGt(byte[] limitKey) throws IOException {
        if (mView.startRangeCompare(limitKey) >= 0) {
            LockResult result = mSource.previousGt(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return previous();
        }
    }

    @Override
    public LockResult find(byte[] key) throws IOException {
        if (mView.inRange(key)) {
            LockResult result = mSource.find(key);
            mOutOfBounds = false;
            return result;
        } else {
            mOutOfBounds = true;
            ViewUtils.findNoLock(mSource, key);
            return LockResult.UNOWNED;
        }
    }

    @Override
    public LockResult findGe(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.startRangeCompare(key) < 0) {
            return first();
        }

        final Cursor source = mSource;
        if (view.mEnd == null) {
            LockResult result = source.findGe(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findGe(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    @Override
    public LockResult findGt(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.startRangeCompare(key) < 0) {
            return first();
        }

        final Cursor source = mSource;
        if (view.mEnd == null) {
            LockResult result = source.findGt(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findGt(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    @Override
    public LockResult findLe(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.endRangeCompare(key) > 0) {
            return last();
        }

        final Cursor source = mSource;
        if (view.mStart == null) {
            LockResult result = source.findLe(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findLe(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    @Override
    public LockResult findLt(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.endRangeCompare(key) > 0) {
            return last();
        }

        final Cursor source = mSource;
        if (view.mStart == null) {
            LockResult result = source.findLt(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findLt(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return source.load();
    }

    @Override
    public LockResult findNearby(byte[] key) throws IOException {
        if (mView.inRange(key)) {
            LockResult result = mSource.findNearby(key);
            mOutOfBounds = false;
            return result;
        } else {<commit_msg>BoundedCursor should only lock and not load when autoload is disabled.<commit_after>/*
 *  Copyright (C) 2011-2017 Cojen.org
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.cojen.tupl.views;

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.util.Comparator;

import org.cojen.tupl.Cursor;
import org.cojen.tupl.LockResult;
import org.cojen.tupl.Ordering;
import org.cojen.tupl.Transaction;

import static org.cojen.tupl.views.BoundedView.*;

/**
 * Cursor implementation vended by {@link BoundedView}.
 *
 * @author <NAME>
 */
final class BoundedCursor implements ScannerCursor {
    final BoundedView mView;
    final Cursor mSource;

    private boolean mOutOfBounds;

    BoundedCursor(BoundedView view, Cursor source) {
        mView = view;
        mSource = source;
    }

    @Override
    public long valueLength() throws IOException {
        return mSource.valueLength();
    }

    @Override
    public void valueLength(long length) throws IOException {
        mSource.valueLength(length);
    }

    @Override
    public int valueRead(long pos, byte[] buf, int off, int len) throws IOException {
        return mSource.valueRead(pos, buf, off, len);
    }

    @Override
    public void valueWrite(long pos, byte[] buf, int off, int len) throws IOException {
        mSource.valueWrite(pos, buf, off, len);
    }

    @Override
    public void valueClear(long pos, long length) throws IOException {
        mSource.valueClear(pos, length);
    }

    @Override
    public InputStream newValueInputStream(long pos) throws IOException {
        return mSource.newValueInputStream(pos);
    }

    @Override
    public InputStream newValueInputStream(long pos, int bufferSize) throws IOException {
        return mSource.newValueInputStream(pos, bufferSize);
    }

    @Override
    public OutputStream newValueOutputStream(long pos) throws IOException {
        return mSource.newValueOutputStream(pos);
    }

    @Override
    public OutputStream newValueOutputStream(long pos, int bufferSize) throws IOException {
        return mSource.newValueOutputStream(pos, bufferSize);
    }

    @Override
    public Ordering ordering() {
        return mSource.ordering();
    }

    @Override
    public Comparator<byte[]> comparator() {
        return mSource.comparator();
    }

    @Override
    public Transaction link(Transaction txn) {
        return mSource.link(txn);
    }

    @Override
    public Transaction link() {
        return mSource.link();
    }

    @Override
    public byte[] key() {
        return mSource.key();
    }

    @Override
    public byte[] value() {
        return mOutOfBounds ? null : mSource.value();
    }

    @Override
    public boolean autoload(boolean mode) {
        return mSource.autoload(mode);
    }

    @Override
    public boolean autoload() {
        return mSource.autoload();
    }

    @Override
    public int compareKeyTo(byte[] rkey) {
        return mSource.compareKeyTo(rkey);
    }

    @Override
    public int compareKeyTo(byte[] rkey, int offset, int length) {
        return mSource.compareKeyTo(rkey, offset, length);
    }

    @Override
    public boolean register() throws IOException {
        return mSource.register();
    }

    @Override
    public void unregister() {
        mSource.unregister();
    }

    @Override
    public LockResult first() throws IOException {
        BoundedView view = mView;
        if (view.mEnd == null) {
            return toFirst();
        }

        // If first is after the source end, then no key must be locked. Switch
        // how the source cursor behaves for this to function correctly.

        final Cursor source = mSource;
        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            toFirst();

            byte[] key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    private LockResult toFirst() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] start = view.mStart;
        Cursor source = mSource;
        if (start == null) {
            result = source.first();
        } else if ((view.mMode & START_EXCLUSIVE) == 0) {
            result = source.findGe(start);
        } else {
            result = source.findGt(start);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult last() throws IOException {
        BoundedView view = mView;
        if (view.mStart == null) {
            return toLast();
        }

        // If last is before the source start, then no key must be locked.
        // Switch how the source cursor behaves for this to function correctly.

        final Cursor source = mSource;
        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            toLast();

            byte[] key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    private LockResult toLast() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] end = view.mEnd;
        Cursor source = mSource;
        if (end == null) {
            result = mSource.last();
        } else if ((view.mMode & END_EXCLUSIVE) == 0) {
            result = source.findLe(end);
        } else {
            result = source.findLt(end);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult skip(long amount) throws IOException {
        BoundedView view = mView;
        byte[] limitKey;
        boolean inclusive;
        if (amount >= 0) {
            limitKey = view.mEnd;
            inclusive = (view.mMode & END_EXCLUSIVE) == 0;
        } else {
            limitKey = view.mStart;
            inclusive = (view.mMode & START_EXCLUSIVE) == 0;
        }

        LockResult result = mSource.skip(amount, limitKey, inclusive);

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult skip(long amount, byte[] limitKey, boolean inclusive) throws IOException {
        LockResult result;

        if (amount == 0 || limitKey == null) {
            result = mSource.skip(0);
        } else {
            BoundedView view = mView;

            if (amount > 0) {
                if (view.endRangeCompare(limitKey) > 0) {
                    limitKey = view.mEnd;
                    inclusive = (view.mMode & END_EXCLUSIVE) == 0;
                }
            } else {
                if (view.startRangeCompare(limitKey) < 0) {
                    limitKey = view.mStart;
                    inclusive = (view.mMode & START_EXCLUSIVE) == 0;
                }
            }

            result = mSource.skip(amount, limitKey, inclusive);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult next() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] end = view.mEnd;
        Cursor source = mSource;
        if (end == null) {
            result = source.next();
        } else if ((view.mMode & END_EXCLUSIVE) == 0) {
            result = source.nextLe(end);
        } else {
            result = source.nextLt(end);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult nextLe(byte[] limitKey) throws IOException {
        if (mView.endRangeCompare(limitKey) <= 0) {
            LockResult result = mSource.nextLe(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return next();
        }
    }

    @Override
    public LockResult nextLt(byte[] limitKey) throws IOException {
        if (mView.endRangeCompare(limitKey) <= 0) {
            LockResult result = mSource.nextLt(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return next();
        }
    }

    @Override
    public LockResult previous() throws IOException {
        LockResult result;

        BoundedView view = mView;
        byte[] start = view.mStart;
        Cursor source = mSource;
        if (start == null) {
            result = source.previous();
        } else if ((view.mMode & START_EXCLUSIVE) == 0) {
            result = source.previousGe(start);
        } else {
            result = source.previousGt(start);
        }

        mOutOfBounds = false;
        return result;
    }

    @Override
    public LockResult previousGe(byte[] limitKey) throws IOException {
        if (mView.startRangeCompare(limitKey) >= 0) {
            LockResult result = mSource.previousGe(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return previous();
        }
    }

    @Override
    public LockResult previousGt(byte[] limitKey) throws IOException {
        if (mView.startRangeCompare(limitKey) >= 0) {
            LockResult result = mSource.previousGt(limitKey);
            mOutOfBounds = false;
            return result;
        } else {
            return previous();
        }
    }

    @Override
    public LockResult find(byte[] key) throws IOException {
        if (mView.inRange(key)) {
            LockResult result = mSource.find(key);
            mOutOfBounds = false;
            return result;
        } else {
            mOutOfBounds = true;
            ViewUtils.findNoLock(mSource, key);
            return LockResult.UNOWNED;
        }
    }

    @Override
    public LockResult findGe(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.startRangeCompare(key) < 0) {
            return first();
        }

        final Cursor source = mSource;
        if (view.mEnd == null) {
            LockResult result = source.findGe(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findGe(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    @Override
    public LockResult findGt(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.startRangeCompare(key) < 0) {
            return first();
        }

        final Cursor source = mSource;
        if (view.mEnd == null) {
            LockResult result = source.findGt(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findGt(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.endRangeCompare(key) > 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    @Override
    public LockResult findLe(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.endRangeCompare(key) > 0) {
            return last();
        }

        final Cursor source = mSource;
        if (view.mStart == null) {
            LockResult result = source.findLe(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findLe(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    @Override
    public LockResult findLt(byte[] key) throws IOException {
        BoundedView view = mView;
        if (view.endRangeCompare(key) > 0) {
            return last();
        }

        final Cursor source = mSource;
        if (view.mStart == null) {
            LockResult result = source.findLt(key);
            mOutOfBounds = false;
            return result;
        }

        final Transaction txn = source.link(Transaction.BOGUS);
        final boolean autoload = source.autoload(false);
        try {
            source.findLt(key);
            mOutOfBounds = false;

            key = source.key();
            if (key == null) {
                return LockResult.UNOWNED;
            }
            if (view.startRangeCompare(key) < 0) {
                source.reset();
                return LockResult.UNOWNED;
            }
        } finally {
            autoload(autoload);
            link(txn);
        }

        // This performs any required lock acquisition.
        return autoload ? source.load() : source.lock();
    }

    @Override
    public LockResult findNearby(byte[] key) throws IOException {
        if (mView.inRange(key)) {
            LockResult result = mSource.findNearby(key);
            mOutOfBounds = false;
            return result;
        } else {
{'fraction_non_alphanumeric': 0.09327859772586583, 'fraction_numerical': 0.0024109731860684847, 'mean_word_length': 1.7592592592592593, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5212754', 'n_tokens_mistral': 8567, 'n_tokens_neox': 8198, 'n_words': 2254}