Added default parser, fixed wrong index sharing, moved app package

This commit is contained in:
Cavallium 2018-11-23 22:23:14 +01:00
parent fa692b391a
commit 69951ebc50
8 changed files with 246 additions and 46 deletions

View File

@ -0,0 +1,33 @@
package org.warp.jcwdb;
import java.io.ByteArrayOutputStream;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;
public class DBGenericObjectParser extends DBTypeParserImpl<Object> {
private static final Kryo kryo = new Kryo();
static {
kryo.setRegistrationRequired(false);
}
private static final DBReader<Object> defaultReader = (i, size) -> {
return kryo.readClassAndObject(i);
};
public DBReader<Object> getReader() {
return defaultReader;
}
public DBDataOutput<Object> getWriter(final Object value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output tmpO = new Output(baos);
kryo.writeClassAndObject(tmpO, value);
tmpO.flush();
final byte[] bytes = baos.toByteArray();
tmpO.close();
return DBDataOutput.create((o) -> {
o.write(bytes);
}, DBStandardTypes.GENERIC_OBJECT, bytes.length);
}
}

View File

@ -1,20 +1,22 @@
package org.warp.jcwdb;
public class DBStandardTypes {
private static final int STD = 0xFFFFF000;
public static final int BOOLEAN = STD| 0;
public static final int BYTE = STD| 1;
public static final int SHORT = STD| 2;
public static final int CHAR = STD| 3;
public static final int INTEGER = STD| 4;
public static final int FLOAT = STD| 5;
public static final int DOUBLE = STD| 6;
public static final int STRING = STD| 7;
public static final int BYTE_ARRAY = STD| 8;
public static final int LIGHT_LIST = STD| 9;
private static final int STD = 0xFFFFF000;
public static final int BOOLEAN = STD| 0;
public static final int BYTE = STD| 1;
public static final int SHORT = STD| 2;
public static final int CHAR = STD| 3;
public static final int INTEGER = STD| 4;
public static final int FLOAT = STD| 5;
public static final int DOUBLE = STD| 6;
public static final int STRING = STD| 7;
public static final int BYTE_ARRAY = STD| 8;
public static final int LIGHT_LIST = STD| 9;
public static final int GENERIC_OBJECT = STD| 10;
public static void registerStandardTypes(JCWDatabase db, TypesManager typesManager) {
typesManager.registerType(String.class, STRING, new DBStringParser());
typesManager.registerType(LightList.class, LIGHT_LIST, new DBLightListParser(db));
typesManager.registerTypeFallback(new DBGenericObjectParser());
}
}

View File

@ -5,13 +5,13 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
public class JCWDatabase implements AutoCloseable {
protected final TypesManager typesManager;
protected final MixedIndexDatabase indices;
private final WeakHashMap<Long, EntryReference<?>> references;
private final WeakHashMap<Object, EntryReference<?>> referencesByObject;
private volatile boolean closed;
private final Object closeLock = new Object();
private final Object indicesAccessLock = new Object();
@ -21,7 +21,6 @@ public class JCWDatabase implements AutoCloseable {
this.typesManager = new TypesManager(this);
this.indices = new MixedIndexDatabase(typesManager, dataFile, metadataFile);
this.references = new WeakHashMap<>();
this.referencesByObject = new WeakHashMap<>();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
JCWDatabase.this.close();
@ -53,7 +52,6 @@ public class JCWDatabase implements AutoCloseable {
DBTypeParser<T> typeParser = this.typesManager.get(type);
ref = new EntryReference<>(this, index, typeParser);
this.references.put(index, ref);
this.referencesByObject.put(ref.value, ref);
}
return ref;
}
@ -62,10 +60,7 @@ public class JCWDatabase implements AutoCloseable {
protected <T> EntryReference<T> add(T value) throws IOException {
checkClosed();
synchronized (referencesAccessLock) {
EntryReference<T> ref = (EntryReference<T>) referencesByObject.getOrDefault(value, null);
if (ref != null) {
return ref;
}
EntryReference<T> ref;
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
long index;
synchronized (indicesAccessLock) {
@ -73,7 +68,6 @@ public class JCWDatabase implements AutoCloseable {
}
ref = new EntryReference<>(this, index, typeParser, value);
this.references.put(index, ref);
this.referencesByObject.put(value, ref);
return ref;
}
}
@ -102,7 +96,6 @@ public class JCWDatabase implements AutoCloseable {
}
ref = new EntryReference<>(this, index, typeParser);
this.references.put(index, ref);
this.referencesByObject.put(value, ref);
return ref;
}
}
@ -115,7 +108,7 @@ public class JCWDatabase implements AutoCloseable {
}
@Override
public void close() throws Exception {
public void close() throws IOException {
if (closed) {
return;
}

View File

@ -0,0 +1,14 @@
package org.warp.jcwdb;
public class NoParserFoundException extends NullPointerException {
public NoParserFoundException(String string) {
super(string);
}
/**
*
*/
private static final long serialVersionUID = 701010818132241139L;
}

View File

@ -7,6 +7,7 @@ import java.util.HashMap;
public class TypesManager {
private final Map<Integer, DBTypeParser<?>> types;
private final Map<Class<?>, DBTypeParser<?>> typesByClass;
private DBTypeParser<?> fallbackParser;
public TypesManager(JCWDatabase db) {
types = new HashMap<>();
@ -19,11 +20,29 @@ public class TypesManager {
this.typesByClass.put(clazz, parser);
}
public void registerTypeFallback(DBTypeParser<?> parser) {
this.fallbackParser = parser;
}
public <T> DBTypeParser<T> get(int type) {
if (types.containsKey(type) == false) {
if (fallbackParser == null) {
throw new NoParserFoundException("The type " + type + " can't be parsed.");
} else {
return fallbackParser.cast();
}
}
return types.get(type).cast();
}
public <T> DBTypeParser<T> get(Class<T> type) {
if (typesByClass.containsKey(type) == false) {
if (fallbackParser == null) {
throw new NoParserFoundException("The class " + type.getSimpleName() + " can't be parsed.");
} else {
return fallbackParser.cast();
}
}
return typesByClass.get(type).cast();
}
}

View File

@ -13,14 +13,28 @@ public class App {
Files.delete(Paths.get(args[0]));
Files.delete(Paths.get(args[1]));
}
System.out.println("Loading database...");
long time0 = System.currentTimeMillis();
JCWDatabase db = new JCWDatabase(Paths.get(args[0]), Paths.get(args[1]));
long time01 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time01 - time0));
System.out.println("Loading root...");
LightList<String> root = ((EntryReference<LightList<String>>) db.getRoot().cast()).value;
long time1 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time1 - time01));
System.out.println("Root:");
for (int i = 0; i < root.size(); i++) {
System.out.println(" - " + root.get(i));
}
for (int i = 0; i < 100; i++) {
root.add("Test " + System.currentTimeMillis());
// for (int i = 0; i < root.size(); i++) {
// System.out.println(" - " + root.get(i));
// }
for (int i = 0; i < 500000000; i++) {
root.add("Test " + i);
}
long time2 = System.currentTimeMillis();
System.out.println("Root size: "+root.size());
System.out.println("Time elapsed: " + (time2 - time1));
System.out.println("Saving database...");
db.close();
long time3 = System.currentTimeMillis();
System.out.println("Time elapsed: " + (time3 - time2));
}
}

View File

@ -1,20 +0,0 @@
package org.warp;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}

View File

@ -0,0 +1,145 @@
package org.warp.jcwdb;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import org.junit.Test;
import org.warp.jcwdb.EntryReference;
import org.warp.jcwdb.JCWDatabase;
import org.warp.jcwdb.LightList;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
/**
*
* @throws IOException
*/
@Test
public void shouldReadCustomClasses() throws IOException
{
CustomClass customClass = new CustomClass();
customClass.primitive = 1;
customClass.string = "test";
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
JCWDatabase db = new JCWDatabase(path, idx);
EntryReference<LightList<CustomClass>> ref = db.getRoot();
LightList<CustomClass> root = ref.value;
root.add(customClass);
db.close();
JCWDatabase db2 = new JCWDatabase(path, idx);
EntryReference<LightList<CustomClass>> ref2 = db2.getRoot();
LightList<CustomClass> root2 = ref2.value;
CustomClass customClass2 = root2.get(0);
assertTrue(customClass.equals(customClass2));
assertTrue(customClass.string.equals(customClass2.string));
db2.close();
Files.delete(path);
Files.delete(idx);
}
public static class CustomClass {
public String string;
public int primitive;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + primitive;
result = prime * result + ((string == null) ? 0 : string.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomClass other = (CustomClass) obj;
if (primitive != other.primitive)
return false;
if (string == null) {
if (other.string != null)
return false;
} else if (!string.equals(other.string))
return false;
return true;
}
}
/**
*
* @throws IOException
*/
@Test
public void shouldActAsAnArrayListWithEqualObjects() throws IOException
{
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
JCWDatabase db = new JCWDatabase(path, idx);
EntryReference<LightList<String>> ref = db.getRoot();
LightList<String> list1 = ref.value;
ArrayList<String> list2 = new ArrayList<String>();
String s = "a";
for (int i = 0; i < 10; 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);
}
/**
*
* @throws IOException
*/
@Test
public void shouldActAsAnArrayListWithDifferentObjects() throws IOException
{
Path path = Files.createTempFile("", ".db"), idx = Files.createTempFile("", ".idx");
JCWDatabase db = new JCWDatabase(path, idx);
EntryReference<LightList<String>> ref = db.getRoot();
LightList<String> list1 = ref.value;
ArrayList<String> list2 = new ArrayList<String>();
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);
}
}