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