value reference private

This commit is contained in:
Cavallium 2018-12-11 23:00:51 +01:00
parent 468e58994c
commit adfdb571d8
12 changed files with 333 additions and 161 deletions

View File

@ -1,6 +1,7 @@
package org.warp.jcwdb; package org.warp.jcwdb;
import java.io.IOException; import java.io.IOException;
import java.util.function.Consumer;
public class CacheIndexManager implements IndexManager { public class CacheIndexManager implements IndexManager {
@ -19,6 +20,12 @@ public class CacheIndexManager implements IndexManager {
return 0; return 0;
} }
@Override
public long getHash(long index) {
// TODO: implement
return 0;
}
@Override @Override
public <T> long add(DBDataOutput<T> writer) { public <T> long add(DBDataOutput<T> writer) {
// TODO: implement // TODO: implement
@ -26,8 +33,9 @@ public class CacheIndexManager implements IndexManager {
} }
@Override @Override
public <T> void set(long index, DBDataOutput<T> writer) { public <T> IndexDetails set(long index, DBDataOutput<T> writer) {
// TODO: implement // TODO: implement
return null;
} }
@Override @Override

View File

@ -62,15 +62,15 @@ public class Cleaner {
while(!stopRequest) { while(!stopRequest) {
try { try {
System.out.println("[CLEANER] Waiting " + sleepInterval + "ms."); System.out.println("[CLEANER] Waiting " + sleepInterval + "ms.");
Thread.sleep(sleepInterval); sleepFor(sleepInterval);
final double removedItems = clean(); final double removedItems = clean();
double suggestedExecutionTimeByItemsCalculations = sleepInterval; double suggestedExecutionTimeByItemsCalculations = (sleepInterval + MAXIMUM_SLEEP_INTERVAL) / 2;
System.out.println("[CLEANER] REMOVED_ITEMS: " + removedItems); System.out.println("[CLEANER] REMOVED_ITEMS: " + removedItems);
if (removedItems > 0) { if (removedItems > 0) {
final double removedItemsRatio = removedItems / NORMAL_REMOVED_ITEMS; final double removedItemsRatio = removedItems / NORMAL_REMOVED_ITEMS;
System.out.println("[CLEANER] REMOVED_ITEMS_RATIO: " + removedItemsRatio); System.out.println("[CLEANER] REMOVED_ITEMS_RATIO: " + removedItemsRatio);
if (removedItemsRatio < 1d / REMOVED_ITEMS_RATIO || removedItemsRatio > REMOVED_ITEMS_RATIO) { if (removedItemsRatio < 1d / REMOVED_ITEMS_RATIO && removedItemsRatio >= REMOVED_ITEMS_RATIO) {
suggestedExecutionTimeByItemsCalculations = sleepInterval / removedItemsRatio; suggestedExecutionTimeByItemsCalculations = sleepInterval / removedItemsRatio;
} }
} }
@ -95,6 +95,21 @@ public class Cleaner {
} }
} }
} }
private void sleepFor(int sleepInterval) throws InterruptedException {
int lastI = (int) Math.ceil(((double) sleepInterval) / 1000d);
for (int i = 0; i < lastI; i++) {
if (stopRequest) {
return;
}
if (i == lastI) {
Thread.sleep(sleepInterval % 1000);
} else {
Thread.sleep(lastI);
}
Thread.sleep(sleepInterval);
}
}
} }
} }

View File

@ -4,42 +4,37 @@ import java.util.ArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
public class DBLightListParser extends DBTypeParserImpl<LightList> { public class DBLightListParser<T> extends DBTypeParserImpl<LightList<T>> {
private final JCWDatabase db; private final JCWDatabase db;
public DBLightListParser(JCWDatabase db) { public DBLightListParser(JCWDatabase db) {
this.db = db; this.db = db;
} }
public DBReader<LightList> getReader() { public DBReader<LightList<T>> getReader() {
return (i, size) -> { return (i, size) -> {
LongArrayList internalList = new LongArrayList(); LongArrayList internalList = new LongArrayList();
LongArrayList hashes = new LongArrayList(); long max = size / Long.BYTES;
long max = size / (Long.BYTES * 2);
for (int item = 0; item < max; item++){ for (int item = 0; item < max; item++){
long itm = i.readLong(); long itm = i.readLong();
long hash = i.readLong();
internalList.add(itm); internalList.add(itm);
hashes.add(hash);
} }
return new LightList(db, internalList, hashes); return new LightList<T>(db, internalList);
}; };
} }
public DBDataOutput<LightList> getWriter(final LightList value) { public DBDataOutput<LightList<T>> getWriter(final LightList<T> value) {
final int elementsCount = value.size(); final int elementsCount = value.size();
return DBDataOutput.create((o) -> { return DBDataOutput.create((o) -> {
LongArrayList list = value.internalList; LongArrayList list = value.internalList;
LongArrayList hashes = value.hashes;
for (int i = 0; i < elementsCount; i++) { for (int i = 0; i < elementsCount; i++) {
o.writeLong(list.getLong(i)); o.writeLong(list.getLong(i));
o.writeLong(hashes.getLong(i));
} }
}, DBStandardTypes.LIGHT_LIST, elementsCount * Long.BYTES * 2, calculateHash(value)); }, DBStandardTypes.LIGHT_LIST, elementsCount * Long.BYTES, calculateHash(value));
} }
@Override @Override
public long calculateHash(LightList value) { public long calculateHash(LightList<T> value) {
return value.hashCodeLong(); return value.internalList.hashCode();
} }
} }

