initial commit
This commit is contained in:
commit
dcee1e4f22
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/bin/
|
||||
/target/
|
||||
/dependency-reduced-pom.xml
|
||||
/.settings/
|
||||
/.classpath
|
23
.project
Normal file
23
.project
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>SimpleNIO</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
73
pom.xml
Normal file
73
pom.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.mrletsplay</groupId>
|
||||
<artifactId>SimpleNIO</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.1</version>
|
||||
<configuration>
|
||||
<release>11</release>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<encoding>UTF-8</encoding>
|
||||
<sourcepath>src/main/java</sourcepath>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>Graphite-Official</id>
|
||||
<url>https://maven.graphite-official.com/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.mrletsplay</groupId>
|
||||
<artifactId>MrCore</artifactId>
|
||||
<version>4.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,5 @@
|
||||
package me.mrletsplay.simplenio._test;
|
||||
|
||||
public class SimpleNIOMain {
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public interface Expectation {
|
||||
|
||||
public void orElseRun(IOConsumer<ReaderInstance<?>> run);
|
||||
|
||||
public void orElseRun(IORunnable run);
|
||||
|
||||
public void orElseThrow(Supplier<? extends IOException> exception);
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public class ExpectationImpl implements Expectation {
|
||||
|
||||
private IOConsumer<ReaderInstance<?>> run;
|
||||
|
||||
@Override
|
||||
public void orElseRun(IOConsumer<ReaderInstance<?>> run) {
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orElseRun(IORunnable run) {
|
||||
this.run = instance -> run.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orElseThrow(Supplier<? extends IOException> exception) {
|
||||
this.run = instance -> { throw exception.get(); };
|
||||
}
|
||||
|
||||
public void fail(ReaderInstance<?> instance) throws IOException {
|
||||
if(run == null) throw new IOException("Expectation failed");
|
||||
run.accept(instance);
|
||||
}
|
||||
|
||||
}
|
46
src/main/java/me/mrletsplay/simplenio/reader/Operation.java
Normal file
46
src/main/java/me/mrletsplay/simplenio/reader/Operation.java
Normal file
@ -0,0 +1,46 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public interface Operation extends OperationCallback {
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException;
|
||||
|
||||
public Operation copy();
|
||||
|
||||
public void reset();
|
||||
|
||||
public default Operation then(Operation other) {
|
||||
return Operations.allOf(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* API Note: {@code other} must be a stateless runnable, because it might be called from multiple threads.
|
||||
* @param other The action to run
|
||||
* @return An Operation
|
||||
*/
|
||||
public default Operation thenRun(IORunnable other) {
|
||||
return then(Operations.stateless((instance, buf) -> {
|
||||
other.run();
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* API Note: {@code other} must be a stateless runnable, because it might be called from multiple threads.
|
||||
* @param other The action to run
|
||||
* @return An Operation
|
||||
*/
|
||||
public default Operation thenRun(IOConsumer<ReaderInstance<?>> other) {
|
||||
return then(Operations.stateless((instance, buf) -> {
|
||||
other.accept(instance);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface OperationCallback {
|
||||
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException;
|
||||
|
||||
}
|
370
src/main/java/me/mrletsplay/simplenio/reader/Operations.java
Normal file
370
src/main/java/me/mrletsplay/simplenio/reader/Operations.java
Normal file
@ -0,0 +1,370 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public class Operations {
|
||||
|
||||
public static Operation stateless(OperationCallback callback) {
|
||||
return new Operation() {
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
return callback.read(instance, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static Operation run(IORunnable run) {
|
||||
return stateless((instance, buf) -> {
|
||||
run.run();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public static Operation run(IOConsumer<ReaderInstance<?>> run) {
|
||||
return stateless((instance, buf) -> {
|
||||
run.accept(instance);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public static Operation allOf(Operation... operations) {
|
||||
return new CombinedOperation(operations);
|
||||
}
|
||||
|
||||
public static Operation readByte(SimpleRef<Byte> ref) {
|
||||
return stateless((instance, buf) -> {
|
||||
if(!buf.hasRemaining()) return false;
|
||||
ref.set(instance, buf.get());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public static Operation readInt(SimpleRef<Integer> ref) {
|
||||
return new ReadIntOperation(ref);
|
||||
}
|
||||
|
||||
public static Operation readNBytes(SimpleRef<byte[]> ref, int n) {
|
||||
return new ReadBytesOperation(ref, n);
|
||||
}
|
||||
|
||||
public static Operation readUntilBytes(SimpleRef<byte[]> ref, byte[] delimiter, int limit) {
|
||||
return new ReadBytesUntilOperation(ref, delimiter, limit);
|
||||
}
|
||||
|
||||
public static Operation lazy(Ref<Operation> lazy) {
|
||||
return new LazyOperation(lazy);
|
||||
}
|
||||
|
||||
public static Operation branch(Ref<Integer> value, Operation... branches) {
|
||||
return new BranchOperation(value, branches);
|
||||
}
|
||||
|
||||
public static Operation loopUntil(Ref<Boolean> condition, Operation body) {
|
||||
return new LoopUntilOperation(condition, body);
|
||||
}
|
||||
|
||||
public static <T> Operation read(SimpleRef<T> ref, Reader<? extends T> reader) {
|
||||
return new ReaderOperation<>(ref, reader);
|
||||
}
|
||||
|
||||
private static class CombinedOperation implements Operation {
|
||||
|
||||
private Operation[] operations;
|
||||
private int i;
|
||||
|
||||
public CombinedOperation(Operation... operations) {
|
||||
this.operations = operations;
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
while(i < operations.length) {
|
||||
if(operations[i].read(instance, buf)) {
|
||||
i++;
|
||||
}else if(!buf.hasRemaining()) break;
|
||||
}
|
||||
|
||||
return i == operations.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new CombinedOperation(Arrays.stream(operations).map(Operation::copy).toArray(Operation[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for(Operation op : operations) op.reset();
|
||||
i = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ReadIntOperation implements Operation {
|
||||
|
||||
private SimpleRef<Integer> ref;
|
||||
private int value;
|
||||
private int i;
|
||||
|
||||
public ReadIntOperation(SimpleRef<Integer> ref) {
|
||||
this.ref = ref;
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
while(buf.hasRemaining() && i > 0) {
|
||||
i--;
|
||||
value |= (buf.get() & 0xFF) << i;
|
||||
}
|
||||
|
||||
if(i == 0) {
|
||||
ref.set(instance, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new ReadIntOperation(ref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.value = 0;
|
||||
this.i = 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ReadBytesOperation implements Operation {
|
||||
|
||||
private SimpleRef<byte[]> ref;
|
||||
private byte[] bytes;
|
||||
private int i;
|
||||
|
||||
public ReadBytesOperation(SimpleRef<byte[]> ref, int n) {
|
||||
this.ref = ref;
|
||||
this.bytes = new byte[n];
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
while(buf.hasRemaining() && i < bytes.length) {
|
||||
bytes[i++] = buf.get();
|
||||
}
|
||||
|
||||
if(i == bytes.length) {
|
||||
ref.set(instance, bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new ReadBytesOperation(ref, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ReadBytesUntilOperation implements Operation {
|
||||
|
||||
private SimpleRef<byte[]> ref;
|
||||
private byte[] delimiter;
|
||||
private byte[] bytes;
|
||||
private int i;
|
||||
|
||||
public ReadBytesUntilOperation(SimpleRef<byte[]> ref, byte[] delimiter, int limit) {
|
||||
this.ref = ref;
|
||||
this.delimiter = delimiter;
|
||||
this.bytes = new byte[limit];
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
while(buf.hasRemaining() && i < bytes.length) {
|
||||
bytes[i++] = buf.get();
|
||||
|
||||
if(i >= delimiter.length && Arrays.equals(bytes, i - delimiter.length, i, delimiter, 0, delimiter.length)) {
|
||||
ref.set(instance, Arrays.copyOfRange(bytes, 0, i));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == bytes.length) throw new IOException("Buffer limit reached");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new ReadBytesUntilOperation(ref, delimiter, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class LazyOperation implements Operation {
|
||||
|
||||
private Ref<Operation> operationSupplier;
|
||||
private Operation operation;
|
||||
|
||||
public LazyOperation(Ref<Operation> operationSupplier) {
|
||||
this.operationSupplier = operationSupplier;
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
if(operation == null) operation = operationSupplier.get(instance);
|
||||
return operation.read(instance, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new LazyOperation(operationSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.operation = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BranchOperation implements Operation {
|
||||
|
||||
private Ref<Integer> value;
|
||||
private Operation[] branches;
|
||||
private int i;
|
||||
|
||||
public BranchOperation(Ref<Integer> value, Operation... branches) {
|
||||
this.value = value;
|
||||
this.branches = branches;
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
if(i == -1) i = value.get(instance);
|
||||
if(branches[i] == null) return true;
|
||||
return branches[i].read(instance, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new BranchOperation(value, Arrays.stream(branches).map(op -> op == null ? null : op.copy()).toArray(Operation[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for(Operation op : branches) {
|
||||
if(op != null) op.reset();
|
||||
}
|
||||
this.i = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class LoopUntilOperation implements Operation {
|
||||
|
||||
private Ref<Boolean> condition;
|
||||
private Operation body;
|
||||
|
||||
public LoopUntilOperation(Ref<Boolean> condition, Operation body) {
|
||||
this.condition = condition;
|
||||
this.body = body;
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
if(body.read(instance, buf)) {
|
||||
body.reset();
|
||||
if(condition.get(instance)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new LoopUntilOperation(condition, body.copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
body.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ReaderOperation<T> implements Operation {
|
||||
|
||||
private SimpleRef<T> ref;
|
||||
private Reader<? extends T> reader;
|
||||
private ReaderInstance<? extends T> readerInstance;
|
||||
|
||||
public ReaderOperation(SimpleRef<T> ref, Reader<? extends T> reader) {
|
||||
this.ref = ref;
|
||||
this.reader = reader;
|
||||
this.readerInstance = reader.createInstance();
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ReaderInstance<?> instance, ByteBuffer buf) throws IOException {
|
||||
if(readerInstance.read(buf)) {
|
||||
ref.set(instance, readerInstance.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation copy() {
|
||||
return new ReaderOperation<>(ref, reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
readerInstance.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
61
src/main/java/me/mrletsplay/simplenio/reader/Reader.java
Normal file
61
src/main/java/me/mrletsplay/simplenio/reader/Reader.java
Normal file
@ -0,0 +1,61 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IOFunction;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public interface Reader<T> {
|
||||
|
||||
public Ref<Byte> readByte();
|
||||
|
||||
public Ref<Integer> readInt();
|
||||
|
||||
public Ref<byte[]> readNBytes(int n);
|
||||
|
||||
public Ref<byte[]> readNBytes(Ref<Integer> n);
|
||||
|
||||
public Ref<byte[]> readUntilByte(byte delimiter, int limit);
|
||||
|
||||
public Ref<byte[]> readUntilByte(Ref<Byte> delimiter, int limit);
|
||||
|
||||
public Ref<byte[]> readUntilBytes(byte[] delimiter, int limit);
|
||||
|
||||
public Ref<byte[]> readUntilBytes(Ref<byte[]> delimiter, int limit);
|
||||
|
||||
public void read(Operation operation);
|
||||
|
||||
public <O> Ref<O> read(Reader<O> reader);
|
||||
|
||||
public void branch(Ref<Boolean> condition, Operation ifTrue, Operation ifFalse);
|
||||
|
||||
public void branch(Ref<Integer> value, Operation... branches);
|
||||
|
||||
public <O> Ref<O> branch(Ref<Boolean> condition, Reader<? extends O> ifTrue, Reader<? extends O> ifFalse);
|
||||
|
||||
public <O> Ref<O> branch(Ref<Integer> value, @SuppressWarnings("unchecked") Reader<? extends O>... branches);
|
||||
|
||||
public void loopUntil(Ref<Boolean> condition, Operation body);
|
||||
|
||||
public <O> Ref<List<O>> loopUntil(Ref<Boolean> condition, Reader<O> body);
|
||||
|
||||
public void run(IORunnable action);
|
||||
|
||||
public void run(IOConsumer<ReaderInstance<?>> action);
|
||||
|
||||
public Expectation expect(Ref<Boolean> condition);
|
||||
|
||||
public Expectation expectByte(byte b);
|
||||
|
||||
public Expectation expectByte(Ref<Byte> b);
|
||||
|
||||
public Expectation expectBytes(byte[] bytes);
|
||||
|
||||
public Expectation expectBytes(Ref<byte[]> b);
|
||||
|
||||
public void setConverter(IOFunction<ReaderInstance<T>, T> converter);
|
||||
|
||||
public ReaderInstance<T> createInstance();
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
public class ReaderException {
|
||||
|
||||
}
|
286
src/main/java/me/mrletsplay/simplenio/reader/ReaderImpl.java
Normal file
286
src/main/java/me/mrletsplay/simplenio/reader/ReaderImpl.java
Normal file
@ -0,0 +1,286 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import me.mrletsplay.simplenio.util.IOConsumer;
|
||||
import me.mrletsplay.simplenio.util.IOFunction;
|
||||
import me.mrletsplay.simplenio.util.IORunnable;
|
||||
|
||||
public class ReaderImpl<T> implements Reader<T> {
|
||||
|
||||
private List<Operation> operations;
|
||||
private Map<Operation, StackTraceElement[]> stackTraces;
|
||||
private IOFunction<ReaderInstance<T>, T> converter;
|
||||
|
||||
public ReaderImpl() {
|
||||
this.operations = new ArrayList<>();
|
||||
this.stackTraces = new HashMap<>();
|
||||
}
|
||||
|
||||
private void addOperation(Operation operation) {
|
||||
operations.add(operation);
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
stackTraces.put(operation, Arrays.copyOfRange(stackTrace, 1, stackTrace.length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<Byte> readByte() {
|
||||
SimpleRef<Byte> ref = SimpleRef.create();
|
||||
addOperation(Operations.readByte(ref));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<Integer> readInt() {
|
||||
SimpleRef<Integer> valueRef = SimpleRef.create();
|
||||
addOperation(Operations.readInt(valueRef));
|
||||
return valueRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readNBytes(int n) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.readNBytes(ref, n));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readNBytes(Ref<Integer> n) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.lazy(instance -> Operations.readNBytes(ref, n.get(instance))));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readUntilByte(byte delimiter, int limit) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.readUntilBytes(ref, new byte[] {delimiter}, limit));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readUntilByte(Ref<Byte> delimiter, int limit) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.lazy(instance -> Operations.readUntilBytes(ref, new byte[] {delimiter.get(instance)}, limit)));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readUntilBytes(byte[] delimiter, int limit) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.readUntilBytes(ref, delimiter, limit));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref<byte[]> readUntilBytes(Ref<byte[]> delimiter, int limit) {
|
||||
SimpleRef<byte[]> ref = SimpleRef.create();
|
||||
addOperation(Operations.lazy(instance -> Operations.readUntilBytes(ref, delimiter.get(instance), limit)));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Operation operation) {
|
||||
addOperation(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <O> Ref<O> read(Reader<O> reader) {
|
||||
SimpleRef<O> ref = SimpleRef.create();
|
||||
addOperation(Operations.read(ref, reader));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void branch(Ref<Boolean> condition, Operation ifTrue, Operation ifFalse) {
|
||||
addOperation(Operations.branch(condition.map(c -> c ? 0 : 1), ifTrue, ifFalse));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void branch(Ref<Integer> value, Operation... branches) {
|
||||
addOperation(Operations.branch(value, branches));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <O> Ref<O> branch(Ref<Boolean> condition, Reader<? extends O> ifTrue, Reader<? extends O> ifFalse) {
|
||||
SimpleRef<O> ref = SimpleRef.create();
|
||||
addOperation(Operations.branch(condition.map(c -> c ? 0 : 1), ifTrue == null ? null : Operations.read(ref, ifTrue), ifFalse == null ? null : Operations.read(ref, ifFalse)));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <O> Ref<O> branch(Ref<Integer> value, @SuppressWarnings("unchecked") Reader<? extends O>... branches) {
|
||||
SimpleRef<O> ref = SimpleRef.create();
|
||||
addOperation(Operations.branch(value, Arrays.stream(branches).map(b -> b == null ? null : Operations.read(ref, b)).toArray(Operation[]::new)));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loopUntil(Ref<Boolean> condition, Operation body) {
|
||||
addOperation(Operations.loopUntil(condition, body));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <O> Ref<List<O>> loopUntil(Ref<Boolean> condition, Reader<O> body) {
|
||||
SimpleRef<O> current = SimpleRef.create();
|
||||
SimpleRef<List<O>> ref = SimpleRef.create();
|
||||
addOperation(Operations.run(instance -> ref.set(instance, new ArrayList<>())).then(Operations.loopUntil(condition, Operations.read(current, body).thenRun(instance -> ref.get(instance).add(current.get(instance))))));
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(IORunnable action) {
|
||||
addOperation(Operations.run(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(IOConsumer<ReaderInstance<?>> action) {
|
||||
addOperation(Operations.run(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation expect(Ref<Boolean> condition) {
|
||||
ExpectationImpl expectation = new ExpectationImpl();
|
||||
addOperation(Operations.stateless((instance, buf) -> {
|
||||
if(!condition.get(instance)) expectation.fail(instance);
|
||||
return true;
|
||||
}));
|
||||
return expectation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation expectByte(byte b) {
|
||||
Ref<Byte> read = readByte();
|
||||
return expect(instance -> read.get(instance) == b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation expectByte(Ref<Byte> b) {
|
||||
Ref<Byte> read = readByte();
|
||||
return expect(instance -> read.get(instance) == b.get(instance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation expectBytes(byte[] bytes) {
|
||||
Ref<byte[]> read = readNBytes(bytes.length);
|
||||
return expect(instance -> Arrays.equals(read.get(instance), bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expectation expectBytes(Ref<byte[]> bytes) {
|
||||
SimpleRef<byte[]> read = SimpleRef.create();
|
||||
addOperation(Operations.lazy(instance -> Operations.readNBytes(read, bytes.get(instance).length)));
|
||||
return expect(instance -> Arrays.equals(read.get(instance), bytes.get(instance)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConverter(IOFunction<ReaderInstance<T>, T> converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReaderInstance<T> createInstance() {
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
protected class Instance implements ReaderInstance<T> {
|
||||
|
||||
private List<Operation> operations;
|
||||
private Map<Operation, StackTraceElement[]> stackTraces;
|
||||
private int idx;
|
||||
private Map<Ref<?>, Object> refValues;
|
||||
private boolean finished;
|
||||
private T value;
|
||||
private Consumer<T> onFinished;
|
||||
|
||||
public Instance() {
|
||||
this.stackTraces = new HashMap<>();
|
||||
this.operations = new ArrayList<>(ReaderImpl.this.operations.size());
|
||||
for(Operation op : ReaderImpl.this.operations) {
|
||||
Operation copy = op.copy();
|
||||
this.operations.add(copy);
|
||||
this.stackTraces.put(copy, ReaderImpl.this.stackTraces.get(op));
|
||||
}
|
||||
this.refValues = new HashMap<>();
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean read(ByteBuffer buffer) throws IOException {
|
||||
if(idx >= operations.size()) throw new IllegalStateException("Read after finished");
|
||||
|
||||
while(idx < operations.size()) {
|
||||
try {
|
||||
Operation operation = operations.get(idx);
|
||||
if(operation.read(this, buffer)) {
|
||||
idx++;
|
||||
}else if(!buffer.hasRemaining()) break;
|
||||
}catch(Exception e) {
|
||||
IOException ex = new IOException("Operation at index " + idx + " failed", e);
|
||||
ex.setStackTrace(stackTraces.get(operations.get(idx)));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
if(idx == operations.size()) {
|
||||
finished();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReaderInstance<T> onFinished(Consumer<T> consumer) {
|
||||
this.onFinished = consumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void finished() throws IOException {
|
||||
finished = true;
|
||||
value = converter.apply(this);
|
||||
if(onFinished != null) onFinished.accept(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws IllegalStateException {
|
||||
if(!finished) throw new IllegalStateException("Reader is not finished");
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> void setRef(SimpleRef<R> ref, R value) {
|
||||
refValues.put(ref, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R> R getRef(SimpleRef<R> ref) throws IllegalStateException {
|
||||
if(!refValues.containsKey(ref)) throw new IllegalStateException("Tried to access ref before it was set (make sure you only call get() in a callback)");
|
||||
return (R) refValues.get(ref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefSet(SimpleRef<?> ref) {
|
||||
return refValues.containsKey(ref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for(Operation op : operations) op.reset();
|
||||
this.idx = 0;
|
||||
this.refValues.clear();
|
||||
this.finished = false;
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ReaderInstance<T> {
|
||||
|
||||
public boolean read(ByteBuffer buf) throws IOException;
|
||||
|
||||
public ReaderInstance<T> onFinished(Consumer<T> consumer);
|
||||
|
||||
public T get() throws IllegalStateException;
|
||||
|
||||
public <R> void setRef(SimpleRef<R> ref, R value);
|
||||
|
||||
public <R> R getRef(SimpleRef<R> ref) throws IllegalStateException;
|
||||
|
||||
public boolean isRefSet(SimpleRef<?> ref);
|
||||
|
||||
public void reset();
|
||||
|
||||
}
|
52
src/main/java/me/mrletsplay/simplenio/reader/Ref.java
Normal file
52
src/main/java/me/mrletsplay/simplenio/reader/Ref.java
Normal file
@ -0,0 +1,52 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Readonly representation of a reference.
|
||||
* @param <T> The referenced type
|
||||
*/
|
||||
public interface Ref<T> {
|
||||
|
||||
public T get(ReaderInstance<?> instance) throws IllegalStateException;
|
||||
|
||||
public default <O> Ref<O> map(Function<T, O> map) {
|
||||
return instance -> map.apply(get(instance));
|
||||
}
|
||||
|
||||
public default boolean isSet(ReaderInstance<?> instance) {
|
||||
try {
|
||||
get(instance);
|
||||
return true;
|
||||
}catch(IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public default T getOrElse(ReaderInstance<?> instance, T other) {
|
||||
try {
|
||||
return get(instance);
|
||||
}catch(IllegalStateException e) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
public static Ref<String> asString(Ref<byte[]> bytes, Charset charset) {
|
||||
return bytes.map(b -> new String(b, charset));
|
||||
}
|
||||
|
||||
public static Ref<String> asString(Ref<byte[]> bytes) {
|
||||
return asString(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static Ref<byte[]> asBytes(Ref<String> string, Charset charset) {
|
||||
return string.map(s -> s.getBytes(charset));
|
||||
}
|
||||
|
||||
public static Ref<byte[]> asBytes(Ref<String> string) {
|
||||
return asBytes(string, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
31
src/main/java/me/mrletsplay/simplenio/reader/SimpleRef.java
Normal file
31
src/main/java/me/mrletsplay/simplenio/reader/SimpleRef.java
Normal file
@ -0,0 +1,31 @@
|
||||
package me.mrletsplay.simplenio.reader;
|
||||
|
||||
public class SimpleRef<T> implements Ref<T> {
|
||||
|
||||
private SimpleRef() {}
|
||||
|
||||
public void set(ReaderInstance<?> instance, T value) {
|
||||
instance.setRef(this, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSet(ReaderInstance<?> instance) {
|
||||
return instance.isRefSet(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(ReaderInstance<?> instance) throws IllegalStateException {
|
||||
return instance.getRef(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getOrElse(ReaderInstance<?> instance, T other) {
|
||||
if(!instance.isRefSet(this)) return other;
|
||||
return get(instance);
|
||||
}
|
||||
|
||||
public static <T> SimpleRef<T> create() {
|
||||
return new SimpleRef<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package me.mrletsplay.simplenio.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IOConsumer<T> {
|
||||
|
||||
public void accept(T value) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package me.mrletsplay.simplenio.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IOFunction<I, O> {
|
||||
|
||||
public O apply(I i) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package me.mrletsplay.simplenio.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IORunnable {
|
||||
|
||||
public void run() throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package me.mrletsplay.simplenio.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IOSupplier<T> {
|
||||
|
||||
public T get() throws IOException;
|
||||
|
||||
}
|
6
src/main/java/module-info.java
Normal file
6
src/main/java/module-info.java
Normal file
@ -0,0 +1,6 @@
|
||||
module me.mrletsplay.simplenio {
|
||||
exports me.mrletsplay.simplenio.reader;
|
||||
exports me.mrletsplay.simplenio.util;
|
||||
|
||||
requires transitive me.mrletsplay.mrcore;
|
||||
}
|
12
src/test/java/me/mrletsplay/simplenio/ReaderTest.java
Normal file
12
src/test/java/me/mrletsplay/simplenio/ReaderTest.java
Normal file
@ -0,0 +1,12 @@
|
||||
package me.mrletsplay.simplenio;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ReaderTest {
|
||||
|
||||
@Test
|
||||
public void testReader() {
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user