Dead simple persistent ordered map. It is thread safe, via coarse synchronization on writes. Reads are unsynchronized, though the underlying FileChannel is shared. Deletes do not reclaim file space; they simply stop referencing the block. Writes are CRC checked on restart, file is truncated to ma
| 65 | * @author cschanck |
| 66 | */ |
| 67 | public class ChiseledMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> { |
| 68 | |
| 69 | public static final int DIGEST_MASK = 0x7fffffff; |
| 70 | |
| 71 | public static final byte[] HDR = "(-:AnonymousBC:ChiseledMap-)".getBytes(StandardCharsets.US_ASCII); |
| 72 | |
| 73 | /** |
| 74 | * Open methods. |
| 75 | */ |
| 76 | public enum OpenOption { |
| 77 | MUST_BE_NEW, MUST_EXIST, DONT_CARE |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * For the map methods, IOExceptions are wrapped in this. |
| 82 | */ |
| 83 | public static class RuntimeIOException extends RuntimeException { |
| 84 | public RuntimeIOException(Throwable cause) { |
| 85 | super(cause); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Encode a key/value (here you must handle null values) into a ByteBuffer. |
| 91 | * @param <KK> key type |
| 92 | * @param <VV> value type |
| 93 | */ |
| 94 | @FunctionalInterface |
| 95 | public interface Encoder<KK, VV> { |
| 96 | ByteBuffer encode(KK k, VV v) throws IOException; |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Decode a byte array into a key/value pair. |
| 101 | * @param <KK> key type |
| 102 | * @param <VV> value type |
| 103 | */ |
| 104 | @FunctionalInterface |
| 105 | public interface Decoder<KK, VV> { |
| 106 | Entry<KK, VV> decode(byte[] bArray) throws IOException; |
| 107 | } |
| 108 | |
| 109 | private final CRC32 digest; |
| 110 | private final Comparator<K> comp; |
| 111 | private final FileChannel fc; |
| 112 | private final ConcurrentSkipListMap<K, Long> map; |
| 113 | private final ByteBuffer lenBuffer = ByteBuffer.allocate(4); |
| 114 | private final ByteBuffer digestBuffer = ByteBuffer.allocate(4); |
| 115 | private final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024 * 1024); |
| 116 | private final File file; |
| 117 | private long currentWritePos = HDR.length; |
| 118 | private long nextWritePos = HDR.length; |
| 119 | private volatile int pendingWrites = 0; |
| 120 | private final Encoder<K, V> encoder; |
| 121 | private final Decoder<K, V> decoder; |
| 122 | private long entriesOnDisk = 0; |
| 123 | |
| 124 | /** |