View File

@ -1,31 +1,44 @@
package org.warp.jcwdb; package org.warp.jcwdb;
import java.io.IOException; import java.io.IOException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
/** /**
* You must have only a maximum of 1 reference for each index * You must have only a maximum of 1 reference for each index
* @param <T> * @param <T>
*/ */
public class EntryReference<T> implements Castable { public class EntryReference<T> implements Castable, Saveable {
private final JCWDatabase.EntryReferenceTools db; private final JCWDatabase.EntryReferenceTools db;
private final long entryIndex; private final long entryIndex;
private final DBTypeParser<T> parser; private final DBTypeParser<T> parser;
private T value; private T value;
private long cachedHash;
private volatile boolean isHashCached;
private volatile boolean loaded;
private volatile boolean closed; private volatile boolean closed;
private final Object hashCacheLock = new Object();
private final Object accessLock = new Object();
private final Object closeLock = new Object(); private final Object closeLock = new Object();
public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, DBTypeParser<T> parser) throws IOException { public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser<T> parser) {
this.loaded = false;
this.isHashCached = false;
this.db = db; this.db = db;
this.entryIndex = entryId; this.entryIndex = entryId;
this.parser = parser; this.parser = parser;
this.value = db.read(entryId, parser.getReader()); this.value = null;
} }
public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, DBTypeParser<T> parser, T value) { public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser<T> parser, T value) {
this.loaded = true;
this.isHashCached = true;
this.db = db; this.db = db;
this.entryIndex = entryId; this.entryIndex = entryId;
this.parser = parser; this.parser = parser;
this.cachedHash = hash;
this.value = value; this.value = value;
} }
@ -38,44 +51,116 @@ public class EntryReference<T> implements Castable {
} }
public long calculateHash() { public long calculateHash() {
return parser.calculateHash(value); synchronized(accessLock) {
load();
synchronized(hashCacheLock) {
if (isHashCached) {
return cachedHash;
}
}
return parser.calculateHash(this.value);
}
} }
/** /**
* Note that this method won't be called when closing without saving * Note that this method won't be called when closing without saving
* @throws IOException
*/ */
public void save() throws IOException { public void save() {
if (!closed) { synchronized(accessLock) {
db.write(entryIndex, parser.getWriter(value)); if (loaded && !closed) {
try {
IndexDetails returnedDetails = db.write(entryIndex, parser.getWriter(value));
synchronized(hashCacheLock) {
this.cachedHash = returnedDetails.getHash();
this.isHashCached = true;
}
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} }
public void editValue(Function<T, T> editFunction) throws IOException { /**
this.value = editFunction.apply(this.value); * Reccomended way to edit the value
this.save(); * @param editFunction
* @throws IOException
*/
public void editValue(BiFunction<T, Saveable, T> editFunction) throws IOException {
synchronized(accessLock) {
load();
this.value = editFunction.apply(this.value, this);
this.save();
}
} }
/** /**
* Use editValue instead * Reccomended way to edit the value
* @param editFunction
* @throws IOException
*/
public void editValue(BiConsumer<T, Saveable> editFunction) throws IOException {
synchronized(accessLock) {
load();
editFunction.accept(this.value, this);
this.save();
}
}
/**
* Substitute the old value with a new one
* @param val
* @throws IOException
*/
public void setValue(T val) throws IOException {
synchronized(accessLock) {
this.loaded = true;
this.value = val;
synchronized(hashCacheLock) {
this.isHashCached = false;
}
this.save();
}
}
/**
* Use editValue instead. READ ONLY!!
* @return * @return
*/ */
@Deprecated() @Deprecated()
public T getValue() { public T getValue() {
return this.value; return getValueReadOnly();
} }
/** /**
* DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED
* @return * @return
*/ */
public T getValueUnsafe() { public T getValueReadOnly() {
return this.value; synchronized(accessLock) {
load();
return this.value;
}
} }
private void load() {
synchronized(accessLock) {
if (!loaded) {
try {
this.value = db.read(entryIndex, parser.getReader());
this.loaded = true;
} catch (IOException e) {
throw (NullPointerException) new NullPointerException(e.getLocalizedMessage()).initCause(e);
}
}
}
}
@SuppressWarnings("unchecked")
@Override @Override
public <T> T cast() { public <U> U cast() {
return (T) this; return (U) this;
} }
protected void close() throws IOException { protected void close() throws IOException {
@ -105,4 +190,8 @@ public class EntryReference<T> implements Castable {
closed = true; closed = true;
} }
} }
public Object getAccessLock() {
return accessLock;
}
} }

View File

@ -11,6 +11,7 @@ import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.function.Consumer;
public class FileIndexManager implements IndexManager { public class FileIndexManager implements IndexManager {
private final SeekableByteChannel dataFileChannel, metadataFileChannel; private final SeekableByteChannel dataFileChannel, metadataFileChannel;
@ -69,16 +70,21 @@ public class FileIndexManager implements IndexManager {
} }
@Override @Override
public <T> void set(long index, DBDataOutput<T> data) throws IOException { public long getHash(long index) throws IOException {
return getIndexMetadata(index).getHash();
}
@Override
public <T> IndexDetails set(long index, DBDataOutput<T> data) throws IOException {
checkClosed(); checkClosed();
final int dataSize = data.getSize(); final int dataSize = data.getSize();
final IndexDetails indexDetails = getIndexMetadataUnsafe(index); final IndexDetails indexDetails = getIndexMetadataUnsafe(index);
if (indexDetails == null || indexDetails.getSize() < dataSize) { if (indexDetails == null || indexDetails.getSize() < dataSize) {
// Allocate new space // Allocate new space
allocateAndWrite(index, data); return allocateAndWrite(index, data);
} else { } else {
// Check if size changed // Check if size changed
if (indexDetails.getSize() > dataSize) { if (dataSize < indexDetails.getSize()) {
// Mark free the unused bytes // Mark free the unused bytes
fileAllocator.markFree(indexDetails.getOffset() + dataSize, dataSize); fileAllocator.markFree(indexDetails.getOffset() + dataSize, dataSize);
} }
@ -86,6 +92,8 @@ public class FileIndexManager implements IndexManager {
editIndex(index, indexDetails, indexDetails.getOffset(), dataSize, indexDetails.getType(), data.calculateHash()); editIndex(index, indexDetails, indexDetails.getOffset(), dataSize, indexDetails.getType(), data.calculateHash());
// Write data // Write data
writeExact(indexDetails, data); writeExact(indexDetails, data);
// Before returning, return IndexDetails
return indexDetails;
} }
} }
@ -122,13 +130,14 @@ public class FileIndexManager implements IndexManager {
o.flush(); o.flush();
} }
private void allocateAndWrite(final long index, DBDataOutput<?> w) throws IOException { private IndexDetails allocateAndWrite(final long index, DBDataOutput<?> w) throws IOException {
final int size = w.getSize(); final int size = w.getSize();
final int type = w.getType(); final int type = w.getType();
final long hash = w.calculateHash(); final long hash = w.calculateHash();
final long offset = fileAllocator.allocate(size); final long offset = fileAllocator.allocate(size);
IndexDetails details = editIndex(index, offset, size, type, hash); IndexDetails details = editIndex(index, offset, size, type, hash);
writeExact(details, w); writeExact(details, w);
return details;
} }
@Override @Override
@ -156,16 +165,17 @@ public class FileIndexManager implements IndexManager {
SeekableByteChannel metadata = metadataFileChannel.position(index * IndexDetails.TOTAL_BYTES); SeekableByteChannel metadata = metadataFileChannel.position(index * IndexDetails.TOTAL_BYTES);
eraseIndexDetails(metadata); eraseIndexDetails(metadata);
} }
if (dirtyLoadedIndices.contains(index)) { boolean isDirty = false;
synchronized (indicesMapsAccessLock) { IndexDetails indexDetails = null;
synchronized (indicesMapsAccessLock) {
if (dirtyLoadedIndices.contains(index)) {
indexDetails = loadedIndices.get(index);
dirtyLoadedIndices.remove(index); dirtyLoadedIndices.remove(index);
} }
}
if (isDirty) {
// Update indices metadata // Update indices metadata
SeekableByteChannel metadata = metadataFileChannel.position(index * IndexDetails.TOTAL_BYTES); SeekableByteChannel metadata = metadataFileChannel.position(index * IndexDetails.TOTAL_BYTES);
IndexDetails indexDetails;
synchronized (indicesMapsAccessLock) {
indexDetails = loadedIndices.get(index);
}
writeIndexDetails(metadata, indexDetails); writeIndexDetails(metadata, indexDetails);
} }
synchronized (indicesMapsAccessLock) { synchronized (indicesMapsAccessLock) {
@ -196,7 +206,7 @@ public class FileIndexManager implements IndexManager {
*/ */
private IndexDetails editIndex(long index, IndexDetails oldData, long offset, int size, int type, long hash) { private IndexDetails editIndex(long index, IndexDetails oldData, long offset, int size, int type, long hash) {
if (oldData.getOffset() != offset || oldData.getSize() != size || oldData.getType() != type || oldData.getHash() != hash) { if (oldData.getOffset() != offset || oldData.getSize() != size || oldData.getType() != type || oldData.getHash() != hash) {
editIndex(index, offset, size, type, hash); return editIndex(index, offset, size, type, hash);
} else { } else {
return oldData; return oldData;
} }

View File

@ -1,12 +1,14 @@
package org.warp.jcwdb; package org.warp.jcwdb;
import java.io.IOException; import java.io.IOException;
import java.util.function.Consumer;
public interface IndexManager extends Cleanable { public interface IndexManager extends Cleanable {
<T> T get(long index, DBReader<T> reader) throws IOException; <T> T get(long index, DBReader<T> reader) throws IOException;
int getType(long index) throws IOException; int getType(long index) throws IOException;
long getHash(long index) throws IOException;
<T> long add(DBDataOutput<T> writer) throws IOException; <T> long add(DBDataOutput<T> writer) throws IOException;
<T> void set(long index, DBDataOutput<T> writer) throws IOException; <T> IndexDetails set(long index, DBDataOutput<T> writer) throws IOException;
void delete(long index) throws IOException; void delete(long index) throws IOException;
boolean has(long index); boolean has(long index);
void close() throws IOException; void close() throws IOException;

View File

@ -5,6 +5,7 @@ import java.lang.ref.WeakReference;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.function.Consumer;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
@ -51,7 +52,7 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
if (exists(0)) { if (exists(0)) {
return get(0); return get(0);
} else { } else {
LightList<T> newRoot = new LightList<T>(this, new LongArrayList(), new LongArrayList()); LightList<T> newRoot = new LightList<T>(this, new LongArrayList());
return set(0, newRoot); return set(0, newRoot);
} }
} }
@ -63,11 +64,13 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
EntryReference<T> ref; EntryReference<T> ref;
if (refRef == null || (ref = (EntryReference<T>) refRef.get()) == null) { if (refRef == null || (ref = (EntryReference<T>) refRef.get()) == null) {
int type; int type;
long hash;
synchronized (indicesAccessLock) { synchronized (indicesAccessLock) {
type = this.indices.getType(index); type = this.indices.getType(index);
hash = this.indices.getHash(index);
} }
DBTypeParser<T> typeParser = this.typesManager.get(type); DBTypeParser<T> typeParser = this.typesManager.get(type);
ref = new EntryReference<>(entryReferenceTools, index, typeParser); ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser);
refRef = new WeakReference<>(ref); refRef = new WeakReference<>(ref);
this.references.put(index, refRef); this.references.put(index, refRef);
} }
@ -81,10 +84,12 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
EntryReference<T> ref; EntryReference<T> ref;
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass()); DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
long index; long index;
long hash;
synchronized (indicesAccessLock) { synchronized (indicesAccessLock) {
index = indices.add(typeParser.getWriter(value)); index = indices.add(typeParser.getWriter(value));
hash = indices.getHash(index);
} }
ref = new EntryReference<>(entryReferenceTools, index, typeParser, value); ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser, value);
this.references.put(index, new WeakReference<>(ref)); this.references.put(index, new WeakReference<>(ref));
return ref; return ref;
} }
@ -105,14 +110,17 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
EntryReference<T> ref; EntryReference<T> ref;
if (exists(index)) { if (exists(index)) {
ref = get(index); ref = get(index);
ref.value = value; ref.setValue(value);
return ref; return ref;
} else { } else {
@SuppressWarnings("unchecked")
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass()); DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
long hash;
synchronized (indicesAccessLock) { synchronized (indicesAccessLock) {
indices.set(index, typeParser.getWriter(value)); IndexDetails returnedDetails = indices.set(index, typeParser.getWriter(value));
hash = returnedDetails.getHash();
} }
ref = new EntryReference<>(entryReferenceTools, index, typeParser); ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser);
this.references.put(index, new WeakReference<EntryReference<?>>(ref)); this.references.put(index, new WeakReference<EntryReference<?>>(ref));
return ref; return ref;
} }
@ -194,6 +202,15 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
while (iterator.hasNext()) { while (iterator.hasNext()) {
Entry<WeakReference<EntryReference<?>>> entry = iterator.next(); Entry<WeakReference<EntryReference<?>>> entry = iterator.next();
if (count > MAX_LOADED_REFERENCES * 3l / 2l) { if (count > MAX_LOADED_REFERENCES * 3l / 2l) {
WeakReference<EntryReference<?>> weakRef = entry.getValue();
EntryReference<?> ref = weakRef.get();
if (ref != null) {
try {
ref.close();
} catch (IOException e) {
e.printStackTrace();
}
}
iterator.remove(); iterator.remove();
removedReferences++; removedReferences++;
} else { } else {
@ -214,8 +231,8 @@ public class JCWDatabase implements AutoCloseable, Cleanable {
return indices.get(index, reader); return indices.get(index, reader);
} }
public <T> void write(long index, DBDataOutput<T> writer) throws IOException { public <T> IndexDetails write(long index, DBDataOutput<T> writer) throws IOException {
indices.set(index, writer); return indices.set(index, writer);
} }
} }

View File

@ -10,12 +10,10 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
public class LightList<T> implements List<T> { public class LightList<T> implements List<T> {
public final LongArrayList internalList; public final LongArrayList internalList;
public final LongArrayList hashes;
private final transient JCWDatabase db; private final transient JCWDatabase db;
public LightList(JCWDatabase db, LongArrayList internalList, LongArrayList hashes) { public LightList(JCWDatabase db, LongArrayList internalList) {
this.internalList = internalList; this.internalList = internalList;
this.hashes = hashes;
this.db = db; this.db = db;
} }
@ -36,7 +34,7 @@ public class LightList<T> implements List<T> {
EntryReference<T> ref = null; EntryReference<T> ref = null;
try { try {
ref = db.get(element); ref = db.get(element);
if (o.equals(ref.value)) { if (o.equals(ref.getValueReadOnly())) {
return true; return true;
} }
} catch (IOException e) { } catch (IOException e) {
@ -47,19 +45,37 @@ public class LightList<T> implements List<T> {
return false; return false;
} }
/**
* Use iteratorReferences()
*/
@Deprecated
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Iterator<T> iterator() { public Iterator<T> iterator() {
final ArrayList<T> elements = new ArrayList<>(); final ArrayList<T> elements = new ArrayList<>();
for (Long element : internalList) { for (Long element : internalList) {
try { try {
elements.add((T) db.get(element).value); elements.add((T) db.get(element).getValueReadOnly());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
return elements.iterator(); return elements.iterator();
} }
@SuppressWarnings("unchecked")
public Iterator<EntryReference<T>> iteratorReferences() {
final ArrayList<EntryReference<T>> elements = new ArrayList<>();
for (Long element : internalList) {
try {
elements.add(db.get(element));
} catch (IOException e) {
e.printStackTrace();
}
}
return elements.iterator();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -67,7 +83,7 @@ public class LightList<T> implements List<T> {
final T[] elements = (T[]) new Objects[internalList.size()]; final T[] elements = (T[]) new Objects[internalList.size()];
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
try { try {
elements[i] = (T) db.get(internalList.getLong(i)).value; elements[i] = (T) db.get(internalList.getLong(i)).getValueReadOnly();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -81,7 +97,7 @@ public class LightList<T> implements List<T> {
final T1[] elements = (T1[]) new Objects[internalList.size()]; final T1[] elements = (T1[]) new Objects[internalList.size()];
for (int i = 0; i < elements.length; i++) { for (int i = 0; i < elements.length; i++) {
try { try {
elements[i] = (T1) db.get(internalList.getLong(i)).value; elements[i] = (T1) db.get(internalList.getLong(i)).getValueReadOnly();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -98,11 +114,10 @@ public class LightList<T> implements List<T> {
public EntryReference<T> addEntry(T o) { public EntryReference<T> addEntry(T o) {
EntryReference<T> ref = addToDatabase(o); EntryReference<T> ref = addToDatabase(o);
if (internalList.add(ref.getIndex())) { if (internalList.add(ref.getIndex())) {
hashes.add(ref.getParser().calculateHash(ref.value)); return ref;
} else { } else {
return null; return null;
} }
return ref;
} }
@Override @Override
@ -110,7 +125,6 @@ public class LightList<T> implements List<T> {
int removeIndex = indexOf(o); int removeIndex = indexOf(o);
if (removeIndex >= 0) { if (removeIndex >= 0) {
internalList.removeLong(removeIndex); internalList.removeLong(removeIndex);
hashes.removeLong(removeIndex);
return true; return true;
} }
return false; return false;
@ -120,7 +134,6 @@ public class LightList<T> implements List<T> {
int removeIndex = indexOfEntry(ref); int removeIndex = indexOfEntry(ref);
if (removeIndex >= 0) { if (removeIndex >= 0) {
internalList.removeLong(removeIndex); internalList.removeLong(removeIndex);
hashes.removeLong(removeIndex);
return true; return true;
} }
return false; return false;
@ -193,14 +206,30 @@ public class LightList<T> implements List<T> {
@Override @Override
public void clear() { public void clear() {
internalList.clear(); internalList.clear();
hashes.clear(); }
/**
* Use getReference or getReadOnlyValue
*/
@Deprecated
@Override
public T get(int index) {
return getReadOnlyValue(index);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override public T getReadOnlyValue(int index) {
public T get(int index) {
try { try {
return (T) db.get(internalList.getLong(index)).value; return (T) db.get(internalList.getLong(index)).getValueReadOnly();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public EntryReference<T> getReference(int index) {
try {
return db.get(internalList.getLong(index)).cast();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
@ -212,10 +241,9 @@ public class LightList<T> implements List<T> {
public T set(int index, T element) { public T set(int index, T element) {
EntryReference<T> ref = addToDatabase(element); EntryReference<T> ref = addToDatabase(element);
long oldIndex = internalList.set(index, ref.getIndex()); long oldIndex = internalList.set(index, ref.getIndex());
hashes.set(index, ref.calculateHash());
try { try {
ref.close(); ref.close();
return ((EntryReference<T>) (db.get(oldIndex))).value; return ((EntryReference<T>) (db.get(oldIndex))).getValueReadOnly();
} catch (IOException e) { } catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e); throw (NullPointerException) new NullPointerException().initCause(e);
} }
@ -225,26 +253,26 @@ public class LightList<T> implements List<T> {
public void add(int index, T element) { public void add(int index, T element) {
EntryReference<T> ref = addToDatabase(element); EntryReference<T> ref = addToDatabase(element);
internalList.add(index, ref.getIndex()); internalList.add(index, ref.getIndex());
hashes.add(index, ref.getParser().calculateHash(ref.value));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public T remove(int index) { public T remove(int index) {
long oldIndex = internalList.removeLong(index); long oldIndex = internalList.removeLong(index);
hashes.removeLong(index);
try { try {
return ((EntryReference<T>) (db.get(oldIndex))).value; return ((EntryReference<T>) (db.get(oldIndex))).getValueReadOnly();
} catch (IOException e) { } catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e); throw (NullPointerException) new NullPointerException().initCause(e);
} }
} }
@Override @Override
public int indexOf(Object o) { public int indexOf(Object o) {
EntryReference<?> ref = addToDatabase(o); EntryReference<?> ref = addToDatabase(o);
long objToRemoveHash = ref.calculateHash(); long objToRemoveHash = ref.calculateHash();
LongArrayList hashes = new LongArrayList();
for (int i = 0; i < hashes.size(); i++) { for (int i = 0; i < hashes.size(); i++) {
long hash = hashes.getLong(i); long hash = hashes.getLong(i);
if (objToRemoveHash == hash) { if (objToRemoveHash == hash) {
@ -261,18 +289,15 @@ public class LightList<T> implements List<T> {
} }
public int indexOfEntry(EntryReference<T> ref) { public int indexOfEntry(EntryReference<T> ref) {
long objToRemoveHash = ref.calculateHash(); for (int i = 0; i < internalList.size(); i++) {
long index = internalList.getLong(i);
for (int i = 0; i < hashes.size(); i++) { try {
long hash = hashes.getLong(i); EntryReference<?> ref2 = db.get(index);
if (objToRemoveHash == hash) { if (ref.getValueReadOnly().equals(ref2.getValueReadOnly())) {
try { return i;
if (ref.equals(db.get(internalList.getLong(i)))) {
return i;
}
} catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e);
} }
} catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e);
} }
} }
return -1; return -1;
@ -285,16 +310,17 @@ public class LightList<T> implements List<T> {
int lastValue = -1; int lastValue = -1;
for (int i = 0; i < hashes.size(); i++) { for (int i = 0; i < internalList.size(); i++) {
long hash = hashes.getLong(i); long index2 = internalList.getLong(i);
if (objToRemoveHash == hash) { try {
try { EntryReference<?> ref2 = db.get(index2);
if (ref.equals(db.get(internalList.getLong(i)))) { if (objToRemoveHash == ref2.calculateHash()) {
if (ref.getValueReadOnly().equals(ref2.getValueReadOnly())) {
lastValue = i; lastValue = i;
} }
} catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e);
} }
} catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e);
} }
} }
return lastValue; return lastValue;
@ -336,19 +362,10 @@ public class LightList<T> implements List<T> {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((hashes == null) ? 0 : hashes.hashCode());
result = prime * result + ((internalList == null) ? 0 : internalList.hashCode()); result = prime * result + ((internalList == null) ? 0 : internalList.hashCode());
return result; return result;
} }
public long hashCodeLong() {
final int prime = 31;
int result1 = prime + ((hashes == null) ? 0 : hashes.hashCode());
int result2 = prime + ((internalList == null) ? 0 : internalList.hashCode());
long result = (((long) result1) << 32) | (result2 & 0xffffffffL);
return result;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean removeIf(Predicate<? super T> filter) { public boolean removeIf(Predicate<? super T> filter) {
@ -357,13 +374,12 @@ public class LightList<T> implements List<T> {
for (int i = 0; i < internalList.size(); ) { for (int i = 0; i < internalList.size(); ) {
T obj; T obj;
try { try {
obj = ((EntryReference<T>) (db.get(internalList.getLong(i)).cast())).value; obj = ((EntryReference<T>) (db.get(internalList.getLong(i)).cast())).getValueReadOnly();
} catch (IOException e) { } catch (IOException e) {
throw (NullPointerException) new NullPointerException().initCause(e); throw (NullPointerException) new NullPointerException().initCause(e);
} }
if (filter.test(obj)) { if (filter.test(obj)) {
internalList.removeLong(i); internalList.removeLong(i);
hashes.removeLong(i);
removed = true; removed = true;
} else { } else {
i++; i++;

View File

@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2LongMap;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.Consumer;
public class MixedIndexDatabase implements IndexManager { public class MixedIndexDatabase implements IndexManager {
private final Long2LongMap mostAccessedIndices; private final Long2LongMap mostAccessedIndices;
@ -36,17 +37,26 @@ public class MixedIndexDatabase implements IndexManager {
} }
} }
@Override
public long getHash(long index) throws IOException {
if (cacheIndices.has(index)) {
return cacheIndices.getHash(index);
} else {
return fileIndices.getHash(index);
}
}
@Override @Override
public <T> long add(DBDataOutput<T> writer) throws IOException { public <T> long add(DBDataOutput<T> writer) throws IOException {
return fileIndices.add(writer); return fileIndices.add(writer);
} }
@Override @Override
public <T> void set(long index, DBDataOutput<T> writer) throws IOException { public <T> IndexDetails set(long index, DBDataOutput<T> writer) throws IOException {
if (cacheIndices.has(index)) { if (cacheIndices.has(index)) {
cacheIndices.set(index, writer); return cacheIndices.set(index, writer);
} else { } else {
fileIndices.set(index, writer); return fileIndices.set(index, writer);
} }
} }

View File

@ -0,0 +1,7 @@
package org.warp.jcwdb;
import java.io.IOException;
public interface Saveable {
public void save();
}

View File

@ -12,6 +12,7 @@ import java.nio.file.Paths;
import java.util.function.Predicate; import java.util.function.Predicate;
public class App { public class App {
static long time3;
public static void main(String[] args) { public static void main(String[] args) {
try { try {
if (args.length > 2 && Boolean.parseBoolean(args[2])) { if (args.length > 2 && Boolean.parseBoolean(args[2])) {
@ -26,55 +27,57 @@ public class App {
System.out.println("Time elapsed: " + (time01 - time0)); System.out.println("Time elapsed: " + (time01 - time0));
System.out.println("Loading root..."); System.out.println("Loading root...");
EntryReference<LightList<Animal>> rootRef = db.getRoot(Animal.class); EntryReference<LightList<Animal>> rootRef = db.getRoot(Animal.class);
LightList<Animal> root = rootRef.getValue(); rootRef.editValue((root, saver) -> {
long time1 = System.currentTimeMillis(); long time1 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time1 - time01)); System.out.println("Time elapsed: " + (time1 - time01));
System.out.println("Root size: " + root.size()); System.out.println("Root size: " + root.size());
System.out.println("Root:"); System.out.println("Root:");
// for (int i = 0; i < root.size(); i++) { // for (int i = 0; i < root.size(); i++) {
// System.out.println(" - " + root.get(i)); // System.out.println(" - " + root.get(i));
// } // }
long prectime = System.currentTimeMillis(); long prectime = System.currentTimeMillis();
for (int i = 0; i < 2000000/* 2000000 */; i++) { for (int i = 0; i < 20000/* 2000000 */; i++) {
Animal animal = new StrangeAnimal(i % 40); Animal animal = new StrangeAnimal(i % 40);
root.add(animal); root.add(animal);
if (i > 0 && i % 200000 == 0) { if (i > 0 && i % 200000 == 0) {
long precprectime = prectime; long precprectime = prectime;
prectime = System.currentTimeMillis(); prectime = System.currentTimeMillis();
System.out.println("Element " + i + " (" + (prectime - precprectime) + "ms)"); System.out.println("Element " + i + " (" + (prectime - precprectime) + "ms)");
}
} }
} long time2 = System.currentTimeMillis();
long time2 = System.currentTimeMillis(); saver.save();
System.out.println("Root size: " + root.size()); System.out.println("Root size: " + root.size());
System.out.println("Time elapsed: " + (time2 - time1)); System.out.println("Time elapsed: " + (time2 - time1));
System.out.println("Used memory: " System.out.println("Used memory: "
+ ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB"); + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
long time2_0 = System.currentTimeMillis(); long time2_0 = System.currentTimeMillis();
System.out.println("Filtering strings..."); System.out.println("Filtering strings...");
//root.removeIf(Animal::hasFourLegs); //root.removeIf(Animal::hasFourLegs);
long time2_1 = System.currentTimeMillis(); long time2_1 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time2_1 - time2_0)); System.out.println("Time elapsed: " + (time2_1 - time2_0));
ObjectList<Animal> results = new ObjectArrayList<>(); ObjectList<Animal> results = new ObjectArrayList<>();
root.forEach((value) -> { root.forEach((value) -> {
if (Animal.hasFourLegs(value)) { if (Animal.hasFourLegs(value)) {
results.add(value); results.add(value);
} }
//System.out.println("val:" + value); //System.out.println("val:" + value);
});
long time2_2 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time2_2 - time2_1));
System.out.println("Used memory: "
+ ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Cleaning database (to reduce the amount of used memory and detect memory leaks)...");
long removedItems = db.clean();
time3 = System.currentTimeMillis();
System.out.println("Removed items: " + removedItems);
System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Time elapsed: " + (time3 - time2_2));
System.out.println("Saving database...");
System.out.println("Root size: " + root.size());
}); });
long time2_2 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time2_2 - time2_1));
System.out.println("Used memory: "
+ ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Cleaning database (to reduce the amount of used memory and detect memory leaks)...");
long removedItems = db.clean();
long time3 = System.currentTimeMillis();
System.out.println("Removed items: " + removedItems);
System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB");
System.out.println("Time elapsed: " + (time3 - time2_2));
System.out.println("Saving database...");
System.out.println("Root size: " + root.size());
db.close(); db.close();
long time4 = System.currentTimeMillis(); long time4 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time4 - time3)); System.out.println("Time elapsed: " + (time4 - time3));

View File

@ -41,14 +41,14 @@ public class AppTest
JCWDatabase db = new JCWDatabase(path, idx); JCWDatabase db = new JCWDatabase(path, idx);
EntryReference<LightList<CustomClass>> ref = db.getRoot(); EntryReference<LightList<CustomClass>> ref = db.getRoot();
LightList<CustomClass> root = ref.value; ref.editValue((root, saver) -> {
root.add(customClass); root.add(customClass);
});
db.close(); db.close();
JCWDatabase db2 = new JCWDatabase(path, idx); JCWDatabase db2 = new JCWDatabase(path, idx);
EntryReference<LightList<CustomClass>> ref2 = db2.getRoot(); EntryReference<LightList<CustomClass>> ref2 = db2.getRoot();
LightList<CustomClass> root2 = ref2.value; CustomClass customClass2 = ref2.getValueReadOnly().getReadOnlyValue(0);
CustomClass customClass2 = root2.get(0);
assertTrue(customClass.equals(customClass2)); assertTrue(customClass.equals(customClass2));
assertTrue(customClass.string.equals(customClass2.string)); assertTrue(customClass.string.equals(customClass2.string));
db2.close(); db2.close();
@ -99,7 +99,7 @@ public class AppTest
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx"); Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
JCWDatabase db = new JCWDatabase(path, idx); JCWDatabase db = new JCWDatabase(path, idx);
EntryReference<LightList<String>> ref = db.getRoot(); EntryReference<LightList<String>> ref = db.getRoot();
LightList<String> list1 = ref.value; LightList<String> list1 = ref.getValueReadOnly();
ArrayList<String> list2 = new ArrayList<String>(); ArrayList<String> list2 = new ArrayList<String>();
String s = "a"; String s = "a";
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {