import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /// The little-endian encoding shared with `ffi/rust/src/codec.rs`. final class Codec { /// An effect decoded from Rust. The encoding is the same for both machine /// kinds; how the awaited effect is answered differs per driver. sealed interface Effect { record Count() implements Effect {} record Lookup(String name) implements Effect {} record ReadLine() implements Effect {} record Sleep(long millis) implements Effect {} record Write(String text) implements Effect {} } /// Decode a batch. static List decode(byte[] bytes) { var buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); var out = new ArrayList(); while (buf.hasRemaining()) { int tag = buf.get() & 0xff; switch (tag) { case 1 -> out.add(new Effect.Count()); case 2 -> { var name = str(buf); out.add(new Effect.Lookup(name)); } case 3 -> out.add(new Effect.ReadLine()); case 4 -> { long millis = buf.getLong(); out.add(new Effect.Sleep(millis)); } case 5 -> out.add(new Effect.Write(str(buf))); default -> throw new IllegalStateException("unknown effect tag " + tag); } } return out; } private static String str(ByteBuffer buf) { int len = buf.getInt(); var bytes = new byte[len]; buf.get(bytes); return new String(bytes, StandardCharsets.UTF_8); } /// Sans-io inputs. static byte[] start() { return new byte[] {0}; } static byte[] line(String s) { return tagged(1, s); } static byte[] greeting(String s) { return tagged(2, s); } static byte[] woke() { return new byte[] {3}; } static byte[] counted(int n) { return ByteBuffer.allocate(5).order(ByteOrder.LITTLE_ENDIAN).put((byte) 4).putInt(n).array(); } private static byte[] tagged(int tag, String s) { var bytes = s.getBytes(StandardCharsets.UTF_8); return ByteBuffer.allocate(5 + bytes.length) .order(ByteOrder.LITTLE_ENDIAN) .put((byte) tag) .putInt(bytes.length) .put(bytes) .array(); } }