diff --git a/jcwdb.iml b/jcwdb.iml deleted file mode 100644 index 78b2cc5..0000000 --- a/jcwdb.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/AdvancedSaveable.java b/src/main/java/org/warp/jcwdb/AdvancedSaveable.java deleted file mode 100644 index 991edc2..0000000 --- a/src/main/java/org/warp/jcwdb/AdvancedSaveable.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.warp.jcwdb; - -public interface AdvancedSaveable extends Saveable { - public void save(boolean isEditFinished); -} diff --git a/src/main/java/org/warp/jcwdb/Editable.java b/src/main/java/org/warp/jcwdb/Editable.java new file mode 100644 index 0000000..82008dc --- /dev/null +++ b/src/main/java/org/warp/jcwdb/Editable.java @@ -0,0 +1,60 @@ +package org.warp.jcwdb; + +import java.io.IOException; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +public interface Editable { + /** + * Reccomended way to edit the value + * + * @param editFunction + * @throws IOException + */ + void editValue(BiFunction editFunction); + + /** + * Reccomended way to edit the value + * @param editFunction + * @throws IOException + */ + void editValue(Function editFunction); + + /** + * Reccomended way to edit the value + * @param editFunction + * @throws IOException + */ + void editValue(BiConsumer editFunction); + + /** + * Reccomended way to edit the value + * @param editFunction + * @throws IOException + */ + void editValue(Consumer editFunction); + + /** + * Reccomended way to view the value + * @param viewFunction + * @throws IOException + */ + void viewValue(Consumer viewFunction); + + /** + * Substitute the old value with a new one + * @param val + * @throws IOException + */ + void setValue(T val); + + + /** + * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED + * @return + */ + T getValueReadOnlyUnsafe(); + +} diff --git a/src/main/java/org/warp/jcwdb/EntryReference.java b/src/main/java/org/warp/jcwdb/EntryReference.java index 6828080..2dc4a0b 100644 --- a/src/main/java/org/warp/jcwdb/EntryReference.java +++ b/src/main/java/org/warp/jcwdb/EntryReference.java @@ -10,7 +10,7 @@ import java.util.function.Function; * You must have only a maximum of 1 reference for each index * @param */ -public class EntryReference implements Castable, AdvancedSaveable { +public class EntryReference implements Editable, Saveable, Castable { private final JCWDatabase.EntryReferenceTools db; private final long entryIndex; private final DBTypeParser parser; @@ -63,6 +63,10 @@ public class EntryReference implements Castable, AdvancedSaveable { } } + public boolean isClosed() { + return closed; + } + /** * Note that this method won't be called when closing without saving */ @@ -70,21 +74,27 @@ public class EntryReference implements Castable, AdvancedSaveable { this.save(false); } - public void save(boolean isEditFinished) { + public void saveAndFlush() { + this.save(true); + } + + private void save(boolean flush) { synchronized(accessLock) { if (loaded && !closed) { try { - if (value instanceof AdvancedSaveable) { - ((AdvancedSaveable)value).save(isEditFinished); - } else if (value instanceof Saveable) { - ((Saveable)value).save(); + if (value instanceof Saveable) { + if (flush) { + ((Saveable)value).saveAndFlush(); + } else { + ((Saveable)value).save(); + } } IndexDetails returnedDetails = this.db.write(entryIndex, parser.getWriter(value)); synchronized(hashCacheLock) { this.cachedHash = returnedDetails.getHash(); this.isHashCached = true; } - if (isEditFinished) { + if (flush) { if (!isFlushingAllowed) { this.db.setFlushingAllowed(entryIndex, true); this.isFlushingAllowed = true; @@ -149,6 +159,19 @@ public class EntryReference implements Castable, AdvancedSaveable { } } + /** + * Reccomended way to edit the value + * DO NOT EDIT THE VALUE + * @param viewFunction + * @throws IOException + */ + public void viewValue(Consumer viewFunction) { + synchronized(accessLock) { + load(); + viewFunction.accept(this.value); + } + } + /** * Substitute the old value with a new one * @param val @@ -171,14 +194,14 @@ public class EntryReference implements Castable, AdvancedSaveable { */ @Deprecated() public T getValue() { - return getValueReadOnly(); + return getValueReadOnlyUnsafe(); } /** * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED * @return */ - public T getValueReadOnly() { + public T getValueReadOnlyUnsafe() { synchronized(accessLock) { load(); return this.value; diff --git a/src/main/java/org/warp/jcwdb/JCWDatabase.java b/src/main/java/org/warp/jcwdb/JCWDatabase.java index c8c21d0..c38b57e 100644 --- a/src/main/java/org/warp/jcwdb/JCWDatabase.java +++ b/src/main/java/org/warp/jcwdb/JCWDatabase.java @@ -1,7 +1,11 @@ package org.warp.jcwdb; +import java.io.IOError; import java.io.IOException; import java.nio.file.Path; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.function.Supplier; public class JCWDatabase implements AutoCloseable, Cleanable { public final static long MAX_LOADED_INDICES = 1000; @@ -13,6 +17,7 @@ public class JCWDatabase implements AutoCloseable, Cleanable { private volatile boolean closed; private final Object closeLock = new Object(); private final Object indicesAccessLock = new Object(); + private final LinkedList> usedReferences = new LinkedList<>(); public JCWDatabase(Path dataFile, Path metadataFile) throws IOException { this.typesManager = new TypesManager(this); @@ -29,17 +34,31 @@ public class JCWDatabase implements AutoCloseable, Cleanable { this.databaseCleaner.start(); } - public EntryReference> getRoot() throws IOException { - checkClosed(); - if (exists(0)) { - return get(0); - } else { - LightList newRoot = new LightBigList<>(this); - return set(0, newRoot); + public EntryReference> getRoot() { + try { + checkClosed(); + if (exists(0)) { + return get(0); + } else { + LightList newRoot = new LightBigList<>(this); + return set(0, newRoot); + } + } catch (IOException e) { + throw new IOError(e); } } - public EntryReference> getRoot(Class clazz) throws IOException { + @SuppressWarnings("unchecked") + public EntryReference getRootItem(int index) { + return ((LightList) getRoot().getValueReadOnlyUnsafe()).getReference(index); + } + + @SuppressWarnings("unchecked") + public EntryReference getRootItem(int index, Supplier defaultValue) { + return ((LightList) getRoot().getValueReadOnlyUnsafe()).getReferenceOrInitialize(index, defaultValue); + } + + public EntryReference> getRoot(Class clazz) { return getRoot().cast(); } @@ -137,6 +156,14 @@ public class JCWDatabase implements AutoCloseable, Cleanable { @Override public long clean() { long removedItems = indices.clean(); + Iterator> usedReferencesIterator = usedReferences.iterator(); + while(usedReferencesIterator.hasNext()) { + EntryReference entryReference = usedReferencesIterator.next(); + if (entryReference.isClosed()) { + usedReferencesIterator.remove(); + removedItems += 1; + } + } return removedItems; } @@ -156,6 +183,10 @@ public class JCWDatabase implements AutoCloseable, Cleanable { public void setFlushingAllowed(long index, boolean isFlushingAllowed) { indices.setFlushingAllowed(index, isFlushingAllowed); } + + public void setUsed(EntryReference ref) { + usedReferences.add(ref); + } } @SuppressWarnings("unchecked") diff --git a/src/main/java/org/warp/jcwdb/LightArrayList.java b/src/main/java/org/warp/jcwdb/LightArrayList.java index b65087f..83dcf93 100644 --- a/src/main/java/org/warp/jcwdb/LightArrayList.java +++ b/src/main/java/org/warp/jcwdb/LightArrayList.java @@ -48,7 +48,7 @@ public class LightArrayList implements LightList { EntryReference ref = null; try { ref = db.get(element); - if (o.equals(ref.getValueReadOnly())) { + if (o.equals(ref.getValueReadOnlyUnsafe())) { return true; } } catch (IOException e) { @@ -70,7 +70,7 @@ public class LightArrayList implements LightList { final ArrayList elements = new ArrayList<>(); for (long element : internalList) { try { - elements.add((T) db.get(element).getValueReadOnly()); + elements.add((T) db.get(element).getValueReadOnlyUnsafe()); } catch (IOException e) { e.printStackTrace(); } @@ -125,7 +125,7 @@ public class LightArrayList implements LightList { final T[] elements = (T[]) new Object[internalList.size()]; for (int i = 0; i < elements.length; i++) { try { - T element = (T) db.get(internalList.getLong(i)).getValueReadOnly(); + T element = (T) db.get(internalList.getLong(i)).getValueReadOnlyUnsafe(); elements[i] = element; } catch (IOException e) { e.printStackTrace(); @@ -140,7 +140,7 @@ public class LightArrayList implements LightList { final T1[] elements = (T1[]) new Objects[internalList.size()]; for (int i = 0; i < elements.length; i++) { try { - elements[i] = (T1) db.get(internalList.getLong(i)).getValueReadOnly(); + elements[i] = (T1) db.get(internalList.getLong(i)).getValueReadOnlyUnsafe(); } catch (IOException e) { e.printStackTrace(); } @@ -265,7 +265,7 @@ public class LightArrayList implements LightList { @SuppressWarnings("unchecked") public T getReadOnlyValue(int index) { try { - return (T) db.get(internalList.getLong(index)).getValueReadOnly(); + return (T) db.get(internalList.getLong(index)).getValueReadOnlyUnsafe(); } catch (IOException e) { e.printStackTrace(); return null; @@ -289,7 +289,7 @@ public class LightArrayList implements LightList { long oldIndex = internalList.set(index, ref.getIndex()); try { ref.close(); - return ((EntryReference) (db.get(oldIndex))).getValueReadOnly(); + return ((EntryReference) (db.get(oldIndex))).getValueReadOnlyUnsafe(); } catch (IOException e) { throw (NullPointerException) new NullPointerException().initCause(e); } @@ -306,7 +306,7 @@ public class LightArrayList implements LightList { public T remove(int index) { long oldIndex = internalList.removeLong(index); try { - return ((EntryReference) (db.get(oldIndex))).getValueReadOnly(); + return ((EntryReference) (db.get(oldIndex))).getValueReadOnlyUnsafe(); } catch (IOException e) { throw (NullPointerException) new NullPointerException().initCause(e); } @@ -340,7 +340,7 @@ public class LightArrayList implements LightList { long index = internalList.getLong(i); try { EntryReference ref2 = db.get(index); - if (ref.getValueReadOnly().equals(ref2.getValueReadOnly())) { + if (ref.getValueReadOnlyUnsafe().equals(ref2.getValueReadOnlyUnsafe())) { return i; } } catch (IOException e) { @@ -372,7 +372,7 @@ public class LightArrayList implements LightList { try { EntryReference ref2 = db.get(index2); if (objToRemoveHash == ref2.calculateHash()) { - if (ref.getValueReadOnly().equals(ref2.getValueReadOnly())) { + if (ref.getValueReadOnlyUnsafe().equals(ref2.getValueReadOnlyUnsafe())) { lastValue = i; } } @@ -431,7 +431,7 @@ public class LightArrayList implements LightList { for (int i = 0; i < internalList.size(); ) { T obj; try { - obj = ((EntryReference) (db.get(internalList.getLong(i)).cast())).getValueReadOnly(); + obj = ((EntryReference) (db.get(internalList.getLong(i)).cast())).getValueReadOnlyUnsafe(); } catch (IOException e) { throw (NullPointerException) new NullPointerException().initCause(e); } diff --git a/src/main/java/org/warp/jcwdb/LightBigList.java b/src/main/java/org/warp/jcwdb/LightBigList.java index 85d68bc..915e6a4 100644 --- a/src/main/java/org/warp/jcwdb/LightBigList.java +++ b/src/main/java/org/warp/jcwdb/LightBigList.java @@ -4,11 +4,11 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.io.IOError; import java.io.IOException; import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; public class LightBigList implements LightList, Saveable { @@ -65,7 +65,7 @@ public class LightBigList implements LightList, Saveable { } catch (IOException ex) { throw (NullPointerException) new NullPointerException().initCause(ex); } - this.cachedChunk = this.cachedChunkRef.getValueReadOnly(); + this.cachedChunk = this.cachedChunkRef.getValueReadOnlyUnsafe(); } /** @@ -134,7 +134,7 @@ public class LightBigList implements LightList, Saveable { for (long chunkIndex : chunks) { try { EntryReference> chunkRef = db.get(chunkIndex); - LightArrayList chunk = chunkRef.getValueReadOnly(); + LightArrayList chunk = chunkRef.getValueReadOnlyUnsafe(); if (chunk.contains(o)) { return true; } @@ -211,7 +211,7 @@ public class LightBigList implements LightList, Saveable { try { EntryReference> chunkRef = db.get(chunkIndex); - LightArrayList chunk = chunkRef.getValueReadOnly(); + LightArrayList chunk = chunkRef.getValueReadOnlyUnsafe(); for (int i1 = 0; i1 < chunk.size(); i1++) { result[(int)(chunkStartOffset + i1)] = chunk.get(i); } @@ -378,7 +378,7 @@ public class LightBigList implements LightList, Saveable { @SuppressWarnings("unchecked") public T getReadOnlyValue(int index) { try { - return (T) db.get(chunks.getLong(index)).getValueReadOnly(); + return (T) db.get(chunks.getLong(index)).getValueReadOnlyUnsafe(); } catch (IOException e) { e.printStackTrace(); return null; @@ -387,10 +387,26 @@ public class LightBigList implements LightList, Saveable { @Override public EntryReference getReference(int index) { + return getReferenceUnsafe(index, true); + } + + @Override + public EntryReference getReferenceOrInitialize(int index, Supplier initializer) { + EntryReference value = getReferenceUnsafe(index, false); + if (value != null) { + return value; + } else { + T initializedData = initializer.get(); + EntryReference initializedDataRef = addToDatabase(initializedData); + return set(index, initializedDataRef); + } + } + + private EntryReference getReferenceUnsafe(int index, boolean throwError) { try { return db.get(chunks.getLong(index)).cast(); } catch (IOException e) { - e.printStackTrace(); + if (throwError) e.printStackTrace(); return null; } } @@ -398,8 +414,14 @@ public class LightBigList implements LightList, Saveable { @SuppressWarnings("unchecked") @Override public T set(int setOffset, final T element) { + return set(setOffset, addToDatabase(element)).getValueReadOnlyUnsafe(); + } + + @SuppressWarnings("unchecked") + @Override + public EntryReference set(int setOffset, final EntryReference element) { long nextChunkOffset = 0; - VariableWrapper wrapper = new VariableWrapper<>(null); + VariableWrapper> wrapper = new VariableWrapper<>(null); if (setOffset >= 0) { // Iterate through all chunks for (int i = 0; i < chunks.size(); i++) { @@ -420,8 +442,7 @@ public class LightBigList implements LightList, Saveable { try { EntryReference> chunkRef = db.get(chunkIndex); chunkRef.editValue((chunk) -> { - chunk.set(relativeOffset, element); - wrapper.var = element; + wrapper.var = chunk.set(relativeOffset, element); }); } catch (IOException ex) { throw (NullPointerException) new NullPointerException().initCause(ex); @@ -463,7 +484,7 @@ public class LightBigList implements LightList, Saveable { // Get chunk index final long chunkIndex = chunks.getLong(i); EntryReference> chunkRef = db.get(chunkIndex); - final int foundIndex = chunkRef.getValueReadOnly().indexOfEntry(ref); + final int foundIndex = chunkRef.getValueReadOnlyUnsafe().indexOfEntry(ref); if (foundIndex >= 0) { return currentOffset + foundIndex; } @@ -492,7 +513,7 @@ public class LightBigList implements LightList, Saveable { // Get chunk index final long chunkIndex = chunks.getLong(i); EntryReference> chunkRef = db.get(chunkIndex); - final int foundIndex = chunkRef.getValueReadOnly().lastIndexOfEntry(ref); + final int foundIndex = chunkRef.getValueReadOnlyUnsafe().lastIndexOfEntry(ref); if (foundIndex >= 0) { return currentOffset + foundIndex; } @@ -578,4 +599,9 @@ public class LightBigList implements LightList, Saveable { this.cachedChunkRef.save(); } } + + @Override + public void saveAndFlush() { + save(); + } } diff --git a/src/main/java/org/warp/jcwdb/LightList.java b/src/main/java/org/warp/jcwdb/LightList.java index 9c699ba..1f9fc7d 100644 --- a/src/main/java/org/warp/jcwdb/LightList.java +++ b/src/main/java/org/warp/jcwdb/LightList.java @@ -3,6 +3,7 @@ package org.warp.jcwdb; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; public interface LightList extends List { @@ -12,10 +13,14 @@ public interface LightList extends List { EntryReference addEntry(T o); + EntryReference set(int setOffset, final EntryReference element); + boolean remove(EntryReference ref); EntryReference getReference(int index); + EntryReference getReferenceOrInitialize(int index, Supplier initializer); + int indexOfEntry(EntryReference ref); int lastIndexOfEntry(EntryReference ref); diff --git a/src/main/java/org/warp/jcwdb/Saveable.java b/src/main/java/org/warp/jcwdb/Saveable.java index 385ee18..b8fcffb 100644 --- a/src/main/java/org/warp/jcwdb/Saveable.java +++ b/src/main/java/org/warp/jcwdb/Saveable.java @@ -3,5 +3,6 @@ package org.warp.jcwdb; import java.io.IOException; public interface Saveable { - public void save(); + void save(); + void saveAndFlush(); } diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/App.java b/src/main/java/org/warp/jcwdb/exampleimpl/App.java index b9cfe2f..60f9a49 100644 --- a/src/main/java/org/warp/jcwdb/exampleimpl/App.java +++ b/src/main/java/org/warp/jcwdb/exampleimpl/App.java @@ -63,7 +63,7 @@ public class App { System.out.println("Retrieving items..."); root.forEachReference((valueReference) -> { - Animal value = valueReference.getValueReadOnly(); + Animal value = valueReference.getValueReadOnlyUnsafe(); if (Animal.hasFourLegs(value)) { results.add(value); }