[PlutoUSS2/PlutoLib] Added PlutoUSS2

This commit is contained in:
Tefek 2021-01-16 17:44:44 +01:00
parent f61c47b294
commit a4d5d102f2
8 changed files with 403 additions and 3 deletions

View File

@ -30,6 +30,7 @@ version numbers.*
* **PlutoShader** - Stable * **PlutoShader** - Stable
* **PlutoSpriteSheet** - Stable, some features are unfinished * **PlutoSpriteSheet** - Stable, some features are unfinished
* **PlutoStatic** - Stable, collision API nowhere near completion * **PlutoStatic** - Stable, collision API nowhere near completion
* **PlutoUSS2** - Stable
### Unstable submodules ### Unstable submodules
* **PlutoAudio** - Somewhat usable, unfinished * **PlutoAudio** - Somewhat usable, unfinished
@ -64,7 +65,7 @@ See `NEXT_RELEASE_DRAFT.md` for details.
[ *Items not required immediately, planned to be implemented eventually.* ] [ *Items not required immediately, planned to be implemented eventually.* ]
* Allow multiple running instances of Pluto * Allow multiple running instances of Pluto
* Alternatively, if this deems too difficult to implement, * Alternatively, if this deems too difficult to implement,
prohibit the creation of more than instance per VM to avoid issues prohibit the creation of more than one instance per JVM to avoid issues
* A networking API * A networking API
* Expand upon the Color API * Expand upon the Color API
* Color mixing and blending * Color mixing and blending

View File

@ -1,4 +1,6 @@
## 20.2.0.0-alpha.3 ## 20.2.0.0-alpha.3
* `[PlutoUSS2]` **Added USS2 as a new module**
* `[PlutoLib]` `PlutoLib` now depends on `PlutoUSS2`
* `[PlutoLib]` *Removed* `Severity`, use `SmartSeverity` instead * `[PlutoLib]` *Removed* `Severity`, use `SmartSeverity` instead
* `[PlutoLib]` *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream` * `[PlutoLib]` *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream`
* `[PlutoLib]` *Removed* `StaticPlutoEventManager` as the implementation was too obscure * `[PlutoLib]` *Removed* `StaticPlutoEventManager` as the implementation was too obscure
@ -18,7 +20,7 @@
* `[PlutoLib]` Created the Version API * `[PlutoLib]` Created the Version API
* Added the `IVersion` interface * Added the `IVersion` interface
* Added support for version objects * Added support for version objects
* As a result, all fields except the version string are no longer compile-time constants * As a result, all fields in `Pluto` except the version string are no longer compile-time constants
* `[PlutoCore]` Made `PlutoApplication`'s constructor private * `[PlutoCore]` Made `PlutoApplication`'s constructor private
* `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message * `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message

View File

