From d03aead855dda50e59f26e04e3308df0aca410fd Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Sun, 30 Nov 2025 13:32:15 +0100 Subject: [PATCH 1/7] Update example to use libstored 2.0.0 --- examples/Arq/LibStored.Arq/Dockerfile | 7 +++++-- examples/Arq/LibStored.Net.Arq.AppHost/AppHost.cs | 2 +- examples/Directory.Build.props | 8 ++++++++ examples/Sync/LibStored.Net.Example.AppHost/Dockerfile | 5 ++++- .../LibStored.Net.Example.AppHost.csproj | 10 +++++----- examples/Sync/LibStored.Net.Example.AppHost/Program.cs | 2 +- 6 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 examples/Directory.Build.props diff --git a/examples/Arq/LibStored.Arq/Dockerfile b/examples/Arq/LibStored.Arq/Dockerfile index b6a615c..efac3e1 100644 --- a/examples/Arq/LibStored.Arq/Dockerfile +++ b/examples/Arq/LibStored.Arq/Dockerfile @@ -1,11 +1,13 @@ # Add ARG for libstored version -ARG LIBSTORED_VERSION=1.8.0 +ARG LIBSTORED_VERSION=2.0.0 FROM ubuntu:22.04 as builder # Use the ARG after FROM ARG LIBSTORED_VERSION +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ @@ -19,6 +21,7 @@ RUN apt-get update && \ plantuml \ python3-venv \ python3-dev \ + python3-tk \ lsb-release \ libgl1 \ libegl1 \ @@ -62,4 +65,4 @@ COPY --from=builder /app /app WORKDIR /app/bin -CMD ["./arq"] \ No newline at end of file +CMD ["./arq"] diff --git a/examples/Arq/LibStored.Net.Arq.AppHost/AppHost.cs b/examples/Arq/LibStored.Net.Arq.AppHost/AppHost.cs index 7108e3d..9ee6a23 100644 --- a/examples/Arq/LibStored.Net.Arq.AppHost/AppHost.cs +++ b/examples/Arq/LibStored.Net.Arq.AppHost/AppHost.cs @@ -2,7 +2,7 @@ var builder = DistributedApplication.CreateBuilder(args); -const string libstoredVersion = "1.8.0"; +const string libstoredVersion = "2.0.0"; var arq = builder.AddDockerfile("libstored-arq", "../LibStored.Arq/", "Dockerfile") .WithImage("demcon/libstored-arq-example") .WithImageTag(libstoredVersion) diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props new file mode 100644 index 0000000..bd64a71 --- /dev/null +++ b/examples/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + net10.0 + net10.0 + + diff --git a/examples/Sync/LibStored.Net.Example.AppHost/Dockerfile b/examples/Sync/LibStored.Net.Example.AppHost/Dockerfile index 62a19b5..e3bfed3 100644 --- a/examples/Sync/LibStored.Net.Example.AppHost/Dockerfile +++ b/examples/Sync/LibStored.Net.Example.AppHost/Dockerfile @@ -1,11 +1,13 @@ # Add ARG for libstored version -ARG LIBSTORED_VERSION=1.8.0 +ARG LIBSTORED_VERSION=2.0.0 FROM ubuntu:22.04 as builder # Use the ARG after FROM ARG LIBSTORED_VERSION +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ @@ -19,6 +21,7 @@ RUN apt-get update && \ plantuml \ python3-venv \ python3-dev \ + python3-tk \ lsb-release \ libgl1 \ libegl1 \ diff --git a/examples/Sync/LibStored.Net.Example.AppHost/LibStored.Net.Example.AppHost.csproj b/examples/Sync/LibStored.Net.Example.AppHost/LibStored.Net.Example.AppHost.csproj index d47e470..9a7b996 100644 --- a/examples/Sync/LibStored.Net.Example.AppHost/LibStored.Net.Example.AppHost.csproj +++ b/examples/Sync/LibStored.Net.Example.AppHost/LibStored.Net.Example.AppHost.csproj @@ -1,6 +1,6 @@ - + - + Exe @@ -10,9 +10,9 @@ - - - + + + diff --git a/examples/Sync/LibStored.Net.Example.AppHost/Program.cs b/examples/Sync/LibStored.Net.Example.AppHost/Program.cs index adba444..2a304d3 100644 --- a/examples/Sync/LibStored.Net.Example.AppHost/Program.cs +++ b/examples/Sync/LibStored.Net.Example.AppHost/Program.cs @@ -12,7 +12,7 @@ builder.AddDockerComposeEnvironment("docker-compose"); -const string libstoredVersion = "1.8.0"; +const string libstoredVersion = "2.0.0"; var sync = builder.AddDockerfile("libstored", ".", "Dockerfile") .WithImage("demcon/libstored") .WithImageTag(libstoredVersion) From 602b6b77e984a01eea17c0e1706552cc31a5ff54 Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Sun, 30 Nov 2025 22:55:38 +0100 Subject: [PATCH 2/7] Update libstored python version --- .../Sync/LibStored.Net.Example.AppHost/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Sync/LibStored.Net.Example.AppHost/python/requirements.txt b/examples/Sync/LibStored.Net.Example.AppHost/python/requirements.txt index 8329f18..9bb899a 100644 --- a/examples/Sync/LibStored.Net.Example.AppHost/python/requirements.txt +++ b/examples/Sync/LibStored.Net.Example.AppHost/python/requirements.txt @@ -1 +1 @@ -libstored==1.7.1 \ No newline at end of file +libstored==2.0.0 From 41e6c8076940f1e5592907812322233781c36ce4 Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Sun, 30 Nov 2025 22:56:30 +0100 Subject: [PATCH 3/7] Add Crc32, todo add more tests and verify --- src/LibStored.Net/Protocol/Crc32Layer.cs | 140 ++++++++++++++++++++++ test/LibStored.Net.Tests/ProtocolTests.cs | 20 ++++ 2 files changed, 160 insertions(+) create mode 100644 src/LibStored.Net/Protocol/Crc32Layer.cs diff --git a/src/LibStored.Net/Protocol/Crc32Layer.cs b/src/LibStored.Net/Protocol/Crc32Layer.cs new file mode 100644 index 0000000..4c332d7 --- /dev/null +++ b/src/LibStored.Net/Protocol/Crc32Layer.cs @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2025 Guus Kuiper +// +// SPDX-License-Identifier: MIT + +using System.Buffers.Binary; +using Microsoft.Extensions.Logging; + +namespace LibStored.Net.Protocol; + +/// +/// A protocol layer that adds a 32-bit CRC (reversed polynomial 0x04c11db7) for message integrity. +/// Calculates and verifies CRC values for encoded and decoded messages. +/// +public class Crc32Layer : ProtocolLayer +{ + private static readonly uint[] crc32Table = + [ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, + ]; + + private readonly ILogger? _logger; + private readonly uint _init = 0xffffffff; + private readonly uint _finalXor = 0xffffffff; + private uint _crc; + + /// + /// Initializes a new instance of the class. + /// + /// Optional logger for CRC warnings. + public Crc32Layer(ILogger? logger = null) + { + _logger = logger; + _crc = _init; // Initialize CRC to the initial value + } + + /// + /// Decodes a buffer, verifying the CRC and passing valid data to the next layer. + /// + /// The buffer to decode, including CRC bytes. + public override void Decode(Span buffer) + { + if (buffer.Length < 4) + { + return; + } + + uint crc = _init; + for (int i = 0; i < buffer.Length - 4; i++) + { + crc = Compute(buffer[i], crc); + } + + uint bufferCrc = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(buffer.Length - 4, 4)); + if ((crc ^ _finalXor) != bufferCrc) + { + // CRC mismatch, ignore the packet + _logger?.LogWarning("Invalid crc check: {Actual}, expected: {Expected}", bufferCrc, crc); + return; + } + + base.Decode(buffer.Slice(0, buffer.Length - 4)); + } + + /// + /// Encodes a buffer, updating the CRC and appending it when the message is complete. + /// + /// The data to encode. + /// Indicates if this is the last buffer in the message. + public override void Encode(ReadOnlySpan buffer, bool last) + { + foreach (byte b in buffer) + { + _crc = Compute(b, _crc); + } + + base.Encode(buffer, false); + + if (last) + { + uint crc = _crc ^ _finalXor; + byte[] crcBytesBig = new byte[4]; + BinaryPrimitives.WriteUInt32BigEndian(crcBytesBig, crc); + base.Encode(crcBytesBig, true); + _crc = _init; // Reset CRC for the next message + } + } + + /// + public override void Reset() + { + _crc = _init; + base.Reset(); + } + + /// + public override int Mtu() => base.Mtu() switch + { + 0 => 0, + <= 5 => 1, + var x => x - 4, + }; + + private uint Compute(byte input, uint crc) => crc32Table[input ^ (byte)(crc & 0xFF)] ^ (crc >> 8); +} diff --git a/test/LibStored.Net.Tests/ProtocolTests.cs b/test/LibStored.Net.Tests/ProtocolTests.cs index 6c42609..b2285b9 100644 --- a/test/LibStored.Net.Tests/ProtocolTests.cs +++ b/test/LibStored.Net.Tests/ProtocolTests.cs @@ -246,6 +246,26 @@ public void Crc16DecodeFailsTest(string input) Assert.Empty(logging.Decoded); } + [Theory] + [InlineData("", "00000000")] + [InlineData("1", "3183DCEFB7")] + [InlineData("12", "31324F5344CD")] + [InlineData("123", "313233884863D2")] + public void Crc32EncodeTest(string input, string expectedHex) + { + byte[] expected = Convert.FromHexString(expectedHex); + string expectedString = Encoding.Latin1.GetString(expected); + + Protocol.Crc32Layer crc32 = new(); + Protocol.LoggingLayer logging = new(); + logging.Wrap(crc32); + + Encode(crc32, input); + + Assert.Single(logging.Encoded); + Assert.Equal([expectedString], logging.Encoded); + } + [Theory] [InlineData("123", "123")] [InlineData("123", "12", "3")] From 2abba5e4eecb875b4dcca7eff61d5946bc4d3016 Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Mon, 1 Dec 2025 18:39:51 +0100 Subject: [PATCH 4/7] ADd more crc32 tests, small refactor crc16 --- src/LibStored.Net/Protocol/Crc16Layer.cs | 2 +- test/LibStored.Net.Tests/ProtocolTests.cs | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/LibStored.Net/Protocol/Crc16Layer.cs b/src/LibStored.Net/Protocol/Crc16Layer.cs index 289fd55..ccad310 100644 --- a/src/LibStored.Net/Protocol/Crc16Layer.cs +++ b/src/LibStored.Net/Protocol/Crc16Layer.cs @@ -72,7 +72,7 @@ public override void Decode(Span buffer) crc = Compute(buffer[i], crc); } - if (crc != (ushort)(buffer[buffer.Length - 2] << 8 | buffer[buffer.Length - 1])) + if (crc != BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(buffer.Length - 2, 2))) { // CRC mismatch, ignore the packet _logger?.LogWarning("Invalid crc check: {Actual}, expected: {Expected}", buffer[^1], crc); diff --git a/test/LibStored.Net.Tests/ProtocolTests.cs b/test/LibStored.Net.Tests/ProtocolTests.cs index b2285b9..af9ae43 100644 --- a/test/LibStored.Net.Tests/ProtocolTests.cs +++ b/test/LibStored.Net.Tests/ProtocolTests.cs @@ -251,10 +251,11 @@ public void Crc16DecodeFailsTest(string input) [InlineData("1", "3183DCEFB7")] [InlineData("12", "31324F5344CD")] [InlineData("123", "313233884863D2")] + [InlineData("1234", "313233349BE3E0A3")] public void Crc32EncodeTest(string input, string expectedHex) { byte[] expected = Convert.FromHexString(expectedHex); - string expectedString = Encoding.Latin1.GetString(expected); + string expectedString = String(expected); Protocol.Crc32Layer crc32 = new(); Protocol.LoggingLayer logging = new(); @@ -266,6 +267,26 @@ public void Crc32EncodeTest(string input, string expectedHex) Assert.Equal([expectedString], logging.Encoded); } + [Theory] + [InlineData("", "00000000")] + [InlineData("1", "3183DCEFB7")] + [InlineData("12", "31324F5344CD")] + [InlineData("123", "313233884863D2")] + [InlineData("1234", "313233349BE3E0A3")] + public void Crc32DecodeTest(string expected, string inputHex) + { + byte[] input = Convert.FromHexString(inputHex); + + Protocol.Crc32Layer crc32 = new(); + Protocol.LoggingLayer logging = new(); + crc32.Wrap(logging); + + crc32.Decode(input); + + Assert.Single(logging.Decoded); + Assert.Equal([expected], logging.Decoded); + } + [Theory] [InlineData("123", "123")] [InlineData("123", "12", "3")] From 6a8dbedcf12058650f0cc204bef8f335b2f3ff83 Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Mon, 1 Dec 2025 18:53:56 +0100 Subject: [PATCH 5/7] Add IdleCheckLayer --- src/LibStored.Net/Protocol/IdleCheckLayer.cs | 52 ++++++++ test/LibStored.Net.Tests/ProtocolTests.cs | 127 +++++++++++-------- 2 files changed, 125 insertions(+), 54 deletions(-) create mode 100644 src/LibStored.Net/Protocol/IdleCheckLayer.cs diff --git a/src/LibStored.Net/Protocol/IdleCheckLayer.cs b/src/LibStored.Net/Protocol/IdleCheckLayer.cs new file mode 100644 index 0000000..9cbdaec --- /dev/null +++ b/src/LibStored.Net/Protocol/IdleCheckLayer.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2025 Guus Kuiper +// +// SPDX-License-Identifier: MIT + +namespace LibStored.Net.Protocol; + +/// +/// A layer that tracks if it sees communication through the stack. +/// +/// This may be used to check of long inactivity on stalled or disconnected +/// communication channels. +/// +public class IdleCheckLayer : ProtocolLayer +{ + /// + /// Checks if upstream was idle since the last call to + /// + public bool IdleUp { get; private set; } = true; + + /// + /// Checks if downstream was idle since the last call to + /// + public bool IdleDown { get; private set; } = true; + + /// + /// Checks if both up and down the stack was idle since the last call to . + /// + public bool Idle => IdleUp && IdleDown; + + /// + /// Resets the idle flags. + /// + public void SetIdle() + { + IdleUp = true; + IdleDown = true; + } + + /// + public override void Decode(Span buffer) + { + IdleUp = false; + base.Decode(buffer); + } + + /// + public override void Encode(ReadOnlySpan buffer, bool last) + { + IdleDown = false; + base.Encode(buffer, last); + } +} diff --git a/test/LibStored.Net.Tests/ProtocolTests.cs b/test/LibStored.Net.Tests/ProtocolTests.cs index af9ae43..6f313fc 100644 --- a/test/LibStored.Net.Tests/ProtocolTests.cs +++ b/test/LibStored.Net.Tests/ProtocolTests.cs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT using System.Text; +using LibStored.Net.Protocol; using Microsoft.Extensions.Logging.Abstractions; namespace LibStored.Net.Tests; @@ -12,15 +13,15 @@ public class ProtocolTests [Fact] public void LoggingLayerTest() { - Protocol.LoggingLayer logging = new(); + LoggingLayer logging = new(); byte[] bytes = Enumerable.Range(0, 255).Select(x => (byte)x).ToArray(); logging.Encode(bytes, true); Assert.Single(logging.Encoded); - Assert.Equal([ProtocolTests.String(bytes)], logging.Encoded); - Assert.Equal(bytes, ProtocolTests.Bytes(logging.Encoded[0])); + Assert.Equal([String(bytes)], logging.Encoded); + Assert.Equal(bytes, Bytes(logging.Encoded[0])); } [Theory] @@ -31,8 +32,8 @@ public void LoggingLayerTest() [InlineData("\u007f123\r", "\u007f\u007f123\u007f\u004d")] public void AsciiEncodeTest(string input, string expected) { - Protocol.AsciiEscapeLayer ascii = new(); - Protocol.LoggingLayer logging = new(); + AsciiEscapeLayer ascii = new(); + LoggingLayer logging = new(); logging.Wrap(ascii); Encode(ascii, input); @@ -51,8 +52,8 @@ public void AsciiEncodeTest(string input, string expected) [InlineData("\u007f\u007f", "\u007f")] public void AsciiEncodeSingleTest(string expected, string input) { - Protocol.AsciiEscapeLayer ascii = new(); - Protocol.LoggingLayer logging = new(); + AsciiEscapeLayer ascii = new(); + LoggingLayer logging = new(); logging.Wrap(ascii); Encode(ascii, input); @@ -71,8 +72,8 @@ public void AsciiEncodeSingleTest(string expected, string input) [InlineData("\u007f123\r", "\u007f\u007f123\u007f\u004d")] public void AsciiDecodeTest(string expected, string input) { - Protocol.AsciiEscapeLayer ascii = new(); - Protocol.LoggingLayer logging = new(); + AsciiEscapeLayer ascii = new(); + LoggingLayer logging = new(); ascii.Wrap(logging); Decode(ascii, input); @@ -86,8 +87,8 @@ public void AsciiDecodeTest(string expected, string input) [InlineData( "\u001b_123\u001b\\", "1", "2", "3")] public void TerminalEncodeTest(string expected, params string[] inputs) { - Protocol.TerminalLayer term = new(NullLogger.Instance); - Protocol.LoggingLayer logging = new(); + TerminalLayer term = new(NullLogger.Instance); + LoggingLayer logging = new(); logging.Wrap(term); for (int i = 0; i < inputs.Length; i++) @@ -110,15 +111,15 @@ public void TerminalDecodeTest(string expected, string input, string expectedDeb using StreamWriter writer = new(ms); Console.SetOut(writer); - Protocol.TerminalLayer term = new(NullLogger.Instance); - Protocol.LoggingLayer logging = new(); + TerminalLayer term = new(NullLogger.Instance); + LoggingLayer logging = new(); term.Wrap(logging); Decode(term, input); writer.Flush(); byte[] debug = ms.ToArray(); - string debugMessage = ProtocolTests.String(debug); + string debugMessage = String(debug); Assert.Single(logging.Decoded); Assert.Equal([expected], logging.Decoded); @@ -134,8 +135,8 @@ public void TerminalSplitDecodeTest(string expected, string expectedDebug, param using StreamWriter writer = new(ms); Console.SetOut(writer); - Protocol.TerminalLayer term = new(NullLogger.Instance); - Protocol.LoggingLayer logging = new(); + TerminalLayer term = new(NullLogger.Instance); + LoggingLayer logging = new(); term.Wrap(logging); foreach (string input in inputs) @@ -145,7 +146,7 @@ public void TerminalSplitDecodeTest(string expected, string expectedDebug, param writer.Flush(); byte[] debug = ms.ToArray(); - string debugMessage = ProtocolTests.String(debug); + string debugMessage = String(debug); Assert.Single(logging.Decoded); Assert.Equal([expected], logging.Decoded); @@ -159,8 +160,8 @@ public void TerminalSplitDecodeTest(string expected, string expectedDebug, param [InlineData("123", "123\xfc")] public void Crc8EncodeTest(string input, string expected) { - Protocol.Crc8Layer crc8 = new(); - Protocol.LoggingLayer logging = new(); + Crc8Layer crc8 = new(); + LoggingLayer logging = new(); logging.Wrap(crc8); Encode(crc8, input); @@ -176,8 +177,8 @@ public void Crc8EncodeTest(string input, string expected) [InlineData("123", "123\xfc")] public void Crc8DecodeTest(string expected, string input) { - Protocol.Crc8Layer crc8 = new(); - Protocol.LoggingLayer logging = new(); + Crc8Layer crc8 = new(); + LoggingLayer logging = new(); crc8.Wrap(logging); Decode(crc8, input); @@ -190,8 +191,8 @@ public void Crc8DecodeTest(string expected, string input) [InlineData("1234\xfc")] public void Crc8DecodeFailsTest(string input) { - Protocol.Crc8Layer crc8 = new(); - Protocol.LoggingLayer logging = new(); + Crc8Layer crc8 = new(); + LoggingLayer logging = new(); crc8.Wrap(logging); Decode(crc8, input); @@ -206,8 +207,8 @@ public void Crc8DecodeFailsTest(string input) [InlineData("123", "123\x1c\x84")] public void Crc16EncodeTest(string input, string expected) { - Protocol.Crc16Layer crc16 = new(); - Protocol.LoggingLayer logging = new(); + Crc16Layer crc16 = new(); + LoggingLayer logging = new(); logging.Wrap(crc16); Encode(crc16, input); @@ -223,8 +224,8 @@ public void Crc16EncodeTest(string input, string expected) [InlineData("123", "123\x1c\x84")] public void Crc16DecodeTest(string expected, string input) { - Protocol.Crc16Layer crc16 = new(); - Protocol.LoggingLayer logging = new(); + Crc16Layer crc16 = new(); + LoggingLayer logging = new(); crc16.Wrap(logging); Decode(crc16, input); @@ -237,8 +238,8 @@ public void Crc16DecodeTest(string expected, string input) [InlineData("1234\x1c\x84")] public void Crc16DecodeFailsTest(string input) { - Protocol.Crc16Layer crc16 = new(); - Protocol.LoggingLayer logging = new(); + Crc16Layer crc16 = new(); + LoggingLayer logging = new(); crc16.Wrap(logging); Decode(crc16, input); @@ -257,8 +258,8 @@ public void Crc32EncodeTest(string input, string expectedHex) byte[] expected = Convert.FromHexString(expectedHex); string expectedString = String(expected); - Protocol.Crc32Layer crc32 = new(); - Protocol.LoggingLayer logging = new(); + Crc32Layer crc32 = new(); + LoggingLayer logging = new(); logging.Wrap(crc32); Encode(crc32, input); @@ -277,8 +278,8 @@ public void Crc32DecodeTest(string expected, string inputHex) { byte[] input = Convert.FromHexString(inputHex); - Protocol.Crc32Layer crc32 = new(); - Protocol.LoggingLayer logging = new(); + Crc32Layer crc32 = new(); + LoggingLayer logging = new(); crc32.Wrap(logging); crc32.Decode(input); @@ -293,8 +294,8 @@ public void Crc32DecodeTest(string expected, string inputHex) [InlineData("123", "12", "3", "")] public void BufferLayerEncodeTest(string expected, params string[] inputs) { - Protocol.BufferLayer bufferLayer = new(); - Protocol.LoggingLayer logging = new(); + BufferLayer bufferLayer = new(); + LoggingLayer logging = new(); logging.Wrap(bufferLayer); for (int i = 0; i < inputs.Length; i++) @@ -315,8 +316,8 @@ public void BufferLayerEncodeTest(string expected, params string[] inputs) [InlineData("1234567E", "1234", "567", "")] public void SegmentationLayerEncode8Test(string expected, params string[] input) { - Protocol.SegmentationLayer segmentation = new(8); - Protocol.LoggingLayer logging = new(); + SegmentationLayer segmentation = new(8); + LoggingLayer logging = new(); logging.Wrap(segmentation); for (int i = 0; i < input.Length; i++) @@ -332,8 +333,8 @@ public void SegmentationLayerEncode8Test(string expected, params string[] input) [InlineData("1234567890", "123C", "456C", "789C", "0E")] public void SegmentationLayerEncodeTest(string input, params string[] expected) { - Protocol.SegmentationLayer segmentation = new(4); - Protocol.LoggingLayer logging = new(); + SegmentationLayer segmentation = new(4); + LoggingLayer logging = new(); logging.Wrap(segmentation); Encode(segmentation, input); @@ -344,8 +345,8 @@ public void SegmentationLayerEncodeTest(string input, params string[] expected) [Fact] public void SegmentationLayerEncodeMultipleTest() { - Protocol.SegmentationLayer segmentation = new(4); - Protocol.LoggingLayer logging = new(); + SegmentationLayer segmentation = new(4); + LoggingLayer logging = new(); logging.Wrap(segmentation); Encode(segmentation, "12345", false); @@ -361,8 +362,8 @@ public void SegmentationLayerEncodeMultipleTest() [InlineData("E", "")] public void SegmentationLayerDecodeTest(string input, params string[] expected) { - Protocol.LoggingLayer logging = new(); - Protocol.SegmentationLayer segmentation = new(4); + LoggingLayer logging = new(); + SegmentationLayer segmentation = new(4); segmentation.Wrap(logging); Decode(segmentation, input); @@ -373,8 +374,8 @@ public void SegmentationLayerDecodeTest(string input, params string[] expected) [Fact] public void SegmentationLayerDecodeEmptyTest() { - Protocol.LoggingLayer logging = new(); - Protocol.SegmentationLayer segmentation = new(4); + LoggingLayer logging = new(); + SegmentationLayer segmentation = new(4); segmentation.Wrap(logging); Decode(segmentation, ""); @@ -389,8 +390,8 @@ public void SegmentationLayerDecodeEmptyTest() [InlineData("123456789", "123C", "456789C", "E")] public void SegmentationLayerDecodeMultiTest(string expected, params string[] inputs) { - Protocol.LoggingLayer logging = new(); - Protocol.SegmentationLayer segmentation = new(4); + LoggingLayer logging = new(); + SegmentationLayer segmentation = new(4); segmentation.Wrap(logging); foreach (string input in inputs) @@ -405,9 +406,9 @@ public void SegmentationLayerDecodeMultiTest(string expected, params string[] in [Fact] public void LoopBackTest() { - Protocol.LoggingLayer loggingA = new(); - Protocol.LoggingLayer loggingB = new(); - Protocol.LoopbackLayer _ = new(loggingA, loggingB); + LoggingLayer loggingA = new(); + LoggingLayer loggingB = new(); + LoopbackLayer _ = new(loggingA, loggingB); Encode(loggingA, "Hello ", false); Encode(loggingB, "other text", false); @@ -418,15 +419,33 @@ public void LoopBackTest() Assert.Equal(["other text!"], loggingA.Decoded); } - private void Encode(Protocol.ProtocolLayer layer, string data, bool last = true) + [Fact] + public void IdleLayerTest() + { + IdleCheckLayer idle = new(); + Assert.True(idle.Idle); + + Encode(idle, "down"); + Assert.False(idle.Idle); + Assert.True(idle.IdleUp); + Assert.False(idle.IdleDown); + + Decode(idle, "up"); + Assert.False(idle.IdleUp); + + idle.SetIdle(); + Assert.True(idle.Idle); + } + + private void Encode(ProtocolLayer layer, string data, bool last = true) { - byte[] bytes = ProtocolTests.Bytes(data); + byte[] bytes = Bytes(data); layer.Encode(bytes, last); } - private void Decode(Protocol.ProtocolLayer layer, string data) + private void Decode(ProtocolLayer layer, string data) { - byte[] bytes = ProtocolTests.Bytes(data); + byte[] bytes = Bytes(data); layer.Decode(bytes); } From a421c1c48857a9a445d869e06ac01c141e53396c Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Mon, 1 Dec 2025 19:08:24 +0100 Subject: [PATCH 6/7] Update README --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de42400..28ff0fb 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,13 @@ flowchart TD - TerminalLayer - Crc8Layer (0xA6) - Crc16Layer (0xBAAD) +- Crc32Layer (reversed 0x04c11db7) - LoggingLayer using ILogger - LoopbackLayer - BufferLayer - SegmentationLayer - ArqLayer +- IdleCheckLayer ### Transport layers - ZeroMQ using [NetMQ](https://netmq.readthedocs.io/) @@ -170,12 +172,15 @@ python -m libstored.gui -p 5555 ## Compatibility -Tested with: -- [v1.7.1](https://github.com/DEMCON/libstored/releases/tag/v1.7.1) (see note below) +Tested with libstored versions: +- [v2.0.0](https://github.com/DEMCON/libstored/releases/tag/v2.0.0) - [v1.8.0](https://github.com/DEMCON/libstored/releases/tag/v1.8.0) +- [v1.7.1](https://github.com/DEMCON/libstored/releases/tag/v1.7.1) (see note below) See [libstored changelog](https://demcon.github.io/libstored/doc/changelog.html) for the changes. There do not seem to be breaking changes from v1.7.1 The ZeroMQ socket type changed from `PAIR` to `DEALER` for the SyncZeroMQLayer. Make sure the exact same socket type is used at both ends of the protocol, so `PAIR` - `PAIR` or `DEALER` - `DEALER`, but not `PAIR` - `DEALER`. When connecting to libstored v1.7.1 or lower, use the `PAIR` socket type. For version v1.8 or higher of libstored, use `DEALER`. From e5b66fa116414c23644c15274f4532c8cdd89aa6 Mon Sep 17 00:00:00 2001 From: guuskuiper Date: Mon, 1 Dec 2025 19:28:39 +0100 Subject: [PATCH 7/7] Add Sync readme --- examples/Sync/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/Sync/README.md diff --git a/examples/Sync/README.md b/examples/Sync/README.md new file mode 100644 index 0000000..583dc65 --- /dev/null +++ b/examples/Sync/README.md @@ -0,0 +1,21 @@ +# Sync Example + +This example demonstrates cross-language synchronization between C++ and C# using the [Aspire CLI](https://aspire.dev/docs/cli/). The setup is based on the [8_sync](https://github.com/DEMCON/libstored/tree/main/examples/8_sync) example from the libstored project. + +In this example, the C++ code runs inside a container, while the C# implementation runs natively. The `libstored.gui` Python library connects to both the C++ and C# components, allowing you to observe and interact with the synchronizated stores. + +## Prerequisites + +- [.NET 10 SDK](https://dotnet.microsoft.com/download) +- [Aspire CLI](https://aspire.dev/docs/cli/) +- [Podman](https://podman.io/) or [Docker](https://www.docker.com/products/docker-desktop) + +## Running the Example + +```sh +aspire run +``` + +## Learn More + +- [Aspire Documentation](https://aspire.dev/docs/) \ No newline at end of file