add generics to PersistencMap

This commit is contained in:
2018-11-04 10:42:05 +01:00
parent f2d5c27668
commit 008f0db377
3 changed files with 105 additions and 93 deletions

View File

@@ -2,10 +2,9 @@ package org.lucares.pdb.map;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Stack;
import java.util.function.Function;
import org.lucares.pdb.blockstorage.intsequence.VariableByteEncoder;
import org.lucares.pdb.diskstorage.DiskBlock;
@@ -14,16 +13,16 @@ import org.lucares.utils.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PersistentMap {
public class PersistentMap<K, V> {
private static final Logger LOGGER = LoggerFactory.getLogger(PersistentMap.class);
// the maximum key
private static final byte[] MAX_KEY = new byte[] { Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE,
Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE,
Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE,
Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE,
Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE };
private static final byte[] MAX_KEY;
static {
MAX_KEY = new byte[20];
Arrays.fill(MAX_KEY, Byte.MAX_VALUE);
}
interface VisitorCallback {
void visit(PersistentMapDiskNode node, PersistentMapDiskNode parentNode, NodeEntry nodeEntry, int depth);
@@ -33,11 +32,41 @@ public class PersistentMap {
void visit(PersistentMapDiskNode node, int depth);
}
public static final Function<byte[], String> STRING_DECODER = t -> new String(t, StandardCharsets.UTF_8);
public static final Function<byte[], String> LONG_DECODER = t -> String
.valueOf(VariableByteEncoder.decodeFirstValue(t));
public interface EncoderDecoder<O> {
public byte[] encode(O object);
public O decode(byte[] bytes);
}
private static final class StringCoder implements EncoderDecoder<String> {
@Override
public byte[] encode(final String object) {
return object.getBytes(StandardCharsets.UTF_8);
}
@Override
public String decode(final byte[] bytes) {
return bytes == null ? null : new String(bytes, StandardCharsets.UTF_8);
}
}
private static final class LongCoder implements EncoderDecoder<Long> {
@Override
public byte[] encode(final Long object) {
return VariableByteEncoder.encode(object);
}
@Override
public Long decode(final byte[] bytes) {
return bytes == null ? null : VariableByteEncoder.decodeFirstValue(bytes);
}
}
public static final EncoderDecoder<Long> LONG_CODER = new LongCoder();
public static final EncoderDecoder<String> STRING_CODER = new StringCoder();
private static final Charset UTF8 = StandardCharsets.UTF_8;
static final int BLOCK_SIZE = 4096;
static final long NODE_OFFSET_TO_ROOT_NODE = 8;
@@ -45,8 +74,15 @@ public class PersistentMap {
private int maxEntriesInNode = Integer.MAX_VALUE;
public PersistentMap(final DiskStorage diskStore) throws IOException {
private final EncoderDecoder<K> keyEncoder;
private final EncoderDecoder<V> valueEncoder;
public PersistentMap(final DiskStorage diskStore, final EncoderDecoder<K> keyEncoder,
final EncoderDecoder<V> valueEncoder) throws IOException {
this.diskStore = diskStore;
this.keyEncoder = keyEncoder;
this.valueEncoder = valueEncoder;
initIfNew();
}
@@ -77,51 +113,26 @@ public class PersistentMap {
}
}
public Long putValue(final String key, final long value) throws IOException {
final byte[] oldValue = putValue(key.getBytes(UTF8), VariableByteEncoder.encode(value));
return oldValue == null ? null : VariableByteEncoder.decodeFirstValue(oldValue);
public V putValue(final K key, final V value) throws IOException {
final byte[] encodedKey = keyEncoder.encode(key);
final byte[] encodedValue = valueEncoder.encode(value);
final byte[] oldValue = putValue(encodedKey, encodedValue);
return valueEncoder.decode(oldValue);
}
public String putValue(final long key, final String value) throws IOException {
final byte[] oldValue = putValue(VariableByteEncoder.encode(key), value.getBytes(UTF8));
return oldValue == null ? null : new String(oldValue, UTF8);
public V getValue(final K key) throws IOException {
final byte[] encodedKey = keyEncoder.encode(key);
final byte[] foundValue = getValue(encodedKey);
return valueEncoder.decode(foundValue);
}
public Long putValue(final long key, final long value) throws IOException {
final byte[] oldValue = putValue(VariableByteEncoder.encode(key), VariableByteEncoder.encode(value));
return oldValue == null ? null : VariableByteEncoder.decodeFirstValue(oldValue);
}
public Long getAsLong(final String key) throws IOException {
final byte[] buffer = get(key.getBytes(UTF8));
return buffer == null ? null : VariableByteEncoder.decodeFirstValue(buffer);
}
public Long getAsLong(final long key) throws IOException {
final byte[] buffer = get(VariableByteEncoder.encode(key));
return buffer == null ? null : VariableByteEncoder.decodeFirstValue(buffer);
}
public String putValue(final String key, final String value) throws IOException {
final byte[] keyBytes = key.getBytes(UTF8);
final byte[] valueBytes = value.getBytes(UTF8);
final byte[] oldValue = putValue(keyBytes, valueBytes);
return oldValue == null ? null : new String(oldValue, UTF8);
}
public String getAsString(final String key) throws IOException {
final byte[] value = get(key.getBytes(UTF8));
return value == null ? null : new String(value, UTF8);
}
public byte[] putValue(final byte[] key, final byte[] value) throws IOException {
private byte[] putValue(final byte[] key, final byte[] value) throws IOException {
final long rootNodeOffset = readNodeOffsetOfRootNode();
final Stack<PersistentMapDiskNode> parents = new Stack<>();
return insert(parents, rootNodeOffset, key, value);
}
public byte[] get(final byte[] key) throws IOException {
private byte[] getValue(final byte[] key) throws IOException {
final long rootNodeOffset = readNodeOffsetOfRootNode();
final NodeEntry entry = findNodeEntry(rootNodeOffset, key);
@@ -266,8 +277,7 @@ public class PersistentMap {
diskBlock.force();
}
public void print(final Function<byte[], String> keyDecoder, final Function<byte[], String> valueDecoder)
throws IOException {
public void print() throws IOException {
visitNodeEntriesPreOrder((node, parentNode, nodeEntry, depth) -> {
@@ -275,8 +285,8 @@ public class PersistentMap {
final String children = "#" + node.getEntries().size();
writer.println(" ".repeat(depth) + "@" + node.getNodeOffset() + " " + children + " "
+ nodeEntry.toString(keyDecoder, valueDecoder));
writer.println(" ".repeat(depth) + "@" + node.getNodeOffset() + " " + children + " " + nodeEntry
.toString(b -> String.valueOf(keyEncoder.decode(b)), b -> String.valueOf(valueEncoder.decode(b))));
});
}

View File

@@ -15,26 +15,23 @@ import org.lucares.utils.Preconditions;
* <pre>
* Node layout:
*
* ◀────────── Prefix ──────────▶ ◀───────────────── Suffix ──────────────────▶
* ┏━━━━━┳━━━┳━━━━━┳━━━━━┳━━━━━┳━━━┳╸╺╸╺╸╺╸╺┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
* ┃ 456 ┃ 6 ┃ 5,6 ┃ 3,6 ┃ 3,2 ┃ ∅ ┃ ┃"ba"->"147"┃"foobar"->"467"┃"foobaz"->"value"┃
* ┗━━━━━┻━━━┻━━━━━┻━━━━━┻━━━━━┻━━━┻╸╺╸╺╸╺╸╺┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛
* │ │ │ │ │ │ │ └▶ null byte that serves as a separator for the prefix.
* │ │ │ │ │ │ └▶ size of the third last key ("ba" in this example)
* │ │ │ │ │ └▶ size of the third last value ("147" in this example)
* │ │ │ │ └▶ size of the second last key ("foobar" in this example)
* │ │ │ └▶ size of the second last value ("467" in this example)
* │ │ └▶ size of the last key ("foobaz" in this example)
* │ └▶ size of the last value (the string "value" in this example)
* └▶ number of entries * 2
* └▶ node offset of the parent node (-1 if there is no parent node)
* ◀─────── Prefix ──────▶ ◀───────────────── Suffix ──────────────────▶
* ┏━━━┳━━━━━┳━━━━━┳━━━━━┳━━━┳╸╺╸╺╸╺╸╺┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
* ┃ 6 ┃ 5,6 ┃ 3,6 ┃ 3,2 ┃ ∅ ┃ ┃"ba"->"147"┃"foobar"->"467"┃"foobaz"->"value"┃
* ┗━━━┻━━━━━┻━━━━━┻━━━━━┻━━━┻╸╺╸╺╸╺╸╺┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┛
* │ │ │ │ │ │ │ └▶ null byte that serves as a separator for the prefix.
* │ │ │ │ │ │ └▶ size of the third last key ("ba" in this example)
* │ │ │ │ │ └▶ size of the third last value ("147" in this example)
* │ │ │ │ └▶ size of the second last key ("foobar" in this example)
* │ │ │ └▶ size of the second last value ("467" in this example)
* │ │ └▶ size of the last key ("foobaz" in this example)
* │ └▶ size of the last value (the string "value" in this example)
* └▶ number of entries * 2
*
* </pre>
*/
public class PersistentMapDiskNode {
public static final long NO_NODE_OFFSET = -1;
private final List<NodeEntry> entries;
private final long nodeOffset;
@@ -53,9 +50,6 @@ public class PersistentMapDiskNode {
"block size must be " + PersistentMap.BLOCK_SIZE + " but was " + data.length);
}
final LongList longs = VariableByteEncoder.decode(data);
if (longs.size() == 0) {
System.out.println();
}
final List<NodeEntry> entries = deserialize(longs, data);
return new PersistentMapDiskNode(nodeOffset, entries);