@ -33,6 +33,8 @@ task generateConfigs(type: Copy) {
compileJava.dependsOn generateConfigs compileJava.dependsOn generateConfigs
dependencies { dependencies {
api project(":plutouss2")
api platform("org.lwjgl:lwjgl-bom:$lwjglVersion") api platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
api "com.google.code.findbugs:jsr305:3.0.2" api "com.google.code.findbugs:jsr305:3.0.2"

7
plutouss2/build.gradle Normal file
View File

@ -0,0 +1,7 @@
apply plugin: 'java-library'
description = "UniversalSerializationSystem 2 (formerly UserStorageSystem) is a simple library for " +
"in-memory serialization of basic data types to ByteBuffers with versioned schema support."
dependencies {
}

View File

@ -0,0 +1,10 @@
package cz.tefek.pluto.uss2.properties;
public enum EnumUSS2PropertyType
{
BYTE,
INT,
LONG,
STRING,
DOUBLE
}

View File

@ -0,0 +1,104 @@
package cz.tefek.pluto.uss2.properties;
import java.nio.ByteBuffer;
public final class USS2PropertyObject
{
final USS2PropertySchema schema;
final int currentVersion;
final int latestVersion;
boolean dirty;
final ByteBuffer data;
private USS2PropertyObject(ByteBuffer buf, USS2PropertySchema schema)
{
this.schema = schema;
if (buf.limit() != schema.getCapacity())
throw new IllegalArgumentException("The input ByteBuffer's limit and the USS2PropertySchema size must be the same value!");
this.currentVersion = 0xff & buf.get(0);
this.latestVersion = schema.getVersion();
if (this.currentVersion > this.latestVersion)
throw new RuntimeException(String.format("The file's version (%d) is newer than what USS2 can read (%d)!", this.currentVersion, this.latestVersion));
this.data = buf;
}
private static USS2PropertyObject upgrade(USS2PropertyObject po)
{
var poNew = create(po.schema);
var props = po.schema.getProperties();
props.forEach(property -> {
if (property instanceof USS2PropertySchema.USS2Int)
{
var ussInt = (USS2PropertySchema.USS2Int) property;
ussInt.write(poNew, ussInt.read(po));
}
else if (property instanceof USS2PropertySchema.USS2Long)
{
var ussLong = (USS2PropertySchema.USS2Long) property;
ussLong.write(poNew, ussLong.read(po));
}
else if (property instanceof USS2PropertySchema.USS2Double)
{
var ussDouble = (USS2PropertySchema.USS2Double) property;
ussDouble.write(poNew, ussDouble.read(po));
}
else if (property instanceof USS2PropertySchema.USS2Byte)
{
var ussByte = (USS2PropertySchema.USS2Byte) property;
ussByte.write(poNew, ussByte.read(po));
}
else if (property instanceof USS2PropertySchema.USS2String)
{
var ussString = (USS2PropertySchema.USS2String) property;
ussString.write(poNew, ussString.read(po));
}
});
return poNew;
}
public static USS2PropertyObject create(USS2PropertySchema schema)
{
var buf = ByteBuffer.wrap(new byte[schema.getCapacity()]);
buf.put(0, (byte) schema.getVersion());
var po = new USS2PropertyObject(buf, schema);
po.dirty = true;
return po;
}
public static USS2PropertyObject from(ByteBuffer buf, USS2PropertySchema schema)
{
var po = new USS2PropertyObject(buf, schema);
if (po.latestVersion != po.currentVersion)
return upgrade(po);
return po;
}
public ByteBuffer getData()
{
return this.data;
}
public byte[] getDataByteArray()
{
if (!this.data.hasArray())
throw new RuntimeException("Data does not have a backing array!");
return this.data.array();
}
public boolean isDirty()
{
return this.dirty;
}
}

View File

@ -0,0 +1,273 @@
package cz.tefek.pluto.uss2.properties;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public abstract class USS2PropertySchema
{
public static final int DOES_NOT_EXIST_IN_VERSION = -1;
public static final int MAX_VERSION = 0xff;
private final List<USS2Property> properties;
private final int capacity;
private int version;
private final int[] offsets;
protected USS2PropertySchema(int capacity)
{
if (capacity < 16)
throw new IllegalArgumentException("Please use a starting capacity of at least 16 bytes.");
this.properties = new ArrayList<>();
this.offsets = new int[MAX_VERSION + 1];
// Version byte
Arrays.fill(this.offsets, Byte.BYTES);
this.capacity = capacity;
this.version = 0;
}
public final String getInfo()
{
float used = this.offsets[MAX_VERSION] / (float) this.capacity;
final int bars = 30;
int usedBars = Math.round(used * bars);
return String.format("[%s%s] %.2f%% of schema used (%d/%d bytes).", "|".repeat(usedBars), " ".repeat(bars - usedBars), used * 100, this.offsets[MAX_VERSION], this.capacity);
}
private void declareProperty(USS2Property property)
{
Arrays.fill(property.offsets, 0, property.version, DOES_NOT_EXIST_IN_VERSION);
System.arraycopy(this.offsets, property.version, property.offsets, property.version, property.offsets.length - property.version);
// No need to check all versions, because the highest version is the largest by default (properties cannot be removed)
if (this.offsets[MAX_VERSION] + property.objectSize > this.capacity)
throw new IllegalStateException(
String.format("Error: Declaring another property (of size %d) " +
"would put the schema's total size to %d, which is over the capacity of %d.",
property.objectSize,
this.offsets[MAX_VERSION] + property.objectSize,
this.capacity));
this.properties.add(property);
if (property.version > this.version)
this.version = property.version;
for (int i = property.version; i <= MAX_VERSION; i++)
this.offsets[i] += property.objectSize;
}
public final List<USS2Property> getProperties()
{
return Collections.unmodifiableList(this.properties);
}
public final int getVersion()
{
return this.version;
}
public final int getCapacity()
{
return this.capacity;
}
protected final USS2Int declareInt(byte version)
{
var ussInt = new USS2Int(0xff & version);
this.declareProperty(ussInt);
return ussInt;
}
protected final USS2Long declareLong(byte version)
{
var ussLong = new USS2Long(0xff & version);
this.declareProperty(ussLong);
return ussLong;
}
protected final USS2Double declareDouble(byte version)
{
var ussDouble = new USS2Double(0xff & version);
this.declareProperty(ussDouble);
return ussDouble;
}
protected final USS2Byte declareByte(byte version)
{
var ussByte = new USS2Byte(0xff & version);
this.declareProperty(ussByte);
return ussByte;
}
protected final USS2String declareString(byte version, byte length)
{
var ussByte = new USS2String(0xff & version, 0xff & length);
this.declareProperty(ussByte);
return ussByte;
}
public static abstract class USS2Property
{
private final int version;
private final int objectSize;
protected final int[] offsets;
protected USS2Property(int version, int objectSize)
{
this.version = version;
this.objectSize = objectSize;
this.offsets = new int[MAX_VERSION];
}
final int getObjectSize()
{
return this.objectSize;
}
int getVersion()
{
return this.version;
}
}
public static final class USS2Int extends USS2Property
{
private USS2Int(int version)
{
super(version, Integer.BYTES);
}
public final void write(USS2PropertyObject po, int value)
{
po.dirty = true;
po.data.putInt(this.offsets[po.currentVersion], value);
}
public final int read(USS2PropertyObject po)
{
if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION)
{
return 0;
}
return po.data.getInt(this.offsets[po.currentVersion]);
}
}
public static final class USS2Long extends USS2Property
{
private USS2Long(int version)
{
super(version, Long.BYTES);
}
public final void write(USS2PropertyObject po, long value)
{
po.dirty = true;
po.data.putLong(this.offsets[po.currentVersion], value);
}
public final long read(USS2PropertyObject po)
{
if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION)
{
return 0;
}
return po.data.getLong(this.offsets[po.currentVersion]);
}
}
public static final class USS2Double extends USS2Property
{
private USS2Double(int version)
{
super(version, Double.BYTES);
}
public final void write(USS2PropertyObject po, double value)
{
po.dirty = true;
po.data.putDouble(this.offsets[po.currentVersion], value);
}
public final double read(USS2PropertyObject po)
{
if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION)
{
return 0.0;
}
return po.data.getDouble(this.offsets[po.currentVersion]);
}
}
public static final class USS2Byte extends USS2Property
{
public USS2Byte(int version)
{
super(version, Byte.BYTES);
}
public final void write(USS2PropertyObject po, byte value)
{
po.dirty = true;
po.data.put(this.offsets[po.currentVersion], value);
}
public final byte read(USS2PropertyObject po)
{
if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION)
{
return 0;
}
return po.data.get(this.offsets[po.currentVersion]);
}
}
public static final class USS2String extends USS2Property
{
private USS2String(int version, int length)
{
super(version, Byte.BYTES + length);
}
public final void write(USS2PropertyObject po, String value)
{
po.dirty = true;
var offset = this.offsets[po.currentVersion];
var data = value.getBytes(StandardCharsets.UTF_8);
po.data.position(offset);
po.data.put((byte) data.length);
po.data.put(data);
po.data.rewind();
}
public final String read(USS2PropertyObject po)
{
if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION)
{
return "";
}
var offset = this.offsets[po.currentVersion];
po.data.position(offset);
var length = po.data.get();
var bytes = new byte[0xff & length];
po.data.get(bytes);
po.data.rewind();
return new String(bytes, StandardCharsets.UTF_8);
}
}
}

View File

@ -1,4 +1,5 @@
include 'plutolib', include 'plutouss2',
'plutolib',
'plutostatic', 'plutostatic',
'plutotexturing', 'plutotexturing',
'plutomesher', 'plutomesher',