package be.bagofwords.db.memory;
import be.bagofwords.db.CoreDataInterface;
import be.bagofwords.db.combinator.Combinator;
import be.bagofwords.iterator.CloseableIterator;
import be.bagofwords.iterator.IterableUtils;
import be.bagofwords.util.DataLock;
import be.bagofwords.util.KeyValue;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class InMemoryDataInterface<T extends Object> extends CoreDataInterface<T> {
private Map<Long, T> values;
private final DataLock lock;
public InMemoryDataInterface(String name, Class<T> objectClass, Combinator<T> combinator) {
super(name, objectClass, combinator, true);
this.values = new ConcurrentHashMap<>();
this.lock = new DataLock();
}
@Override
public T read(long key) {
return values.get(key);
}
@Override
public void write(long key, T value) {
lock.lockWrite(key);
nonSynchronizedWrite(key, value);
lock.unlockWrite(key);
}
private void nonSynchronizedWrite(long key, T value) {
if (value == null) {
values.remove(key);
} else {
T currentValue = values.get(key);
if (currentValue == null) {
values.put(key, value);
} else {
values.put(key, getCombinator().combine(currentValue, value));
}
}
}
@Override
public void write(Iterator<KeyValue<T>> entries) {
lock.lockWriteAll();
while (entries.hasNext()) {
KeyValue<T> entry = entries.next();
nonSynchronizedWrite(entry.getKey(), entry.getValue());
}
lock.unlockWriteAll();
}
@Override
public CloseableIterator<KeyValue<T>> iterator() {
//We should probably add locking for this iterator, but do we want to
//keep all the data locked until it is closed?
List<Map.Entry<Long, T>> sortedValues = new ArrayList<>(values.entrySet());
Collections.sort(sortedValues, (o1, o2) -> Long.compare(o1.getKey(), o2.getKey()));
final Iterator<Map.Entry<Long, T>> valuesIt = sortedValues.iterator();
return new CloseableIterator<KeyValue<T>>() {
@Override
public boolean hasNext() {
return valuesIt.hasNext();
}
@Override
public KeyValue<T> next() {
Map.Entry<Long, T> next = valuesIt.next();
return new KeyValue<>(next.getKey(), next.getValue());
}
@Override
public void remove() {
valuesIt.remove();
}
@Override
public void closeInt() {
//ok
}
};
}
@Override
public void dropAllData() {
lock.lockWriteAll();
values.clear();
lock.unlockWriteAll();
}
@Override
public void flush() {
ifNotClosed(() -> {
//make sure that all writes have completely finished:
lock.lockWriteAll();
lock.unlockWriteAll();
});
}
@Override
protected void doClose() {
lock.lockWriteAll();
values = null;
lock.unlockWriteAll();
}
@Override
public long apprSize() {
return values.size(); //no locking needed since it is only the approximate size
}
@Override
public CloseableIterator<Long> keyIterator() {
lock.lockReadAll();
List<Long> sortedKeys = new ArrayList<>(values.keySet());
lock.unlockReadAll();
Collections.sort(sortedKeys);
return IterableUtils.iterator(sortedKeys.iterator());
}
@Override
public void optimizeForReading() {
//do nothing
}
@Override
public long exactSize() {
lock.lockReadAll();
long result = values.size();
lock.unlockReadAll();
return result;
}
@Override
public boolean mightContain(long key) {
lock.lockRead(key);
boolean result = values.containsKey(key);
lock.unlockRead(key);
return result;
}
}