diff --git a/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderBuildTest.java b/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderBuildTest.java index 8cf4b37ea..478fb042b 100644 --- a/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderBuildTest.java +++ b/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderBuildTest.java @@ -1,6 +1,5 @@ package net.zscript.model.modules.testing.test; -import java.nio.charset.StandardCharsets; import java.util.EnumSet; import java.util.Optional; import java.util.OptionalInt; @@ -20,9 +19,9 @@ import net.zscript.client.modules.test.testing.TestingModule; import net.zscript.client.modules.test.testing.TestingModule.TestCommand0Command.Builder.BitsetReqTestE; -import net.zscript.javaclient.commandbuilder.commandnodes.ZscriptCommandBuilder; import net.zscript.javaclient.commandbuilder.ZscriptFieldOutOfRangeException; import net.zscript.javaclient.commandbuilder.ZscriptMissingFieldException; +import net.zscript.javaclient.commandbuilder.commandnodes.ZscriptCommandBuilder; public class JavaCommandBuilderBuildTest { @Test diff --git a/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderResponseTest.java b/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderResponseTest.java index bf4946231..d1a252601 100644 --- a/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderResponseTest.java +++ b/clients/java-client-lib/client-command-builders/src/test/java/net/zscript/model/modules/testing/test/JavaCommandBuilderResponseTest.java @@ -18,13 +18,13 @@ import net.zscript.javaclient.commandbuilder.commandnodes.ResponseCaptor; import net.zscript.javaclient.commandbuilder.commandnodes.ZscriptCommandBuilder; import net.zscript.javaclient.commandbuilder.commandnodes.ZscriptCommandNode; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.tokenizer.TokenBuffer.TokenReader; -import net.zscript.tokenizer.TokenExtendingBuffer; import net.zscript.tokenizer.Tokenizer; import net.zscript.tokenizer.ZscriptTokenExpression; public class JavaCommandBuilderResponseTest { - final TokenExtendingBuffer buffer = new TokenExtendingBuffer(); + final ExtendingTokenBuffer buffer = new ExtendingTokenBuffer(); final Tokenizer tokenizer = new Tokenizer(buffer.getTokenWriter(), 2); final TokenReader tokenReader = buffer.getTokenReader(); diff --git a/clients/java-client-lib/client-core/src/main/java/net/zscript/javaclient/tokens/ExtendingTokenBuffer.java b/clients/java-client-lib/client-core/src/main/java/net/zscript/javaclient/tokens/ExtendingTokenBuffer.java new file mode 100644 index 000000000..056129c9b --- /dev/null +++ b/clients/java-client-lib/client-core/src/main/java/net/zscript/javaclient/tokens/ExtendingTokenBuffer.java @@ -0,0 +1,39 @@ +package net.zscript.javaclient.tokens; + +import net.zscript.tokenizer.AbstractArrayTokenBuffer; + +/** + * A TokenBuffer that automatically expands on being filled, for handling Zscript responses in a Client. + */ +public class ExtendingTokenBuffer extends AbstractArrayTokenBuffer { + public ExtendingTokenBuffer() { + super(20); + } + + /** + * There's always room, as we auto-expand. + * + * @param writeCursor + * @param size + * @return always true + */ + @Override + protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { + return true; + } + + /** + * Expands the underlying array, and returns the offset we were trying for now that it will fit. + *

+ * {@inheritDoc} + * + * @param overflowedOffset the offset we were trying to access + * @return the overflowedOffset that was passed in, as there's now enough space + */ + @Override + protected int offsetOnOverflow(int overflowedOffset) { + // expand to double current size + extendData(getDataSize()); + return overflowedOffset; + } +} diff --git a/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CommandExecutionPathRegenerationTest.java b/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CommandExecutionPathRegenerationTest.java index 0b7af8ed3..a46592d3c 100644 --- a/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CommandExecutionPathRegenerationTest.java +++ b/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CommandExecutionPathRegenerationTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.params.provider.MethodSource; import net.zscript.javaclient.commandPaths.CommandExecutionPath; -import net.zscript.tokenizer.TokenExtendingBuffer; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.tokenizer.Tokenizer; public class CommandExecutionPathRegenerationTest { @@ -19,7 +19,7 @@ public class CommandExecutionPathRegenerationTest { @ParameterizedTest @MethodSource public void shouldProduceActionsForLogicalCommandSeries(final String input, final String output) { - TokenExtendingBuffer bufferCmd = new TokenExtendingBuffer(); + ExtendingTokenBuffer bufferCmd = new ExtendingTokenBuffer(); Tokenizer tokenizerCmd = new Tokenizer(bufferCmd.getTokenWriter(), 2); for (byte b : input.getBytes(StandardCharsets.UTF_8)) { tokenizerCmd.accept(b); diff --git a/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CompleteCommandGrapherTest.java b/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CompleteCommandGrapherTest.java index 8f49fd9f3..49ce85db7 100644 --- a/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CompleteCommandGrapherTest.java +++ b/clients/java-client-lib/client-core/src/test/java/net/zscript/javaclient/commandPrinting/CompleteCommandGrapherTest.java @@ -13,7 +13,7 @@ import net.zscript.ascii.AnsiCharacterStylePrinter; import net.zscript.javaclient.commandPaths.CommandExecutionPath; -import net.zscript.tokenizer.TokenExtendingBuffer; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.tokenizer.Tokenizer; public class CompleteCommandGrapherTest { @@ -23,7 +23,7 @@ public class CompleteCommandGrapherTest { @ParameterizedTest @MethodSource public void shouldProduceGoodGraphs(final String input, final String output) { - TokenExtendingBuffer bufferCmd = new TokenExtendingBuffer(); + ExtendingTokenBuffer bufferCmd = new ExtendingTokenBuffer(); Tokenizer tokenizerCmd = new Tokenizer(bufferCmd.getTokenWriter(), 2); for (byte b : input.getBytes(StandardCharsets.UTF_8)) { tokenizerCmd.accept(b); diff --git a/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/connectors/RawConnection.java b/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/connectors/RawConnection.java index 11c56f186..18a3f17cc 100644 --- a/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/connectors/RawConnection.java +++ b/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/connectors/RawConnection.java @@ -14,7 +14,7 @@ import net.zscript.javaclient.addressing.CompleteAddressedResponse; import net.zscript.javaclient.nodes.Connection; import net.zscript.javaclient.threading.ZscriptWorkerThread; -import net.zscript.tokenizer.TokenExtendingBuffer; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.tokenizer.Tokenizer; public abstract class RawConnection implements Connection, AutoCloseable { @@ -44,7 +44,7 @@ public final void onReceive(Consumer resp) { onReceiveBytes(data -> thread.moveOntoThread(() -> { AddressedResponse parsed = null; try { - TokenExtendingBuffer buffer = new TokenExtendingBuffer(); + ExtendingTokenBuffer buffer = new ExtendingTokenBuffer(); Tokenizer t = new Tokenizer(buffer.getTokenWriter(), 2); for (byte b : data) { t.accept(b); diff --git a/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/devices/Device.java b/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/devices/Device.java index 63a78968c..401cdff9f 100644 --- a/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/devices/Device.java +++ b/clients/java-client-lib/client-main/src/main/java/net/zscript/javaclient/devices/Device.java @@ -25,8 +25,8 @@ import net.zscript.javaclient.commandbuilder.notifications.NotificationId; import net.zscript.javaclient.nodes.ZscriptNode; import net.zscript.javaclient.sequence.CommandSequence; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.model.ZscriptModel; -import net.zscript.tokenizer.TokenExtendingBuffer; import net.zscript.tokenizer.Tokenizer; public class Device { @@ -133,7 +133,7 @@ public ResponseSequenceCallback sendAndWaitExpectSuccess(final CommandSequenceNo } public void send(final byte[] cmdSeq, final Consumer callback) { - TokenExtendingBuffer buffer = new TokenExtendingBuffer(); + ExtendingTokenBuffer buffer = new ExtendingTokenBuffer(); Tokenizer tok = new Tokenizer(buffer.getTokenWriter(), 2); for (byte b : cmdSeq) { tok.accept(b); diff --git a/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/SerialMain.java b/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/SerialMain.java index 731b74081..973aaa252 100644 --- a/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/SerialMain.java +++ b/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/SerialMain.java @@ -13,8 +13,8 @@ import net.zscript.javaclient.connectors.ZscriptConnectors; import net.zscript.javaclient.connectors.serial.SerialConnector; import net.zscript.javaclient.sequence.CommandSequence; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.model.modules.base.CoreModule; -import net.zscript.tokenizer.TokenExtendingBuffer; import net.zscript.tokenizer.Tokenizer; class SerialMain { @@ -91,7 +91,7 @@ public static void main(String[] args) throws IOException, InterruptedException }); for (int i = 0; i < 10; i++) { byte[] ba = CoreModule.echoBuilder().setAny('A', 35).build().asString().getBytes(StandardCharsets.UTF_8); - TokenExtendingBuffer buffer = new TokenExtendingBuffer(); + ExtendingTokenBuffer buffer = new ExtendingTokenBuffer(); Tokenizer t = new Tokenizer(buffer.getTokenWriter(), 2); for (byte b : ba) { t.accept(b); diff --git a/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/TcpMain.java b/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/TcpMain.java index 095efd398..8c34aa809 100644 --- a/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/TcpMain.java +++ b/clients/java-client-lib/client-main/src/test/java/net/zscript/javaclient/connection/TcpMain.java @@ -9,8 +9,8 @@ import net.zscript.javaclient.connectors.ZscriptConnectors; import net.zscript.javaclient.connectors.ZscriptConnectors.ZscriptConnector; import net.zscript.javaclient.sequence.CommandSequence; +import net.zscript.javaclient.tokens.ExtendingTokenBuffer; import net.zscript.model.modules.base.CoreModule; -import net.zscript.tokenizer.TokenExtendingBuffer; import net.zscript.tokenizer.Tokenizer; class TcpMain { @@ -23,7 +23,7 @@ public static void main(String[] args) throws Exception { conn.onReceive((response) -> { System.out.println("Response: " + response.toString()); }); - TokenExtendingBuffer buffer = new TokenExtendingBuffer(); + ExtendingTokenBuffer buffer = new ExtendingTokenBuffer(); Tokenizer t = new Tokenizer(buffer.getTokenWriter(), 2); for (byte b : CoreModule.echoBuilder().setAny('A', 1234).build().asString().getBytes(StandardCharsets.UTF_8)) { t.accept(b); diff --git a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceSetupCommand.java b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceSetupCommand.java index 1433ab22d..fe44bbec9 100644 --- a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceSetupCommand.java +++ b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceSetupCommand.java @@ -18,22 +18,26 @@ public static void execute(List spaces, CommandContext ctx) { ctx.status(ZscriptStatus.VALUE_OUT_OF_RANGE); return; } - ZscriptCommandOutStream out = ctx.getOutStream(); - ScriptSpace target = spaces.get(spaceIndex.getAsInt()); - out.writeField('P', target.getCurrentLength()); - if (target.isRunning()) { + + ZscriptCommandOutStream out = ctx.getOutStream(); + ScriptSpace targetSpace = spaces.get(spaceIndex.getAsInt()); + out.writeField('P', targetSpace.getCurrentLength()); + + if (targetSpace.isRunning()) { out.writeField('R', 0); } - if (target.canBeWrittenTo()) { + + if (targetSpace.isWritable()) { out.writeField('W', 0); } + out.writeField('L', 0xFFFF); OptionalInt runOpt = ctx.getField('R'); if (runOpt.isPresent()) { if (runOpt.getAsInt() != 0) { - target.run(); + targetSpace.run(); } else { - target.stop(); + targetSpace.stop(); } } } diff --git a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceWriteCommand.java b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceWriteCommand.java index 373e3eaa8..ee781a2ad 100644 --- a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceWriteCommand.java +++ b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/modules/scriptSpaces/ScriptSpaceWriteCommand.java @@ -7,8 +7,8 @@ import net.zscript.javareceiver.core.ZscriptCommandOutStream; import net.zscript.javareceiver.execution.CommandContext; import net.zscript.javareceiver.scriptSpaces.ScriptSpace; +import net.zscript.javareceiver.scriptSpaces.ScriptSpaceTokenBuffer.ScriptSpaceWriterTokenBuffer; import net.zscript.model.components.ZscriptStatus; -import net.zscript.tokenizer.ScriptSpaceBuffer.ScriptSpaceWriterBuffer; import net.zscript.tokenizer.Tokenizer; public class ScriptSpaceWriteCommand { @@ -21,18 +21,21 @@ public static void execute(List spaces, CommandContext ctx) { ctx.status(ZscriptStatus.VALUE_OUT_OF_RANGE); return; } + ZscriptCommandOutStream out = ctx.getOutStream(); ScriptSpace target = spaces.get(spaceIndex.getAsInt()); if (target.isRunning()) { ctx.status(ZscriptStatus.COMMAND_FAIL); return; } - ScriptSpaceWriterBuffer writer; + + ScriptSpaceWriterTokenBuffer writer; if (ctx.hasField((byte) 'R')) { writer = target.overwrite(); } else { writer = target.append(); } + Tokenizer tok = new Tokenizer(writer.getTokenWriter(), 2); for (Iterator iterator = ctx.getBigField(); iterator.hasNext(); ) { tok.accept(iterator.next()); diff --git a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpace.java b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpace.java index 4324c33bf..ea5e85a9e 100644 --- a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpace.java +++ b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpace.java @@ -6,22 +6,24 @@ import net.zscript.javareceiver.core.Zscript; import net.zscript.javareceiver.execution.ActionSource; import net.zscript.javareceiver.execution.ZscriptAction; +import net.zscript.javareceiver.scriptSpaces.ScriptSpaceTokenBuffer.ScriptSpaceWriterTokenBuffer; import net.zscript.javareceiver.semanticParser.ExecutionActionFactory; import net.zscript.javareceiver.semanticParser.SemanticParser; -import net.zscript.tokenizer.ScriptSpaceBuffer; -import net.zscript.tokenizer.ScriptSpaceBuffer.ScriptSpaceWriterBuffer; import net.zscript.tokenizer.Tokenizer; +/** + * + */ public class ScriptSpace implements ActionSource { - private final ScriptSpaceOutStream outStream; - private final ScriptSpaceBuffer buffer; - private final SemanticParser parser; - private final boolean canBeWrittenTo; + private final ScriptSpaceOutStream outStream; + private final ScriptSpaceTokenBuffer buffer; + private final SemanticParser parser; + private final boolean isWritable; public static ScriptSpace from(Zscript z, String str) { - ScriptSpaceBuffer buffer = new ScriptSpaceBuffer(); - ScriptSpaceWriterBuffer writer = buffer.fromStart(); - Tokenizer tok = new Tokenizer(writer.getTokenWriter(), 2); + ScriptSpaceTokenBuffer buffer = new ScriptSpaceTokenBuffer(); + ScriptSpaceWriterTokenBuffer writer = buffer.fromStart(); + Tokenizer tok = new Tokenizer(writer.getTokenWriter(), 2); for (byte b : str.getBytes(StandardCharsets.UTF_8)) { tok.accept(b); } @@ -30,17 +32,17 @@ public static ScriptSpace from(Zscript z, String str) { } public static ScriptSpace blank(Zscript z) { - ScriptSpaceBuffer buffer = new ScriptSpaceBuffer(); - ScriptSpace space = new ScriptSpace(z, buffer, true); + ScriptSpaceTokenBuffer buffer = new ScriptSpaceTokenBuffer(); + ScriptSpace space = new ScriptSpace(z, buffer, true); space.stop(); return space; } - public ScriptSpace(Zscript z, ScriptSpaceBuffer buffer, boolean canBeWrittenTo) { + private ScriptSpace(Zscript z, ScriptSpaceTokenBuffer buffer, boolean isWritable) { this.buffer = buffer; this.parser = new SemanticParser(buffer.getTokenReader(), new ExecutionActionFactory()); this.outStream = new ScriptSpaceOutStream(z, parser, new byte[32]); - this.canBeWrittenTo = canBeWrittenTo; + this.isWritable = isWritable; } @Override @@ -53,20 +55,20 @@ public OutStream getOutStream(Zscript zscript) { return outStream; } - public boolean canBeWrittenTo() { - return canBeWrittenTo; + public boolean isWritable() { + return isWritable; } - public ScriptSpaceWriterBuffer append() { - if (canBeWrittenTo) { + public ScriptSpaceWriterTokenBuffer append() { + if (isWritable) { return buffer.append(); } else { throw new UnsupportedOperationException(); } } - public ScriptSpaceWriterBuffer overwrite() { - if (canBeWrittenTo) { + public ScriptSpaceWriterTokenBuffer overwrite() { + if (isWritable) { return buffer.fromStart(); } else { throw new UnsupportedOperationException(); @@ -88,5 +90,4 @@ public void run() { public void stop() { parser.stop(); } - } diff --git a/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpaceTokenBuffer.java b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpaceTokenBuffer.java new file mode 100644 index 000000000..394916846 --- /dev/null +++ b/receivers/jvm/java-receiver/src/main/java/net/zscript/javareceiver/scriptSpaces/ScriptSpaceTokenBuffer.java @@ -0,0 +1,74 @@ +package net.zscript.javareceiver.scriptSpaces; + +import net.zscript.tokenizer.AbstractArrayTokenBuffer; + +public class ScriptSpaceTokenBuffer extends AbstractArrayTokenBuffer { + + public ScriptSpaceTokenBuffer() { + super(new byte[0], 0); + } + + private int getAvailableWrite(int writeCursor) { + return (writeCursor >= getReadStart() ? getDataSize() : 0) + getReadStart() - writeCursor - 1; + } + + @Override + protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { + return getAvailableWrite(writeCursor) >= size; + } + + @Override + protected int offsetOnOverflow(int overflowedOffset) { + return overflowedOffset - getDataSize(); + } + + public ScriptSpaceWriterTokenBuffer append() { + return new ScriptSpaceWriterTokenBuffer(this, getInternalData(), true); + } + + public ScriptSpaceWriterTokenBuffer fromStart() { + return new ScriptSpaceWriterTokenBuffer(this, new byte[20], false); + } + + public int getCurrentLength() { + return getDataSize(); + } + + @Override + public TokenWriter getTokenWriter() { + throw new UnsupportedOperationException(); + } + + /** + * Special token buffer for collecting tokens to be written to a real ScriptSpaceTokenBuffer. + */ + public static class ScriptSpaceWriterTokenBuffer extends AbstractArrayTokenBuffer { + private final ScriptSpaceTokenBuffer parent; + + private ScriptSpaceWriterTokenBuffer(ScriptSpaceTokenBuffer parent, byte[] data, boolean append) { + super(data, append ? data.length : 0); + this.parent = parent; + // ensure a reasonable amount of space + if (getDataSize() < 32) { + extendData(32 - getDataSize()); + } + } + + @Override + protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { + return true; + } + + @Override + protected int offsetOnOverflow(int overflowedOffset) { + extendData(getDataSize() + overflowedOffset); + return overflowedOffset; + } + + public void commitChanges() { + parent.importInternalDataFrom(this); + // parent.data = Arrays.copyOf(data, writeStart); + // parent.writeStart = writeStart + 2; // Question: Why is it +2 ?? + } + } +} diff --git a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/core/StringWriterOutStream.java b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/core/StringWriterOutStream.java index 999b1565a..a9a42b011 100644 --- a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/core/StringWriterOutStream.java +++ b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/core/StringWriterOutStream.java @@ -11,14 +11,18 @@ */ public class StringWriterOutStream extends OutputStreamOutStream { private final StringWriter stringWriter; - private boolean open = false; + private boolean open = false; public StringWriterOutStream() throws IOException { this(new StringWriter()); } private StringWriterOutStream(StringWriter stringWriter) throws IOException { - super(new WriterOutputStream.Builder().setCharset(StandardCharsets.ISO_8859_1).setWriter(stringWriter).setWriteImmediately(true).get()); + super(new WriterOutputStream.Builder() + .setCharset(StandardCharsets.ISO_8859_1) + .setWriter(stringWriter) + .setWriteImmediately(true) + .get()); this.stringWriter = stringWriter; } diff --git a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/fullRun/FullRunningTest.java b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/fullRun/FullRunningTest.java index ad5c38d45..ddc518aca 100644 --- a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/fullRun/FullRunningTest.java +++ b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/fullRun/FullRunningTest.java @@ -1,9 +1,9 @@ package net.zscript.javareceiver.fullRun; -import static org.assertj.core.api.Assertions.assertThat; - import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -11,6 +11,7 @@ import net.zscript.javareceiver.core.Zscript; import net.zscript.javareceiver.modules.core.ZscriptCoreModule; +@SuppressWarnings("StatementWithEmptyBody") public class FullRunningTest { private final Zscript zscript = new Zscript(); @@ -26,9 +27,38 @@ void setup() throws IOException { public void shouldRunBasicCommand() { StringChannel channel = StringChannel.from("Z1AB\n", outStream); zscript.addChannel(channel); + while (zscript.progress()) { } + assertThat(outStream.getStringAndReset()).isEqualTo("!ABS\n"); } + @Test + public void shouldRunSeveralBasicCommands() { + StringChannel channel = StringChannel.from("Z1AB\nZ1CD\n", outStream); + zscript.addChannel(channel); + + while (zscript.progress()) { + } + + assertThat(outStream.getStringAndReset()).isEqualTo("!ABS\n!CDS\n"); + } + + @Test + public void shouldHandleMultipleChannels() throws IOException { + StringWriterOutStream outStream1 = new StringWriterOutStream(); + StringChannel channel1 = StringChannel.from("Z1AB\nZ1CD\n", outStream1); + zscript.addChannel(channel1); + + StringWriterOutStream outStream2 = new StringWriterOutStream(); + StringChannel channel2 = StringChannel.from("Z1EF\n", outStream2); + zscript.addChannel(channel2); + + while (zscript.progress()) { + } + + assertThat(outStream1.getStringAndReset()).isEqualTo("!ABS\n!CDS\n"); + assertThat(outStream2.getStringAndReset()).isEqualTo("!EFS\n"); + } } diff --git a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/scriptSpace/ScriptSpaceTest.java b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/scriptSpace/ScriptSpaceTest.java index 55e471858..22b207355 100644 --- a/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/scriptSpace/ScriptSpaceTest.java +++ b/receivers/jvm/java-receiver/src/test/java/net/zscript/javareceiver/scriptSpace/ScriptSpaceTest.java @@ -1,9 +1,9 @@ package net.zscript.javareceiver.scriptSpace; -import static org.assertj.core.api.Assertions.assertThat; - import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,6 +14,7 @@ import net.zscript.javareceiver.modules.scriptSpaces.ScriptSpaceModule; import net.zscript.javareceiver.scriptSpaces.ScriptSpace; +@SuppressWarnings("StatementWithEmptyBody") public class ScriptSpaceTest { private final ScriptSpaceModule module = new ScriptSpaceModule(); @@ -31,10 +32,10 @@ void setup() throws IOException { @Test public void shouldRunScriptSpaceWithFailuresAndErrors() { - ScriptSpace space = ScriptSpace.from(zscript, "Z1&Z0\nZ1ABC&Z1S1\nZ1S1|Z0\nZ1S10\n"); + ScriptSpace space = ScriptSpace.from(zscript, "Z1 & Z0\n Z1ABC & Z1S1\n Z1S1 | Z0\n Z1S10\n"); zscript.addActionSource(space); module.addScriptSpace(space); - while (zscript.progress()) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!ABCS&S1\n!S10\n"); } @@ -42,12 +43,12 @@ public void shouldRunScriptSpaceWithFailuresAndErrors() { @Test public void scriptSpaceShouldJamZscript() { StringChannel channel = StringChannel.from("Z1AB\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z1&Z0\n"); + ScriptSpace space = ScriptSpace.from(zscript, "Z1 & Z0\n"); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo(""); } @@ -55,12 +56,11 @@ public void scriptSpaceShouldJamZscript() { @Test public void scriptSpaceDelayShouldUnjamZscript() { StringChannel channel = StringChannel.from("Z1AB\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z2&Z0\n%Z28M10\n"); + ScriptSpace space = ScriptSpace.from(zscript, "Z2 & Z0\n %Z28M10\n"); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!ABS\n"); } @@ -72,90 +72,83 @@ public void scriptSpaceDelayWithLockingShouldJamZscript() { zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo(""); } @Test public void shouldReadScriptSpaceCapabilities() { - StringChannel channel = StringChannel.from("Z2&Z20\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z2&Z0\n"); + StringChannel channel = StringChannel.from("Z2 & Z20\n", outStream); + ScriptSpace space = ScriptSpace.from(zscript, "Z2 & Z0\n"); space.stop(); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&C107P1S\n"); } @Test public void shouldReadScriptSpaceSetup() { - StringChannel channel = StringChannel.from("Z2&Z21P0\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z2&Z0\n"); + StringChannel channel = StringChannel.from("Z2 & Z21P0\n", outStream); + ScriptSpace space = ScriptSpace.from(zscript, "Z2 & Z0\n"); space.stop(); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&P7LffffS\n"); } @Test public void shouldRunScriptSpaceFromSetup() { - StringChannel channel = StringChannel.from("Z2&Z21P0R1\nZ\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z2&Z0\n"); + StringChannel channel = StringChannel.from("Z2 & Z21P0R1\n Z\n", outStream); + ScriptSpace space = ScriptSpace.from(zscript, "Z2 & Z0\n"); space.stop(); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&P7LffffS\n"); } @Test public void shouldRunAndStopScriptSpaceFromSetup() { - StringChannel channel = StringChannel.from("Z2&Z21P0R1&Z21P&Z21PR&Z21P\nZ1\n", outStream); - ScriptSpace space = ScriptSpace.from(zscript, "Z2&Z1S1\n"); + StringChannel channel = StringChannel.from("Z2 & Z21P0R1 & Z21P & Z21PR & Z21P\n Z1\n", outStream); + ScriptSpace space = ScriptSpace.from(zscript, "Z2 & Z1S1\n"); space.stop(); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&PbLffffS&PbRLffffS&PbRLffffS&PbLffffS\n!S\n"); } @Test public void shouldCreateBlankSpace() { - StringChannel channel = StringChannel.from("Z2&Z21P\n", outStream); + StringChannel channel = StringChannel.from("Z2 & Z21P\n", outStream); ScriptSpace space = ScriptSpace.blank(zscript); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&PWLffffS\n"); } @Test public void shouldWriteToBlankSpace() { - StringChannel channel = StringChannel.from("Z2&Z21P&Z22P\"Z1S10=0a\"&Z21PR1\n", outStream); + StringChannel channel = StringChannel.from("Z2 & Z21P & Z22P\"Z1S10=0a\" & Z21PR1\n", outStream); ScriptSpace space = ScriptSpace.blank(zscript); zscript.addActionSource(space); zscript.addChannel(channel); module.addScriptSpace(space); - int i = 0; - while (zscript.progress() && i++ < 1000000) { + for (int i = 0; zscript.progress() && i < 1000000; i++) { } assertThat(outStream.getStringAndReset()).isEqualTo("!AS&PWLffffS&LffffS&P7WLffffS\n!S10\n"); } diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenArrayBuffer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/AbstractArrayTokenBuffer.java similarity index 87% rename from receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenArrayBuffer.java rename to receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/AbstractArrayTokenBuffer.java index b23764dce..61cd2d72d 100644 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenArrayBuffer.java +++ b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/AbstractArrayTokenBuffer.java @@ -4,25 +4,25 @@ import java.util.NoSuchElementException; import java.util.Optional; +import static java.lang.String.format; import static net.zscript.tokenizer.TokenBuffer.isMarker; import static net.zscript.tokenizer.TokenBuffer.isSequenceEndMarker; /** - * Array based implementation of a Token Buffer - the tokens making up incoming command or response sequences are encoded and accessed here. Rules are: + * Array based implementation of a TokenBuffer - the tokens making up incoming command or response sequences are encoded and accessed here. Rules are: *

    *
  1. There is a writable area, owned by a TokenWriter, in the space writeStart <= i < readStart.
  2. *
  3. There is a readable area, owned by a TokenIterator, in the space readStart <= i < writeStart.
  4. *
  5. A token is written as >=2 bytes at writeStart: key | datalen | [data] - so tokens can be iterated by adding (datalen+2) to an existing token start.
  6. *
  7. A marker is written as 1 byte at writeStart, indicating a dataless key - markers are identified as a key with top 3 bits set (eg from 0xe0-0xff).
  8. *
  9. Tokens may exceed datalen of 255 using additional new token with special key TOKEN_EXTENSION
  10. - *
  11. *
*/ -public abstract class TokenArrayBuffer implements TokenBuffer { +public abstract class AbstractArrayTokenBuffer implements TokenBuffer { private static final byte MAX_TOKEN_DATA_LENGTH = (byte) 255; /** the ring-buffer's data array */ - protected byte[] data; + private byte[] data; private final TokenWriter tokenWriter; @@ -31,26 +31,40 @@ public abstract class TokenArrayBuffer implements TokenBuffer { private final TokenBufferFlags flags; /** index of first byte owned by reader, writable space ends just before it */ - protected int readStart = 0; + private int readStart; /** index of first byte owned by TokenWriter, readable space ends just before it */ - protected int writeStart = 0; + private int writeStart; // construct via static factories - protected TokenArrayBuffer(final int sz) { - this(new byte[sz]); + protected AbstractArrayTokenBuffer(final int sz) { + this(new byte[sz], 0); } // construct via static factories - protected TokenArrayBuffer(final byte[] preloaded) { - data = preloaded; - tokenWriter = new TokenArrayBufferWriter(); - tokenReader = new TokenArrayBufferReader(); - flags = new TokenBufferFlags(); + protected AbstractArrayTokenBuffer(final byte[] preloaded, int writeStart) { + this.data = preloaded; + this.tokenWriter = new ArrayTokenBufferWriter(); + this.tokenReader = new ArrayTokenBufferReader(); + this.flags = new TokenBufferFlags(); + this.writeStart = writeStart; + this.readStart = 0; } - // Visible for testing only! - byte[] getInternalData() { - return data.clone(); + protected final byte[] getInternalData() { + return data; + } + + protected final int getDataSize() { + return data.length; + } + + protected final void importInternalDataFrom(final AbstractArrayTokenBuffer other) { + this.data = Arrays.copyOf(other.data, other.writeStart); + this.writeStart = other.writeStart; + } + + protected final void extendData(int extra) { + data = Arrays.copyOf(data, data.length + extra); } @Override @@ -65,7 +79,11 @@ public TokenReader getTokenReader() { protected abstract boolean checkAvailableCapacityFrom(final int writeCursor, final int size); - private class TokenArrayBufferWriter implements TokenWriter { + protected final int getReadStart() { + return readStart; + } + + private class ArrayTokenBufferWriter implements TokenWriter { /** index of data-length field of most recent token segment (esp required for long multi-segment tokens) */ private int writeLastLen = 0; /** the current write index into data array */ @@ -235,13 +253,28 @@ private void moveCursor() { } /** - * Utility method that performs wrap-around for a lookup into the buffer + * Utility method that finds the correct offset from the supplied index, handling the off-the-end condition in a subclass-specific way (eg wrap, or expand the buffer, etc). * * @param index location in the underlying ring-buffer * @param offset an offset to add, >= 0 * @return a new index, at the requested offset */ - abstract int offset(final int index, final int offset); + final int offset(final int index, final int offset) { + if (index < 0 || offset < 0 || offset >= data.length) { + throw new IllegalArgumentException(format("Unexpected values [index=%d, offset=%d]", index, offset)); + } + final int newOffset = index + offset; + return (newOffset < data.length) ? newOffset : offsetOnOverflow(newOffset); + } + + /** + * Hook method that allows an action to be taken when an offset lookup goes off the end of the data buffer. + * + * @param overflowedOffset the offset we were trying to access, required to be data.length <= overflowedOffset < 2*data.length (ie off the end, but not by multiple + * rounds) + * @return a new offset that should be used instead (eg loop around if this is a ring buffer, or just the overflowed offset if we've expanded the buffer) + */ + protected abstract int offsetOnOverflow(final int overflowedOffset); /** * Utility for determining whether the specified index is in the current readable area, defined as readStart <= index < writeStart (but accounting for this being a @@ -257,7 +290,7 @@ boolean isInReadableArea(final int index) { || writeStart < readStart && (index < writeStart || index >= readStart); } - private class TokenArrayBufferReader implements TokenReader { + private class ArrayTokenBufferReader implements TokenReader { private class ArrayBufferTokenIterator implements TokenBufferIterator { private int index; diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/ScriptSpaceBuffer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/ScriptSpaceBuffer.java deleted file mode 100644 index cfa10afc6..000000000 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/ScriptSpaceBuffer.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.zscript.tokenizer; - -import java.util.Arrays; - -import static java.text.MessageFormat.format; - -public class ScriptSpaceBuffer extends TokenArrayBuffer { - - public ScriptSpaceBuffer() { - super(new byte[0]); - writeStart = 0; - } - - private int getAvailableWrite(int writeCursor) { - return (writeCursor >= readStart ? data.length : 0) + readStart - writeCursor - 1; - } - - @Override - protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { - return getAvailableWrite(writeCursor) >= size; - } - - @Override - int offset(final int index, final int offset) { - if (index < 0 || offset < 0 || offset >= data.length) { - throw new IllegalArgumentException(format("Unexpected values [pos={}, offset={}]", index, offset)); - } - return (index + offset) % data.length; - } - - @Override - public TokenWriter getTokenWriter() { - throw new UnsupportedOperationException(); - } - - public static class ScriptSpaceWriterBuffer extends TokenArrayBuffer { - private final ScriptSpaceBuffer parent; - - private ScriptSpaceWriterBuffer(ScriptSpaceBuffer parent, byte[] data) { - super(data); - this.parent = parent; - offset(data.length, 1); - } - - @Override - protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { - return true; - } - - @Override - int offset(int index, int offset) { - if (index < 0 || offset < 0) { - throw new IllegalArgumentException(format("Unexpected values [pos={}, offset={}]", index, offset)); - } - if (index + offset >= data.length) { - int newSize = index + offset + data.length; - data = Arrays.copyOf(data, newSize); - } - return index + offset; - } - - public void commitChanges() { - parent.data = Arrays.copyOf(data, writeStart); - parent.writeStart = writeStart + 2; - } - } - - public ScriptSpaceWriterBuffer append() { - ScriptSpaceWriterBuffer writer = new ScriptSpaceWriterBuffer(this, data); - writer.writeStart = data.length; - return writer; - } - - public ScriptSpaceWriterBuffer fromStart() { - ScriptSpaceWriterBuffer writer = new ScriptSpaceWriterBuffer(this, new byte[20]); - return writer; - } - - public int getCurrentLength() { - return data.length; - } - -} diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenBuffer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenBuffer.java index b9d918075..7d9dd0bf1 100644 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenBuffer.java +++ b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenBuffer.java @@ -2,6 +2,9 @@ import java.util.NoSuchElementException; +/** + * Defines the interface for the standard buffer for tokenizing Zscript. + */ public interface TokenBuffer { /** * Special token key to indicate that this segment is a continuation of the previous token due to its data size hitting maximum. diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenExtendingBuffer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenExtendingBuffer.java deleted file mode 100644 index 7df2ef766..000000000 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenExtendingBuffer.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.zscript.tokenizer; - -import java.util.Arrays; - -import static java.text.MessageFormat.format; - -public class TokenExtendingBuffer extends TokenArrayBuffer { - public TokenExtendingBuffer() { - super(20); - } - - @Override - protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { - return true; - } - - @Override - int offset(int index, int offset) { - if (index < 0 || offset < 0) { - throw new IllegalArgumentException(format("Unexpected values [pos={}, offset={}]", index, offset)); - } - if (index + offset >= data.length) { - int newSize = index + offset + data.length; - data = Arrays.copyOf(data, newSize); - } - return index + offset; - } -} diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenRingBuffer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenRingBuffer.java index 6a5a5c99a..946898197 100644 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenRingBuffer.java +++ b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/TokenRingBuffer.java @@ -1,16 +1,15 @@ package net.zscript.tokenizer; -import static java.text.MessageFormat.format; +import static java.lang.String.format; /** - * Ring-buffer implementation of a Token Buffer - a specific implementation of the ArrayBuffer. Additional rules are: + * Ring-buffer implementation of a TokenBuffer - a specific implementation of the AbstractArrayTokenBuffer. Additional rules are: *
    *
  1. Maximum size is 64k bytes - Zscript isn't expected to work on huge datasets.
  2. *
  3. All indices are incremented modulo the length of the underlying byte array.
  4. - *
  5. *
*/ -public class TokenRingBuffer extends TokenArrayBuffer { +public class TokenRingBuffer extends AbstractArrayTokenBuffer { /** * Zscript shouldn't need huge buffers, so 64K is our extreme limit. It should be addressable by uint16 indexes. Note that *exact* 64K size implies that data.length cannot be * held in a uint16, so careful code required if porting! @@ -24,12 +23,12 @@ public static TokenRingBuffer createBufferWithCapacity(final int sz) { private TokenRingBuffer(int sz) { super(sz); if (sz > MAX_RING_BUFFER_SIZE) { - throw new IllegalArgumentException(format("size too big [sz={}, max={}]", sz, MAX_RING_BUFFER_SIZE)); + throw new IllegalArgumentException(format("size too big [sz=%d, max=%d]", sz, MAX_RING_BUFFER_SIZE)); } } private int getAvailableWrite(int writeCursor) { - return (writeCursor >= readStart ? data.length : 0) + readStart - writeCursor - 1; + return (writeCursor >= getReadStart() ? getDataSize() : 0) + getReadStart() - writeCursor - 1; } @Override @@ -38,10 +37,7 @@ protected boolean checkAvailableCapacityFrom(int writeCursor, int size) { } @Override - int offset(final int index, final int offset) { - if (index < 0 || offset < 0 || offset >= data.length) { - throw new IllegalArgumentException(format("Unexpected values [pos={}, offset={}]", index, offset)); - } - return (index + offset) % data.length; + protected int offsetOnOverflow(int overflowedOffset) { + return overflowedOffset - getDataSize(); } } diff --git a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/Tokenizer.java b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/Tokenizer.java index 6f9341ace..a06c50d1e 100644 --- a/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/Tokenizer.java +++ b/receivers/jvm/java-tokenizer/src/main/java/net/zscript/tokenizer/Tokenizer.java @@ -119,7 +119,7 @@ public boolean checkCapacity() { /** * Requests to process a byte of Zscript input into the tokenizer buffer, if there's capacity. If the offer returns true, then the byte has been consumed; otherwise the byte - * was rejected, and it should be kept so that it can be presented again. It is possible the + * was rejected, and it should be kept so that it can be presented again. * * @param b the new byte of zscript input * @return true if the byte was processed, false otherwise