io.permazen.kv.array.ArrayKVStore Maven / Gradle / Ivy
Show all versions of permazen-kv-array Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.kv.array;
import com.google.common.base.Preconditions;
import com.google.common.collect.UnmodifiableIterator;
import io.permazen.kv.AbstractKVStore;
import io.permazen.kv.KVPair;
import io.permazen.util.ByteUtil;
import io.permazen.util.CloseableIterator;
import java.nio.ByteBuffer;
import java.util.NoSuchElementException;
/**
* A simple read-only {@link io.permazen.kv.KVStore} based on a sorted array of key/value pairs.
*
*
* Instances query three {@link ByteBuffer}s, one for the array index, one for the key data, and one for the value data.
* Data for these {@link ByteBuffer}s is created using {@link ArrayKVWriter}.
*
*
* Instances are optimized for minimal memory overhead and queries using keys sharing a prefix with the previously
* queried key. Key data is prefix-compressed.
*
*
* Key and value data must not exceed 2GB (each separately).
*/
public class ArrayKVStore extends AbstractKVStore {
private final int size;
private final ArrayKVFinder finder;
/**
* Constructor.
*
* @param indx buffer containing index data written by a {@link ArrayKVWriter}
* @param keys buffer containing key data written by a {@link ArrayKVWriter}
* @param vals buffer containing value data written by a {@link ArrayKVWriter}
* @throws IllegalArgumentException if any parameter is null
* @throws IllegalArgumentException if {@code indx} size is not a correct multiple
*/
public ArrayKVStore(ByteBuffer indx, ByteBuffer keys, ByteBuffer vals) {
Preconditions.checkArgument(indx != null, "null indx");
Preconditions.checkArgument(keys != null, "null keys");
Preconditions.checkArgument(vals != null, "null vals");
Preconditions.checkArgument(indx.capacity() % 8 == 0, "index size is not a multiple of 8");
this.size = indx.capacity() / 8;
this.finder = new ArrayKVFinder(indx, keys, vals);
}
@Override
public byte[] get(byte[] key) {
final int index = this.finder.find(key);
if (index < 0)
return null;
return this.finder.readValue(index);
}
@Override
public KVPair getAtLeast(byte[] minKey, byte[] maxKey) {
int index;
if (minKey == null || minKey.length == 0)
index = 0;
else if ((index = this.finder.find(minKey)) < 0)
index = ~index;
if (index == this.size)
return null;
final KVPair pair = this.finder.readKV(index);
assert ByteUtil.compare(pair.getKey(), minKey) >= 0;
return maxKey == null || ByteUtil.compare(pair.getKey(), maxKey) < 0 ? pair : null;
}
@Override
public KVPair getAtMost(byte[] maxKey, byte[] minKey) {
int index;
if (maxKey == null)
index = this.size;
else if ((index = this.finder.find(maxKey)) < 0)
index = ~index;
if (index == 0)
return null;
final KVPair pair = this.finder.readKV(index - 1);
assert ByteUtil.compare(pair.getKey(), maxKey) < 0;
return minKey == null || ByteUtil.compare(pair.getKey(), minKey) >= 0 ? pair : null;
}
@Override
public CloseableIterator getRange(byte[] minKey, byte[] maxKey, final boolean reverse) {
// Find min index
int index;
if (minKey == null || minKey.length == 0)
index = 0;
else if ((index = this.finder.find(minKey)) < 0)
index = ~index;
final int minIndex = index;
// Find max index
if (maxKey == null)
index = this.size;
else if ((index = this.finder.find(maxKey)) < 0)
index = ~index;
final int maxIndex = index;
// Return iterator over array indexes
return new RangeIter(minIndex, maxIndex, reverse);
}
@Override
public void put(byte[] key, byte[] value) {
throw new UnsupportedOperationException();
}
@Override
public void remove(byte[] key) {
throw new UnsupportedOperationException();
}
@Override
public void removeRange(byte[] minKey, byte[] maxKey) {
throw new UnsupportedOperationException();
}
// RangeIter
private class RangeIter extends UnmodifiableIterator implements CloseableIterator {
private final int limit;
private final boolean reverse;
private int index;
RangeIter(int minIndex, int maxIndex, boolean reverse) {
this.index = reverse ? maxIndex : minIndex;
this.limit = reverse ? minIndex : maxIndex;
this.reverse = reverse;
}
@Override
public boolean hasNext() {
return this.reverse ? this.index > this.limit : this.index < this.limit;
}
@Override
public KVPair next() {
if (!this.hasNext())
throw new NoSuchElementException();
return ArrayKVStore.this.finder.readKV(this.reverse ? --this.index : this.index++);
}
@Override
public void close() {
}
}
}