diff --git a/pom.xml b/pom.xml index 6da871f..ba64ed1 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,11 @@ kryo 5.0.0-RC1 + + net.openhft + zero-allocation-hashing + 0.8 + diff --git a/src/main/java/org/warp/jcwdb/CacheIndexManager.java b/src/main/java/org/warp/jcwdb/CacheIndexManager.java index 31c9cb9..ad8ef24 100644 --- a/src/main/java/org/warp/jcwdb/CacheIndexManager.java +++ b/src/main/java/org/warp/jcwdb/CacheIndexManager.java @@ -45,4 +45,10 @@ public class CacheIndexManager implements IndexManager { public void close() { // TODO: implement } + + @Override + public long clean() { + // TODO Auto-generated method stub + return 0; + } } diff --git a/src/main/java/org/warp/jcwdb/DBDataOutput.java b/src/main/java/org/warp/jcwdb/DBDataOutput.java index ef20a51..6af18e9 100644 --- a/src/main/java/org/warp/jcwdb/DBDataOutput.java +++ b/src/main/java/org/warp/jcwdb/DBDataOutput.java @@ -3,9 +3,10 @@ package org.warp.jcwdb; public interface DBDataOutput { int getSize(); int getType(); + long calculateHash(); DBWriter getWriter(); - static DBDataOutput create(DBWriter writer, int type, int size) { + static DBDataOutput create(DBWriter writer, int type, int size, long hash) { return new DBDataOutput() { @Override @@ -17,6 +18,11 @@ public interface DBDataOutput { public int getType() { return type; } + + @Override + public long calculateHash() { + return hash; + } @Override public DBWriter getWriter() { diff --git a/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java b/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java index 1bb7c8a..58dd9ca 100644 --- a/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java +++ b/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java @@ -5,7 +5,10 @@ import java.io.ByteArrayOutputStream; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Output; +import net.openhft.hashing.LongHashFunction; + public class DBGenericObjectParser extends DBTypeParserImpl { + private static final LongHashFunction hashFunction = net.openhft.hashing.LongHashFunction.xx(); private static final Kryo kryo = new Kryo(); static { kryo.setRegistrationRequired(false); @@ -25,9 +28,22 @@ public class DBGenericObjectParser extends DBTypeParserImpl { kryo.writeClassAndObject(tmpO, value); tmpO.flush(); final byte[] bytes = baos.toByteArray(); + final long hash = hashFunction.hashBytes(bytes); tmpO.close(); return DBDataOutput.create((o) -> { o.write(bytes); - }, DBStandardTypes.GENERIC_OBJECT, bytes.length); + }, DBStandardTypes.GENERIC_OBJECT, bytes.length, hash); + } + + @Override + public long calculateHash(Object value) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Output tmpO = new Output(baos); + kryo.writeClassAndObject(tmpO, value); + tmpO.flush(); + final byte[] bytes = baos.toByteArray(); + final long hash = hashFunction.hashBytes(bytes); + tmpO.close(); + return hash; } } diff --git a/src/main/java/org/warp/jcwdb/DBLightListParser.java b/src/main/java/org/warp/jcwdb/DBLightListParser.java index 875cd37..c5e1e0e 100644 --- a/src/main/java/org/warp/jcwdb/DBLightListParser.java +++ b/src/main/java/org/warp/jcwdb/DBLightListParser.java @@ -2,6 +2,8 @@ package org.warp.jcwdb; import java.util.ArrayList; +import it.unimi.dsi.fastutil.longs.LongArrayList; + public class DBLightListParser extends DBTypeParserImpl { private final JCWDatabase db; @@ -11,23 +13,33 @@ public class DBLightListParser extends DBTypeParserImpl { public DBReader getReader() { return (i, size) -> { - ArrayList internalList = new ArrayList<>(); - long max = size / Long.BYTES; + LongArrayList internalList = new LongArrayList(); + LongArrayList hashes = new LongArrayList(); + long max = size / (Long.BYTES * 2); for (int item = 0; item < max; item++){ long itm = i.readLong(); + long hash = i.readLong(); internalList.add(itm); + hashes.add(hash); } - return new LightList(db, internalList); + return new LightList(db, internalList, hashes); }; } public DBDataOutput getWriter(final LightList value) { - final int elementsCount = value.internalList.size(); + final int elementsCount = value.size(); return DBDataOutput.create((o) -> { - ArrayList list = value.internalList; - for (Long item : list) { - o.writeLong(item); + LongArrayList list = value.internalList; + LongArrayList hashes = value.hashes; + for (int i = 0; i < elementsCount; i++) { + o.writeLong(list.getLong(i)); + o.writeLong(hashes.getLong(i)); } - }, DBStandardTypes.LIGHT_LIST, elementsCount * Long.BYTES); + }, DBStandardTypes.LIGHT_LIST, elementsCount * Long.BYTES * 2, calculateHash(value)); + } + + @Override + public long calculateHash(LightList value) { + return value.hashCodeLong(); } } diff --git a/src/main/java/org/warp/jcwdb/DBStringParser.java b/src/main/java/org/warp/jcwdb/DBStringParser.java index b446a01..d7e8061 100644 --- a/src/main/java/org/warp/jcwdb/DBStringParser.java +++ b/src/main/java/org/warp/jcwdb/DBStringParser.java @@ -1,12 +1,13 @@ package org.warp.jcwdb; -import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; -import com.esotericsoftware.kryo.io.Output; +import net.openhft.hashing.LongHashFunction; public class DBStringParser extends DBTypeParserImpl { + private static final LongHashFunction hashFunction = net.openhft.hashing.LongHashFunction.xx(); private static final DBReader defaultReader = (i, size) -> { - return i.readString(); + return new String(i.readBytes(size), StandardCharsets.UTF_16LE); }; public DBReader getReader() { @@ -14,14 +15,13 @@ public class DBStringParser extends DBTypeParserImpl { } public DBDataOutput getWriter(final String value) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Output tmpO = new Output(baos); - tmpO.writeString(value); - tmpO.flush(); - final byte[] bytes = baos.toByteArray(); - tmpO.close(); return DBDataOutput.create((o) -> { - o.write(bytes); - }, DBStandardTypes.STRING, bytes.length); + o.write(value.getBytes(StandardCharsets.UTF_16LE)); + }, DBStandardTypes.STRING, value.length() * 2, calculateHash(value)); + } + + @Override + public long calculateHash(String value) { + return hashFunction.hashBytes(value.getBytes(StandardCharsets.UTF_16LE)); } } diff --git a/src/main/java/org/warp/jcwdb/DBTypeParser.java b/src/main/java/org/warp/jcwdb/DBTypeParser.java index 12d8750..3eecd54 100644 --- a/src/main/java/org/warp/jcwdb/DBTypeParser.java +++ b/src/main/java/org/warp/jcwdb/DBTypeParser.java @@ -3,4 +3,5 @@ package org.warp.jcwdb; public interface DBTypeParser extends Castable { DBReader getReader(); DBDataOutput getWriter(final T value); + long calculateHash(final T value); } diff --git a/src/main/java/org/warp/jcwdb/EntryReference.java b/src/main/java/org/warp/jcwdb/EntryReference.java index 4bc3a94..866b661 100644 --- a/src/main/java/org/warp/jcwdb/EntryReference.java +++ b/src/main/java/org/warp/jcwdb/EntryReference.java @@ -18,7 +18,7 @@ public class EntryReference implements Castable, AutoCloseable { this.db = db; this.entryIndex = entryId; this.parser = parser; - this.value = db.indices.get(entryId, parser.getReader()); + this.value = db.getIndexManager().get(entryId, parser.getReader()); } public EntryReference(JCWDatabase db, long entryId, DBTypeParser parser, T value) { @@ -35,10 +35,18 @@ public class EntryReference implements Castable, AutoCloseable { public long getIndex() { return entryIndex; } + + public long calculateHash() { + return parser.calculateHash(value); + } + /** + * Note that this method won't be called when closing without saving + * @throws IOException + */ public void save() throws IOException { if (!closed) { - db.indices.set(entryIndex, parser.getWriter(value)); + db.getIndexManager().set(entryIndex, parser.getWriter(value)); } } @@ -57,8 +65,23 @@ public class EntryReference implements Castable, AutoCloseable { return; } - db.removeEntryReference(entryIndex); save(); + db.removeEntryReference(entryIndex); + + closed = true; + } + } + + public void closeWithoutSaving() { + if (closed) { + return; + } + synchronized (closeLock) { + if (closed) { + return; + } + + db.removeEntryReference(entryIndex); closed = true; } diff --git a/src/main/java/org/warp/jcwdb/FileIndexManager.java b/src/main/java/org/warp/jcwdb/FileIndexManager.java index 161cec8..872eac8 100644 --- a/src/main/java/org/warp/jcwdb/FileIndexManager.java +++ b/src/main/java/org/warp/jcwdb/FileIndexManager.java @@ -78,11 +78,11 @@ public class FileIndexManager implements IndexManager { public void set(long index, DBDataOutput data) throws IOException { checkClosed(); final IndexDetails indexDetails = getIndexMetadataUnsafe(index); - if (indexDetails == null || indexDetails.getSize() < data.getSize()) { + if (indexDetails == null || indexDetails.getSize() != data.getSize()) { // TODO: should be indexDetails.getSize() < data.getSize(). Need to create a method to mark memory free if the size is bigger than the needed, instead of allocating a new space. allocateAndWrite(index, data); } else { if (indexDetails.getSize() > data.getSize()) { - editIndex(index, indexDetails.getOffset(), data.getSize(), indexDetails.getType()); + editIndex(index, indexDetails.getOffset(), data.getSize(), indexDetails.getType(), data.calculateHash()); fileAllocator.markFree(indexDetails.getOffset()+data.getSize(), data.getSize()); } writeExact(indexDetails, data); @@ -95,7 +95,8 @@ public class FileIndexManager implements IndexManager { final int size = data.getSize(); final long offset = fileAllocator.allocate(size); final int type = data.getType(); - final IndexDetails indexDetails = new IndexDetails(offset, size, type); + final long hash = data.calculateHash(); + final IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); final long index = createIndexMetadata(indexDetails); writeExact(indexDetails, data); return index; @@ -123,8 +124,9 @@ public class FileIndexManager implements IndexManager { private void allocateAndWrite(final long index, DBDataOutput w) throws IOException { final int size = w.getSize(); final int type = w.getType(); + final long hash = w.calculateHash(); final long offset = fileAllocator.allocate(size); - IndexDetails details = editIndex(index, offset, size, type); + IndexDetails details = editIndex(index, offset, size, type, hash); writeExact(details, w); } @@ -181,8 +183,8 @@ public class FileIndexManager implements IndexManager { } } - private IndexDetails editIndex(long index, long offset, int size, int type) { - IndexDetails indexDetails = new IndexDetails(offset, size, type); + private IndexDetails editIndex(long index, long offset, int size, int type, long hash) { + IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); editIndex(index, indexDetails); return indexDetails; } @@ -226,9 +228,13 @@ public class FileIndexManager implements IndexManager { // If it's not deleted continue if ((metadataByteBuffer.getInt() & IndexDetails.MASK_DELETED) == 0) { final long offset = metadataByteBuffer.getLong(); +// final long sizeAndType = metadataByteBuffer.getLong(); +// final int size = (int)(sizeAndType >> 32); +// final int type = (int)sizeAndType; final int size = metadataByteBuffer.getInt(); final int type = metadataByteBuffer.getInt(); - final IndexDetails indexDetails = new IndexDetails(offset, size, type); + final long hash = metadataByteBuffer.getLong(); + final IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); editIndex(index, indexDetails); return indexDetails; } @@ -284,11 +290,17 @@ public class FileIndexManager implements IndexManager { private void writeIndexDetails(SeekableByteChannel position, IndexDetails indexDetails) throws IOException { synchronized (metadataByteBufferLock) { + final int size = indexDetails.getSize(); + final int type = indexDetails.getType(); + final long offset = indexDetails.getOffset(); + final long hash = indexDetails.getHash(); metadataByteBuffer.rewind(); metadataByteBuffer.putInt(0); - metadataByteBuffer.putLong(indexDetails.getOffset()); - metadataByteBuffer.putInt(indexDetails.getSize()); - metadataByteBuffer.putInt(indexDetails.getType()); + metadataByteBuffer.putLong(offset); + metadataByteBuffer.putInt(size); + metadataByteBuffer.putInt(type); + //metadataByteBuffer.putLong((long)size << 32 | type & 0xFFFFFFFFL); + metadataByteBuffer.putLong(hash); metadataByteBuffer.rewind(); position.write(metadataByteBuffer); } diff --git a/src/main/java/org/warp/jcwdb/IndexDetails.java b/src/main/java/org/warp/jcwdb/IndexDetails.java index 67a7e31..ab4f1de 100644 --- a/src/main/java/org/warp/jcwdb/IndexDetails.java +++ b/src/main/java/org/warp/jcwdb/IndexDetails.java @@ -10,22 +10,26 @@ public class IndexDetails { public static final int OFFSET_BYTES = Long.BYTES; public static final int DATA_SIZE_BYTES = Integer.BYTES; public static final int TYPE_BYTES = Integer.BYTES; - public static final int TOTAL_BYTES = BITMASK_SIZE + OFFSET_BYTES + DATA_SIZE_BYTES + TYPE_BYTES; + public static final int HASH_BYTES = Long.BYTES; + public static final int TOTAL_BYTES = BITMASK_SIZE + OFFSET_BYTES + DATA_SIZE_BYTES + TYPE_BYTES + HASH_BYTES; public static final int MASK_DELETED = 0b00000001; private final long offset; private final int size; private final int type; + private final long hash; - public IndexDetails(long offset, int size, int type) { + public IndexDetails(long offset, int size, int type, long hash) { this.offset = offset; this.size = size; this.type = type; + this.hash = hash; } public IndexDetails(IndexDetails indexDetails) { this.offset = indexDetails.offset; this.size = indexDetails.size; this.type = indexDetails.type; + this.hash = indexDetails.hash; } public long getOffset() { @@ -39,29 +43,46 @@ public class IndexDetails { public int getType() { return type; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - IndexDetails that = (IndexDetails) o; - return offset == that.offset && - size == that.size && - type == that.type; + + public long getHash() { + return hash; } @Override public int hashCode() { - return Objects.hash(offset); + final int prime = 31; + int result = 1; + result = prime * result + (int) (hash ^ (hash >>> 32)); + result = prime * result + (int) (offset ^ (offset >>> 32)); + result = prime * result + size; + result = prime * result + type; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + IndexDetails other = (IndexDetails) obj; + if (hash != other.hash) + return false; + if (offset != other.offset) + return false; + if (size != other.size) + return false; + if (type != other.type) + return false; + return true; } @Override public String toString() { - return "IndexDetails{" + - "offset=" + offset + - ", size=" + size + - ", type=" + type + - '}'; + return "IndexDetails [offset=" + offset + ", size=" + size + ", type=" + type + ", hash=" + hash + "]"; } + } diff --git a/src/main/java/org/warp/jcwdb/JCWDatabase.java b/src/main/java/org/warp/jcwdb/JCWDatabase.java index f57d473..f65e4e6 100644 --- a/src/main/java/org/warp/jcwdb/JCWDatabase.java +++ b/src/main/java/org/warp/jcwdb/JCWDatabase.java @@ -8,6 +8,8 @@ import java.util.ArrayList; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.objects.ObjectIterator; public class JCWDatabase implements AutoCloseable, Cleanable { public final static long MAX_LOADED_REFERENCES = 10; @@ -39,11 +41,15 @@ public class JCWDatabase implements AutoCloseable, Cleanable { } public EntryReference> getRoot() throws IOException { + return getRoot(Object.class).cast(); + } + + public EntryReference> getRoot(Class clazz) throws IOException { checkClosed(); if (exists(0)) { return get(0); } else { - LightList newRoot = new LightList(this, new ArrayList<>()); + LightList newRoot = new LightList(this, new LongArrayList(), new LongArrayList()); return set(0, newRoot); } } @@ -130,7 +136,9 @@ public class JCWDatabase implements AutoCloseable, Cleanable { } synchronized (referencesAccessLock) { - for (WeakReference> referenceRef : references.values()) { + ObjectIterator>> it = references.values().iterator(); + while (it.hasNext()) { + WeakReference> referenceRef = it.next(); EntryReference reference = referenceRef.get(); if (reference != null) { reference.close(); @@ -187,4 +195,17 @@ public class JCWDatabase implements AutoCloseable, Cleanable { } return removedReferences; } + + @SuppressWarnings("unchecked") + protected long calculateHash(T o) { + return ((DBTypeParser) typesManager.get(o.getClass())).calculateHash(o); + } + + /** + * + * @return indexManager + */ + public IndexManager getIndexManager() { + return indices; + } } diff --git a/src/main/java/org/warp/jcwdb/LightList.java b/src/main/java/org/warp/jcwdb/LightList.java index dcd22c5..862b38f 100644 --- a/src/main/java/org/warp/jcwdb/LightList.java +++ b/src/main/java/org/warp/jcwdb/LightList.java @@ -2,14 +2,20 @@ package org.warp.jcwdb; import java.io.IOException; import java.util.*; +import java.util.function.Predicate; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class LightList implements List { - public final ArrayList internalList; + public final LongArrayList internalList; + public final LongArrayList hashes; private final transient JCWDatabase db; - public LightList(JCWDatabase db, ArrayList internalList) { + public LightList(JCWDatabase db, LongArrayList internalList, LongArrayList hashes) { this.internalList = internalList; + this.hashes = hashes; this.db = db; } @@ -27,8 +33,9 @@ public class LightList implements List { public boolean contains(Object o) { if (o != null) { for (Long element : internalList) { + EntryReference ref = null; try { - EntryReference ref = db.get(element); + ref = db.get(element); if (o.equals(ref.value)) { return true; } @@ -40,6 +47,7 @@ public class LightList implements List { return false; } + @SuppressWarnings("unchecked") @Override public Iterator iterator() { final ArrayList elements = new ArrayList<>(); @@ -53,12 +61,13 @@ public class LightList implements List { return elements.iterator(); } + @SuppressWarnings("unchecked") @Override public T[] toArray() { final T[] elements = (T[]) new Objects[internalList.size()]; for (int i = 0; i < elements.length; i++) { try { - elements[i] = (T) db.get(internalList.get(i)).value; + elements[i] = (T) db.get(internalList.getLong(i)).value; } catch (IOException e) { e.printStackTrace(); } @@ -66,12 +75,13 @@ public class LightList implements List { return elements; } + @SuppressWarnings("unchecked") @Override public T1[] toArray(T1[] a) { final T1[] elements = (T1[]) new Objects[internalList.size()]; for (int i = 0; i < elements.length; i++) { try { - elements[i] = (T1) db.get(internalList.get(i)).value; + elements[i] = (T1) db.get(internalList.getLong(i)).value; } catch (IOException e) { e.printStackTrace(); } @@ -81,117 +91,284 @@ public class LightList implements List { @Override public boolean add(T o) { - EntryReference ref; - try { - ref = db.add(o); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); + EntryReference ref = addEntry(o); + return ref != null; + } + + public EntryReference addEntry(T o) { + EntryReference ref = addToDatabase(o); + if (internalList.add(ref.getIndex())) { + hashes.add(ref.getParser().calculateHash(ref.value)); + } else { + return null; } - return internalList.add(ref.getIndex()); + return ref; } @Override public boolean remove(Object o) { - EntryReference ref; - try { - ref = db.add((T) o); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); + int removeIndex = indexOf(o); + if (removeIndex >= 0) { + internalList.removeLong(removeIndex); + hashes.removeLong(removeIndex); + return true; } - return internalList.remove(ref.getIndex()); + return false; + } + + public boolean remove(EntryReference ref) { + int removeIndex = indexOfEntry(ref); + if (removeIndex >= 0) { + internalList.removeLong(removeIndex); + hashes.removeLong(removeIndex); + return true; + } + return false; } @Override public boolean containsAll(Collection c) { - // TODO: implement - return false; + for (Object o : c) { + int objIndex = indexOf(o); + if (objIndex < 0) { + return false; + } + } + return true; } + @SuppressWarnings("unchecked") @Override public boolean addAll(Collection c) { - // TODO: implement - return false; + boolean result = false; + for (Object o : c) { + result |= add((T) o); + } + return result; } + @SuppressWarnings("unchecked") @Override public boolean addAll(int index, Collection c) { - // TODO: implement - return false; + boolean result = false; + int delta = 0; + for (Object o : c) { + add(index + delta, (T) o); + result = true; + delta++; + } + return result; } + @SuppressWarnings("unchecked") @Override public boolean removeAll(Collection c) { - // TODO: implement - return false; + boolean result = false; + for (Object o : c) { + result |= remove((T) o); + } + return result; } @Override public boolean retainAll(Collection c) { - // TODO: implement - return false; + boolean result = false; + LongArrayList collectionHashes = new LongArrayList(); + ObjectArrayList collection = new ObjectArrayList<>(); + collection.addAll(c); + for (Object o : c) { + collectionHashes.add(db.calculateHash(o)); + } + for (int i = 0; i < internalList.size(); i++) { + long hash = internalList.getLong(i); + int positionInCollection = collectionHashes.indexOf(hash); + if (positionInCollection == -1) { + remove(collection.get(positionInCollection)); + result = true; + } + } + return result; } @Override public void clear() { - // TODO: implement - + internalList.clear(); + hashes.clear(); } + @SuppressWarnings("unchecked") @Override public T get(int index) { try { - return (T) db.get(internalList.get(index)).value; + return (T) db.get(internalList.getLong(index)).value; } catch (IOException e) { e.printStackTrace(); return null; } } + @SuppressWarnings("unchecked") @Override public T set(int index, T element) { - // TODO: implement - return null; + EntryReference ref = addToDatabase(element); + long oldIndex = internalList.set(index, ref.getIndex()); + hashes.set(index, ref.calculateHash()); + try { + ref.close(); + return ((EntryReference) (db.get(oldIndex))).value; + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } } @Override public void add(int index, T element) { - // TODO: implement - + EntryReference ref = addToDatabase(element); + internalList.add(index, ref.getIndex()); + hashes.add(index, ref.getParser().calculateHash(ref.value)); } + @SuppressWarnings("unchecked") @Override public T remove(int index) { - // TODO: implement - return null; + long oldIndex = internalList.removeLong(index); + hashes.removeLong(index); + try { + return ((EntryReference) (db.get(oldIndex))).value; + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } } @Override public int indexOf(Object o) { - // TODO: implement - return 0; + EntryReference ref = addToDatabase(o); + long objToRemoveHash = ref.calculateHash(); + + for (int i = 0; i < hashes.size(); i++) { + long hash = hashes.getLong(i); + if (objToRemoveHash == hash) { + try { + if (ref.equals(db.get(internalList.getLong(i)))) { + return i; + } + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } + } + } + return -1; + } + + public int indexOfEntry(EntryReference ref) { + long objToRemoveHash = ref.calculateHash(); + + for (int i = 0; i < hashes.size(); i++) { + long hash = hashes.getLong(i); + if (objToRemoveHash == hash) { + try { + if (ref.equals(db.get(internalList.getLong(i)))) { + return i; + } + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } + } + } + return -1; } @Override public int lastIndexOf(Object o) { - // TODO: implement - return 0; + EntryReference ref = addToDatabase(o); + long objToRemoveHash = ref.calculateHash(); + + int lastValue = -1; + + for (int i = 0; i < hashes.size(); i++) { + long hash = hashes.getLong(i); + if (objToRemoveHash == hash) { + try { + if (ref.equals(db.get(internalList.getLong(i)))) { + lastValue = i; + } + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } + } + } + return lastValue; } + @Deprecated @Override public ListIterator listIterator() { // TODO: implement - return null; + throw new RuntimeException("Not implemented!"); } + @Deprecated @Override public ListIterator listIterator(int index) { // TODO: implement - return null; + throw new RuntimeException("Not implemented!"); } + @Deprecated @Override public List subList(int fromIndex, int toIndex) { // TODO: implement - return null; + throw new RuntimeException("Not implemented!"); + } + + private EntryReference addToDatabase(U obj) { + EntryReference ref; + try { + ref = db.add(obj); + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } + return ref; + } + + @Deprecated + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((hashes == null) ? 0 : hashes.hashCode()); + result = prime * result + ((internalList == null) ? 0 : internalList.hashCode()); + 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") + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + boolean removed = false; + for (int i = 0; i < internalList.size(); ) { + T obj; + try { + obj = ((EntryReference) (db.get(internalList.getLong(i)).cast())).value; + } catch (IOException e) { + throw (NullPointerException) new NullPointerException().initCause(e); + } + if (filter.test(obj)) { + internalList.removeLong(i); + hashes.removeLong(i); + removed = true; + } else { + i++; + } + } + return removed; } } diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java b/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java new file mode 100644 index 0000000..1f27eb9 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java @@ -0,0 +1,9 @@ +package org.warp.jcwdb.exampleimpl; + +public abstract class Animal { + protected int legsCount; + + public static boolean hasFourLegs(Animal a) { + return a.legsCount == 4; + } +} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/App.java b/src/main/java/org/warp/jcwdb/exampleimpl/App.java index e76a69b..c61d6dc 100644 --- a/src/main/java/org/warp/jcwdb/exampleimpl/App.java +++ b/src/main/java/org/warp/jcwdb/exampleimpl/App.java @@ -4,8 +4,12 @@ import org.warp.jcwdb.EntryReference; import org.warp.jcwdb.JCWDatabase; import org.warp.jcwdb.LightList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; + import java.nio.file.Files; import java.nio.file.Paths; +import java.util.function.Predicate; public class App { public static void main(String[] args) throws Exception { @@ -19,29 +23,55 @@ public class App { long time01 = System.currentTimeMillis(); System.out.println("Time elapsed: " + (time01 - time0)); System.out.println("Loading root..."); - LightList root = ((EntryReference>) db.getRoot().cast()).value; + EntryReference> rootRef = db.getRoot(Animal.class); + LightList root = rootRef.value; long time1 = System.currentTimeMillis(); System.out.println("Time elapsed: " + (time1 - time01)); + System.out.println("Root size: " + root.size()); System.out.println("Root:"); // for (int i = 0; i < root.size(); i++) { // System.out.println(" - " + root.get(i)); // } long prectime = System.currentTimeMillis(); - for (int i = 0; i < 2/*2000000*/; i++) { - root.add("Test " + i); + for (int i = 0; i < 2000000000/* 2000000 */; i++) { + Animal animal = new StrangeAnimal(i % 40); + root.add(animal); if (i > 0 && i % 200000 == 0) { long precprectime = prectime; prectime = System.currentTimeMillis(); - System.out.println("Element "+i + " ("+(prectime-precprectime)+"ms)"); + System.out.println("Element " + i + " (" + (prectime - precprectime) + "ms)"); } } long time2 = System.currentTimeMillis(); - System.out.println("Root size: "+root.size()); + System.out.println("Root size: " + root.size()); System.out.println("Time elapsed: " + (time2 - time1)); - 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"); + long time2_0 = System.currentTimeMillis(); + System.out.println("Filtering strings..."); + //root.removeIf(Animal::hasFourLegs); + long time2_1 = System.currentTimeMillis(); + System.out.println("Time elapsed: " + (time2_1 - time2_0)); + ObjectList results = new ObjectArrayList<>(); + + root.forEach((value) -> { + if (Animal.hasFourLegs(value)) { + results.add(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("Saving database..."); + System.out.println("Root size: " + root.size()); db.close(); long time3 = System.currentTimeMillis(); - System.out.println("Time elapsed: " + (time3 - time2)); + System.out.println("Time elapsed: " + (time3 - time2_2)); + } + + public static Predicate not(Predicate t) { + return t.negate(); } } diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java b/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java new file mode 100644 index 0000000..d289170 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java @@ -0,0 +1,12 @@ +package org.warp.jcwdb.exampleimpl; + +public class Cat extends Animal { + public Cat() { + this.legsCount = 12; + } + + @Override + public String toString() { + return "Cat [legsCount=" + legsCount + "]"; + } +} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java b/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java new file mode 100644 index 0000000..78b7489 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java @@ -0,0 +1,12 @@ +package org.warp.jcwdb.exampleimpl; + +public class Dog extends Animal { + public Dog() { + this.legsCount = 4; + } + + @Override + public String toString() { + return "Dog [legsCount=" + legsCount + "]"; + } +} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java b/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java new file mode 100644 index 0000000..4e3d84b --- /dev/null +++ b/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java @@ -0,0 +1,15 @@ +package org.warp.jcwdb.exampleimpl; + +public class StrangeAnimal extends Animal { + public StrangeAnimal() { + super(); + } + public StrangeAnimal(int legs) { + super(); + this.legsCount = legs; + } + @Override + public String toString() { + return "StrangeAnimal [legsCount=" + legsCount + "]"; + } +} diff --git a/src/test/java/org/warp/jcwdb/AppTest.java b/src/test/java/org/warp/jcwdb/AppTest.java index 0f9c65b..b6b8aeb 100644 --- a/src/test/java/org/warp/jcwdb/AppTest.java +++ b/src/test/java/org/warp/jcwdb/AppTest.java @@ -115,31 +115,4 @@ public class AppTest Files.delete(path); Files.delete(idx); } - - /** - * - * @throws IOException - */ - @Test - public void shouldActAsAnArrayListWithDifferentObjects() throws IOException - { - Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx"); - JCWDatabase db = new JCWDatabase(path, idx); - EntryReference> ref = db.getRoot(); - LightList list1 = ref.value; - ArrayList list2 = new ArrayList(); - for (int i = 0; i < 10; i++) { - String s = String.valueOf(i); - list1.add(s); - list2.add(s); - } - assertTrue(list1.size() == list2.size()); - for (int i = 0; i < 10; i++) { - assertTrue(list1.get(i) == list2.get(i)); - assertTrue(list2.get(i) == list1.get(i)); - } - db.close(); - Files.delete(path); - Files.delete(idx); - } }