All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.iq80.leveldb.table.BlockIterator Maven / Gradle / Ivy

There is a newer version: 0.12
Show newest version
/**
 * Copyright (C) 2011 the original author or authors.
 * See the notice.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.iq80.leveldb.table;

import com.google.common.base.Preconditions;
import org.iq80.leveldb.impl.SeekingIterator;
import org.iq80.leveldb.util.SliceInput;
import org.iq80.leveldb.util.Slice;
import org.iq80.leveldb.util.Slices;
import org.iq80.leveldb.util.VariableLengthQuantity;
import org.iq80.leveldb.util.SliceOutput;

import java.util.Comparator;
import java.util.NoSuchElementException;

import static org.iq80.leveldb.util.SizeOf.SIZE_OF_INT;

public class BlockIterator implements SeekingIterator
{
    private final SliceInput data;
    private final Slice restartPositions;
    private final int restartCount;
    private final Comparator comparator;

    private BlockEntry nextEntry;

    public BlockIterator(Slice data, Slice restartPositions, Comparator comparator)
    {
        Preconditions.checkNotNull(data, "data is null");
        Preconditions.checkNotNull(restartPositions, "restartPositions is null");
        Preconditions.checkArgument(restartPositions.length() % SIZE_OF_INT == 0, "restartPositions.readableBytes() must be a multiple of %s", SIZE_OF_INT);
        Preconditions.checkNotNull(comparator, "comparator is null");

        this.data = data.input();

        this.restartPositions = restartPositions.slice();
        restartCount = this.restartPositions.length() / SIZE_OF_INT;

        this.comparator = comparator;

        seekToFirst();
    }

    @Override
    public boolean hasNext()
    {
        return nextEntry != null;
    }

    @Override
    public BlockEntry peek()
    {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return nextEntry;
    }

    @Override
    public BlockEntry next()
    {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        BlockEntry entry = nextEntry;

        if (!data.isReadable()) {
            nextEntry = null;
        }
        else {
            // read entry at current data position
            nextEntry = readEntry(data, nextEntry);
        }

        return entry;
    }

    @Override
    public void remove()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Repositions the iterator so the beginning of this block.
     */
    @Override
    public void seekToFirst()
    {
        if (restartCount > 0) {
            seekToRestartPosition(0);
        }
    }

    /**
     * Repositions the iterator so the key of the next BlockElement returned greater than or equal to the specified targetKey.
     */
    @Override
    public void seek(Slice targetKey)
    {
        if (restartCount == 0) {
            return;
        }

        int left = 0;
        int right = restartCount - 1;

        // binary search restart positions to find the restart position immediately before the targetKey
        while (left < right) {
            int mid = (left + right + 1) / 2;

            seekToRestartPosition(mid);

            if (comparator.compare(nextEntry.getKey(), targetKey) < 0) {
                // key at mid is smaller than targetKey.  Therefore all restart
                // blocks before mid are uninteresting.
                left = mid;
            }
            else {
                // key at mid is greater than or equal to targetKey.  Therefore
                // all restart blocks at or after mid are uninteresting.
                right = mid - 1;
            }
        }

        // linear search (within restart block) for first key greater than or equal to targetKey
        for (seekToRestartPosition(left); nextEntry != null; next()) {
            if (comparator.compare(peek().getKey(), targetKey) >= 0) {
                break;
            }
        }

    }

    /**
     * Seeks to and reads the entry at the specified restart position.
     * 

* After this method, nextEntry will contain the next entry to return, and the previousEntry will be null. */ private void seekToRestartPosition(int restartPosition) { Preconditions.checkPositionIndex(restartPosition, restartCount, "restartPosition"); // seek data readIndex to the beginning of the restart block int offset = restartPositions.getInt(restartPosition * SIZE_OF_INT); data.setPosition(offset); // clear the entries to assure key is not prefixed nextEntry = null; // read the entry nextEntry = readEntry(data, null); } /** * Reads the entry at the current data readIndex. * After this method, data readIndex is positioned at the beginning of the next entry * or at the end of data if there was not a next entry. * * @return true if an entry was read */ private static BlockEntry readEntry(SliceInput data, BlockEntry previousEntry) { Preconditions.checkNotNull(data, "data is null"); // read entry header int sharedKeyLength = VariableLengthQuantity.readVariableLengthInt(data); int nonSharedKeyLength = VariableLengthQuantity.readVariableLengthInt(data); int valueLength = VariableLengthQuantity.readVariableLengthInt(data); // read key Slice key = Slices.allocate(sharedKeyLength + nonSharedKeyLength); SliceOutput sliceOutput = key.output(); if (sharedKeyLength > 0) { Preconditions.checkState(previousEntry != null, "Entry has a shared key but no previous entry was provided"); sliceOutput.writeBytes(previousEntry.getKey(), 0, sharedKeyLength); } sliceOutput.writeBytes(data, nonSharedKeyLength); // read value Slice value = data.readSlice(valueLength); return new BlockEntry(key, value); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy