org.jsimpledb.kv.AbstractKVStore Maven / Gradle / Ivy
Show all versions of jsimpledb-kv Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package org.jsimpledb.kv;
import com.google.common.base.Preconditions;
import java.util.Arrays;
import org.jsimpledb.util.ByteReader;
import org.jsimpledb.util.ByteUtil;
import org.jsimpledb.util.ByteWriter;
import org.jsimpledb.util.CloseableIterator;
/**
* Support superclass for {@link KVStore} implementations.
*
*
* This class provides a partial implementation via the following methods:
*
* - A {@link #get get()} implementation based on {@link #getAtLeast getAtLeast()}
* - {@link #getAtLeast getAtLeast()} and {@link #getAtMost getAtMost()} implementations based on
* {@link #getRange getRange()}.
* - A {@link #remove remove()} implementation that delegates to {@link #removeRange removeRange()}.
* - A {@link #removeRange removeRange()} implementation that delegates to {@link #getRange getRange()},
* iterating through the range of keys and removing them one-by-one via {@link java.util.Iterator#remove}.
* - {@link #encodeCounter encodeCounter()}, {@link #decodeCounter encodeCounter()}, and
* {@link #adjustCounter adjustCounter()} implementations using normal reads and writes
* of values in big-endian encoding (does not provide any lock-free behavior).
* - A {@link #put put()} implementation throwing {@link UnsupportedOperationException}
*
*
*
* Therefore, a read-only {@link KVStore} implementation is possible simply by implementing {@link #getRange}.
*
* @see KVPairIterator
*/
public abstract class AbstractKVStore implements KVStore {
protected AbstractKVStore() {
}
@Override
public byte[] get(byte[] key) {
final KVPair pair = this.getAtLeast(key, ByteUtil.getNextKey(key));
if (pair == null)
return null;
assert Arrays.equals(pair.getKey(), key);
return pair.getValue();
}
@Override
public KVPair getAtLeast(byte[] minKey, byte[] maxKey) {
if (minKey != null && maxKey != null && ByteUtil.compare(minKey, maxKey) >= 0)
return null;
try (final CloseableIterator i = this.getRange(minKey, maxKey, false)) {
return i.hasNext() ? i.next() : null;
}
}
@Override
public KVPair getAtMost(byte[] maxKey, byte[] minKey) {
if (minKey != null && maxKey != null && ByteUtil.compare(minKey, maxKey) >= 0)
return null;
try (final CloseableIterator i = this.getRange(minKey, maxKey, true)) {
return i.hasNext() ? i.next() : null;
}
}
@Override
public void put(byte[] key, byte[] value) {
throw new UnsupportedOperationException();
}
@Override
public void remove(byte[] key) {
this.removeRange(key, ByteUtil.getNextKey(key));
}
@Override
public void removeRange(byte[] minKey, byte[] maxKey) {
try (final CloseableIterator i = this.getRange(minKey, maxKey, false)) {
while (i.hasNext()) {
i.next();
i.remove();
}
}
}
@Override
public byte[] encodeCounter(long value) {
final ByteWriter writer = new ByteWriter(8);
ByteUtil.writeLong(writer, value);
return writer.getBytes();
}
@Override
public long decodeCounter(byte[] value) {
Preconditions.checkArgument(value.length == 8, "invalid encoded counter value length != 8");
return ByteUtil.readLong(new ByteReader(value));
}
@Override
public void adjustCounter(byte[] key, long amount) {
if (key == null)
throw new NullPointerException("null key");
final byte[] previous = this.get(key);
if (previous == null)
return;
final long oldValue;
try {
oldValue = this.decodeCounter(previous);
} catch (IllegalArgumentException e) {
return; // if previous value is not valid, behavior is undefined
}
this.put(key, this.encodeCounter(oldValue + amount));
}
}