infinispan.org.iq80.leveldb.impl.Level Maven / Gradle / Ivy
/**
* 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.impl;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.iq80.leveldb.table.UserComparator;
import org.iq80.leveldb.util.InternalTableIterator;
import org.iq80.leveldb.util.LevelIterator;
import org.iq80.leveldb.util.Slice;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.collect.Lists.newArrayList;
import static org.iq80.leveldb.impl.FileMetaData.GET_LARGEST_USER_KEY;
import static org.iq80.leveldb.impl.SequenceNumber.MAX_SEQUENCE_NUMBER;
import static org.iq80.leveldb.impl.ValueType.VALUE;
// todo this class should be immutable
public class Level implements SeekingIterable
{
private final int levelNumber;
private final TableCache tableCache;
private final InternalKeyComparator internalKeyComparator;
private final List files;
public Level(int levelNumber, List files, TableCache tableCache, InternalKeyComparator internalKeyComparator)
{
Preconditions.checkArgument(levelNumber >= 0, "levelNumber is negative");
Preconditions.checkNotNull(files, "files is null");
Preconditions.checkNotNull(tableCache, "tableCache is null");
Preconditions.checkNotNull(internalKeyComparator, "internalKeyComparator is null");
this.files = newArrayList(files);
this.tableCache = tableCache;
this.internalKeyComparator = internalKeyComparator;
Preconditions.checkArgument(levelNumber >= 0, "levelNumber is negative");
this.levelNumber = levelNumber;
}
public int getLevelNumber()
{
return levelNumber;
}
public List getFiles()
{
return files;
}
@Override
public LevelIterator iterator()
{
return createLevelConcatIterator(tableCache, files, internalKeyComparator);
}
public static LevelIterator createLevelConcatIterator(TableCache tableCache, List files, InternalKeyComparator internalKeyComparator)
{
return new LevelIterator(tableCache, files, internalKeyComparator);
}
public LookupResult get(LookupKey key, ReadStats readStats)
{
if (files.isEmpty()) {
return null;
}
List fileMetaDataList = Lists.newArrayListWithCapacity(files.size());
if (levelNumber == 0) {
for (FileMetaData fileMetaData : files) {
if (internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getSmallest().getUserKey()) >= 0 &&
internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getLargest().getUserKey()) <= 0) {
fileMetaDataList.add(fileMetaData);
}
}
}
else {
// Binary search to find earliest index whose largest key >= ikey.
int index = ceilingEntryIndex(Lists.transform(files, GET_LARGEST_USER_KEY), key.getInternalKey(), internalKeyComparator);
// did we find any files that could contain the key?
if (index >= files.size()) {
return null;
}
// check if the smallest user key in the file is less than the target user key
FileMetaData fileMetaData = files.get(index);
if (internalKeyComparator.getUserComparator().compare(key.getUserKey(), fileMetaData.getSmallest().getUserKey()) < 0) {
return null;
}
// search this file
fileMetaDataList.add(fileMetaData);
}
FileMetaData lastFileRead = null;
int lastFileReadLevel = -1;
readStats.clear();
for (FileMetaData fileMetaData : fileMetaDataList) {
if (lastFileRead!=null && readStats.getSeekFile() == null) {
// We have had more than one seek for this read. Charge the first file.
readStats.setSeekFile(lastFileRead);
readStats.setSeekFileLevel(lastFileReadLevel);
}
lastFileRead = fileMetaData;
lastFileReadLevel = levelNumber;
// open the iterator
InternalTableIterator iterator = tableCache.newIterator(fileMetaData);
// seek to the key
iterator.seek(key.getInternalKey());
if (iterator.hasNext()) {
// parse the key in the block
Entry entry = iterator.next();
InternalKey internalKey = entry.getKey();
Preconditions.checkState(internalKey != null, "Corrupt key for %s", key.getUserKey().toString(UTF_8));
// if this is a value key (not a delete) and the keys match, return the value
if (key.getUserKey().equals(internalKey.getUserKey())) {
if (internalKey.getValueType() == ValueType.DELETION) {
return LookupResult.deleted(key);
}
else if (internalKey.getValueType() == VALUE) {
return LookupResult.ok(key, entry.getValue());
}
}
}
}
return null;
}
private static int ceilingEntryIndex(List list, T key, Comparator comparator)
{
int insertionPoint = Collections.binarySearch(list, key, comparator);
if (insertionPoint < 0) {
insertionPoint = -(insertionPoint + 1);
}
return insertionPoint;
}
public boolean someFileOverlapsRange(Slice smallestUserKey, Slice largestUserKey)
{
InternalKey smallestInternalKey = new InternalKey(smallestUserKey, MAX_SEQUENCE_NUMBER, VALUE);
int index = findFile(smallestInternalKey);
UserComparator userComparator = internalKeyComparator.getUserComparator();
return ((index < files.size()) &&
userComparator.compare(largestUserKey, files.get(index).getSmallest().getUserKey()) >= 0);
}
private int findFile(InternalKey targetKey)
{
if (files.size() == 0) {
return files.size();
}
// todo replace with Collections.binarySearch
int left = 0;
int right = files.size() - 1;
// binary search restart positions to find the restart position immediately before the targetKey
while (left < right) {
int mid = (left + right) / 2;
if (internalKeyComparator.compare(files.get(mid).getLargest(), targetKey) < 0) {
// Key at "mid.largest" is < "target". Therefore all
// files at or before "mid" are uninteresting.
left = mid + 1;
}
else {
// Key at "mid.largest" is >= "target". Therefore all files
// after "mid" are uninteresting.
right = mid;
}
}
return right;
}
public void addFile(FileMetaData fileMetaData)
{
// todo remove mutation
files.add(fileMetaData);
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append("Level");
sb.append("{levelNumber=").append(levelNumber);
sb.append(", files=").append(files);
sb.append('}');
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy