From 47b89ef7cff845eaee4bb0ea39ae8e14134f18d8 Mon Sep 17 00:00:00 2001 From: Josh <8677174+bijij@users.noreply.github.com> Date: Sun, 7 Jan 2024 03:47:29 +1000 Subject: [PATCH] Update .NET versions, improve implementation performance --- Bottom.NET.sln | 4 +- src/Bottom.Bench/Bottom.Bench.csproj | 6 +- src/Bottom.CLI/Bottom.CLI.csproj | 5 +- src/Bottom.CLI/Program.cs | 2 +- src/Bottom.UnitTest/Bottom.UnitTest.csproj | 13 +- src/Bottom.UnitTest/UnitTests.cs | 178 +++++++----------- src/Bottom/Bottom.cs | 209 +++++++++++---------- src/Bottom/Bottom.csproj | 8 +- 8 files changed, 200 insertions(+), 225 deletions(-) diff --git a/Bottom.NET.sln b/Bottom.NET.sln index 5ac52d9..9d0a524 100644 --- a/Bottom.NET.sln +++ b/Bottom.NET.sln @@ -1,7 +1,7 @@ ο»Ώ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30804.86 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bottom", "src\Bottom\Bottom.csproj", "{9B492510-BB86-4A94-B3E1-8E3FFCFAE282}" EndProject diff --git a/src/Bottom.Bench/Bottom.Bench.csproj b/src/Bottom.Bench/Bottom.Bench.csproj index 9da2d77..c21b710 100644 --- a/src/Bottom.Bench/Bottom.Bench.csproj +++ b/src/Bottom.Bench/Bottom.Bench.csproj @@ -1,12 +1,12 @@ - +ο»Ώ Exe - netcoreapp3.1 + net8.0 - + diff --git a/src/Bottom.CLI/Bottom.CLI.csproj b/src/Bottom.CLI/Bottom.CLI.csproj index a56343c..bb1b70b 100644 --- a/src/Bottom.CLI/Bottom.CLI.csproj +++ b/src/Bottom.CLI/Bottom.CLI.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net8.0 Bottom.CLI Bottom.CLI.Program 3.0.0.0 @@ -10,7 +10,8 @@ - + + diff --git a/src/Bottom.CLI/Program.cs b/src/Bottom.CLI/Program.cs index e39f2aa..f86365a 100644 --- a/src/Bottom.CLI/Program.cs +++ b/src/Bottom.CLI/Program.cs @@ -1,6 +1,6 @@ ο»Ώusing System; using System.CommandLine; -using System.CommandLine.Invocation; +using System.CommandLine.NamingConventionBinder; using System.IO; using System.Text; diff --git a/src/Bottom.UnitTest/Bottom.UnitTest.csproj b/src/Bottom.UnitTest/Bottom.UnitTest.csproj index 613e7ed..04c77bd 100644 --- a/src/Bottom.UnitTest/Bottom.UnitTest.csproj +++ b/src/Bottom.UnitTest/Bottom.UnitTest.csproj @@ -1,16 +1,19 @@ ο»Ώ - netcoreapp3.1 + net8.0 false - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Bottom.UnitTest/UnitTests.cs b/src/Bottom.UnitTest/UnitTests.cs index 9c388a8..d508cbd 100644 --- a/src/Bottom.UnitTest/UnitTests.cs +++ b/src/Bottom.UnitTest/UnitTests.cs @@ -6,125 +6,91 @@ namespace Bottom.UnitTest public class UnitTests { [TestMethod] - public void TestIsCharacterValueGroup() - { - Assert.AreEqual( - true, - Bottomify.IsCharacterValueGroup("πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ") - ); - Assert.AreEqual( - false, - Bottomify.IsCharacterValueGroup("πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B") - ); - Assert.AreEqual( - true, - Bottomify.IsCharacterValueGroup("hello") - ); - } + [DataRow(true, "πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ")] + [DataRow(true, "β€οΈπŸ‘‰πŸ‘ˆ")] + [DataRow(false, "πŸ’–βœ¨βœ¨βœ¨,,,,")] + [DataRow(false, "πŸ’–β€οΈπŸ‘‰πŸ‘ˆ")] + [DataRow(true, "πŸ’–βœ¨βœ¨βœ¨,,,,\u200B")] + [DataRow(false, "πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B")] + [DataRow(false, "πŸ‘‰πŸ‘ˆ")] + [DataRow(false, "hello")] + public void TestIsCharacterValueGroup(bool expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.IsCharacterValueGroup(input) + ); + [TestMethod] - public void TestIsEncoded() - { - Assert.AreEqual( - true, - Bottomify.IsEncoded("πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B") - ); - Assert.AreEqual( - false, - Bottomify.IsEncoded("Hello") - ); - } + [DataRow(true, "πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B")] + [DataRow(false, "πŸ’–βœ¨βœ¨βœ¨,,,,")] + [DataRow(false, "Hello")] + [DataRow(true, "")] + public void TestIsEncoded(bool expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.IsEncoded(input) + ); + [TestMethod] - public void TestStringEncode() - { - Assert.AreEqual( - "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", - Bottomify.EncodeString("Test") - ); - Assert.AreEqual( - "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆβ€οΈπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", - Bottomify.EncodeString("Te\0st") - ); - } + [DataRow("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", "Test")] + [DataRow("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆβ€οΈπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", "Te\0st")] + public void TestStringEncode(string expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.EncodeString(input) + ); + [TestMethod] - public void TestByteEncode() - { - Assert.AreEqual( - "πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ", - Bottomify.EncodeByte((byte)'h') - ); - Assert.AreEqual( - "β€οΈπŸ‘‰πŸ‘ˆ", - Bottomify.EncodeByte((byte)'\0') - ); - } + [DataRow("πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ", (byte)'h')] + [DataRow("πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ", (byte)'a')] + [DataRow("β€οΈπŸ‘‰πŸ‘ˆ", (byte)'\0')] + public void TestByteEncode(string expectedResult, byte input) => Assert.AreEqual( + expectedResult, + Bottomify.EncodeByte(input) + ); + [TestMethod] - public void TestCharacterValueGroupDecode() - { - Assert.AreEqual( - (byte)'h', - Bottomify.DecodeCharacterValueGroup("πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ") - ); - Assert.AreEqual( - (byte)'a', - Bottomify.DecodeCharacterValueGroup("πŸ’–βœ¨βœ¨βœ¨βœ¨,,,,,,,πŸ‘‰πŸ‘ˆ") - ); - Assert.AreEqual( - (byte)'\0', - Bottomify.DecodeCharacterValueGroup("β€οΈπŸ‘‰πŸ‘ˆ") - ); - } + [DataRow((byte)'h', "πŸ’–πŸ’–,,,,πŸ‘‰πŸ‘ˆ")] + [DataRow((byte)'a', "πŸ’–βœ¨βœ¨βœ¨βœ¨,,,,,,,πŸ‘‰πŸ‘ˆ")] + [DataRow((byte)'a', "πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ")] + [DataRow((byte)'\0', "β€οΈπŸ‘‰πŸ‘ˆ")] + public void TestCharacterValueGroupDecode(byte expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.DecodeCharacterValueGroup(input) + ); + [TestMethod] - public void TestStringDecode() - { - Assert.AreEqual( - "Test", - Bottomify.DecodeString("πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B") - ); - Assert.AreEqual( - "Test", - Bottomify.DecodeString("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ") - ); - Assert.AreEqual( - "Te\0st", - Bottomify.DecodeString("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆβ€οΈπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ") - ); - } + [DataRow("Test", "πŸ’–βœ¨βœ¨βœ¨,,,,\u200BπŸ’–πŸ’–,\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί\u200BπŸ’–πŸ’–βœ¨πŸ₯Ί,\u200B")] + [DataRow("Test", "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ")] + [DataRow("Te\0st", "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆβ€οΈπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ")] + public void TestStringDecode(string expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.DecodeString(input) + ); + [TestMethod] - public void TestUnicodeStringEncode() - { - Assert.AreEqual( - "πŸ«‚βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", - Bottomify.EncodeString("πŸ₯Ί") - ); - Assert.AreEqual( - "πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ" + - "πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆ" + - "πŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ", - Bottomify.EncodeString("γŒγ‚“γ°γ‚Œ") - ); - } + [DataRow("πŸ«‚βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ", "πŸ₯Ί")] + [DataRow("πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ" + + "πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆ" + + "πŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ", "γŒγ‚“γ°γ‚Œ")] + public void TestUnicodeStringEncode(string expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.EncodeString(input) + ); + [TestMethod] - public void TestUnicodeStringDecode() - { - Assert.AreEqual( - "πŸ₯Ί", - Bottomify.DecodeString("πŸ«‚βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ") - ); - Assert.AreEqual( - "γŒγ‚“γ°γ‚Œ", - Bottomify.DecodeString( - "πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ" + - "πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆ" + - "πŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ" - ) - ); - } + [DataRow("πŸ₯Ί", "πŸ«‚βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ")] + [DataRow("γŒγ‚“γ°γ‚Œ", "πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆ" + + "πŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆ" + + "πŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ")] + + public void TestUnicodeStringDecode(string expectedResult, string input) => Assert.AreEqual( + expectedResult, + Bottomify.DecodeString(input) + ); } } diff --git a/src/Bottom/Bottom.cs b/src/Bottom/Bottom.cs index 954fb40..8073f8e 100644 --- a/src/Bottom/Bottom.cs +++ b/src/Bottom/Bottom.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Text.RegularExpressions; namespace Bottom { @@ -10,30 +9,27 @@ public static class Bottomify { #region Private Attributes - private const string LINE_ENDING = "πŸ‘‰πŸ‘ˆ"; + private const string LEGACY_BYTE_TERMINATOR = "\u200B"; + private const string BYTE_TERMINATOR = "πŸ‘‰πŸ‘ˆ"; + private const string NULL_VALUE = "❀️"; - private static readonly Dictionary _character_values = new Dictionary() + private static readonly Dictionary _characterValues = new Dictionary() { {200, "πŸ«‚"}, {50, "πŸ’–"}, {10, "✨"}, {5, "πŸ₯Ί"}, {1, ","}, - {0, "❀️"} + {0, NULL_VALUE} }; - private static readonly Dictionary _character_values_reversed = new Dictionary() - { - {"πŸ«‚", 200}, - {"πŸ’–", 50}, - {"✨", 10}, - {"πŸ₯Ί", 5}, - {",", 1}, - {"❀️", 0} - }; + private static readonly Dictionary _characterValuesReversed = + _characterValues.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - private static readonly string[] _byte_to_stripped_character_value_group = MapByteToStrippedCharacterValueGroup(); - private static readonly Dictionary _stripped_character_value_group_to_byte = MapStrippedCharacterValueGroupToByte(); + private static readonly string[] _byteToStrippedCharacterValueGroup = + Enumerable.Range(byte.MinValue, byte.MaxValue) + .Select(i => ByteToStrippedCharacterValueGroup((byte)i)) + .ToArray(); #endregion @@ -44,20 +40,18 @@ public static class Bottomify /// /// The string to validate. /// True if the input string is a Bottom chracter value group, otherwise false. - public static bool IsCharacterValueGroup(string input) - { - return IsStrippedCharacterValueGroup(StripCharacterValueGroup(input)); - } + public static bool IsCharacterValueGroup(string input) => + TryDecodeCharacterValueGroup(input, out _); + /// /// Encode a Byte in a Bottom character value group. /// /// The byte to encode. /// The encoded Bottom character value group. - public static string EncodeByte(byte value) - { - return _byte_to_stripped_character_value_group[value] + LINE_ENDING; - } + public static string EncodeByte(byte value) => + _byteToStrippedCharacterValueGroup[value] + BYTE_TERMINATOR; + /// /// Decode a Bottom character value group into a byte. @@ -67,28 +61,55 @@ public static string EncodeByte(byte value) /// The decoded byte. public static byte DecodeCharacterValueGroup(string input) { - return DecodeStrippedCharacterValueGroup(StripCharacterValueGroup(input)); + try + { + return DecodeStrippedCharacterValueGroup(GetStrippedCharacterValueGroups(input).Single()); + } + catch (Exception e) when (e is InvalidOperationException || e is KeyNotFoundException) + { + throw new KeyNotFoundException($"Cannot decode value character \"{input}\"."); + } + } + + + /// + /// Try to decode a Bottom character value group into a byte. + /// + /// The Bottom character value group to decode. + /// The decoded byte or 0 if the input string could not be decoded. + /// True if the input string was a valid Bottom character value group, otherwise false. + public static bool TryDecodeCharacterValueGroup(string input, out byte value) + { + try + { + value = DecodeCharacterValueGroup(input); + return true; + } + catch (KeyNotFoundException) + { + value = 0; + return false; + } } + /// /// Determines whether a string is Bottom encoded. /// /// The string to validate. /// True if the input string is Bottom encoded, otherwise false. - public static bool IsEncoded(string input) - { - return GetStrippedCharacterValueGroups(input).All(s => IsStrippedCharacterValueGroup(s)); - } + public static bool IsEncoded(string input) => + TryDecodeString(input, out _); + /// /// Encode a string in Bottom. /// /// The string to encode. /// The Bottom encoded string. - public static string EncodeString(string input) - { - return string.Join("", Encoding.UTF8.GetBytes(input).Select(EncodeByte)); - } + public static string EncodeString(string input) => + string.Join("", Encoding.UTF8.GetBytes(input).Select(EncodeByte)); + /// /// Decode a Bottom encoded string. @@ -96,45 +117,64 @@ public static string EncodeString(string input) /// The Bottom encoded string to decode. /// The input string contained an invalid Bottom character value group. /// The decoded string. - public static string DecodeString(string input) + public static string DecodeString(string input) => + Encoding.UTF8.GetString(GetStrippedCharacterValueGroups(input) + .Select(DecodeStrippedCharacterValueGroup) + .ToArray()); + + + /// + /// Try to decode a Bottom encoded string. + /// + /// The Bottom encoded string to decode. + /// The decoded string or null if the input string could not be decoded. + /// Whether the input string was a valid Bottom encoded string. + public static bool TryDecodeString(string input, out string output) { - return Encoding.UTF8.GetString(GetStrippedCharacterValueGroups(input).Select(DecodeStrippedCharacterValueGroup).ToArray()); + try + { + output = DecodeString(input); + return true; + } + catch (KeyNotFoundException) + { + output = null; + return false; + } } #endregion #region Private Methods - - private static bool IsStrippedCharacterValueGroup(string input) - { - return GetCodepoints(input).All(s => _character_values_reversed.ContainsKey(s)); - } private static byte DecodeStrippedCharacterValueGroup(string input) { - if (_stripped_character_value_group_to_byte.ContainsKey(input)) - { - return _stripped_character_value_group_to_byte[input]; - } - else if (IsStrippedCharacterValueGroup(input)) + if (string.Equals(input, NULL_VALUE)) { - return StrippedCharacterValueGroupToByte(input); + return 0; } - throw new KeyNotFoundException($"Cannot decode value character \"{input}\"."); + + return StrippedCharacterValueGroupToByte(input); } + private static string ByteToStrippedCharacterValueGroup(byte value) { - StringBuilder buffer = new StringBuilder(); + if (value == 0) + { + return NULL_VALUE; + } + + var buffer = new StringBuilder(); do { - foreach (KeyValuePair mapping in _character_values) + foreach (var kvp in _characterValues) { - if (value >= mapping.Key) + if (value >= kvp.Key) { - buffer.Append(mapping.Value); - value -= mapping.Key; + buffer.Append(kvp.Value); + value -= kvp.Key; break; } } @@ -143,74 +183,39 @@ private static string ByteToStrippedCharacterValueGroup(byte value) return buffer.ToString(); } - private static byte StrippedCharacterValueGroupToByte(string input) - { - byte value = 0; - - foreach (string character_value in GetCodepoints(input)) - { - value += _stripped_character_value_group_to_byte[character_value]; - } - - _stripped_character_value_group_to_byte[input] = value; - return value; - } - private static IEnumerable GetStrippedCharacterValueGroups(string input) { - return input.Split(new string[] { "\u200B", LINE_ENDING }, StringSplitOptions.RemoveEmptyEntries); - } + input = input.Replace(LEGACY_BYTE_TERMINATOR, BYTE_TERMINATOR); - private static IEnumerable GetCodepoints(string input) - { - for (int i = 0; i < input.Length; ++i) + while (!string.IsNullOrEmpty(input)) { - yield return char.ConvertFromUtf32(char.ConvertToUtf32(input, i)); - if (char.IsHighSurrogate(input, i)) + var byteEndIndex = input.IndexOf(BYTE_TERMINATOR); + + if (byteEndIndex < 1) { - i++; + throw new KeyNotFoundException($"Cannot decode input \"{input}\"."); } - } - } - private static string StripCharacterValueGroup(string input) - { - return Regex.Match(input, @"(.*)(?=\u200B|(?=πŸ‘‰πŸ‘ˆ))").Value; + yield return input.Substring(0, byteEndIndex); + input = input.Substring(byteEndIndex + BYTE_TERMINATOR.Length); + } } - #region Initialiser Methods - - private static string[] MapByteToStrippedCharacterValueGroup() - { - string[] mapping = new string[256]; - byte i = 0; - do - { - mapping[i] = ByteToStrippedCharacterValueGroup(i); - i++; - } while (i != 0); + private static byte StrippedCharacterValueGroupToByte(string input) => + (byte)GetCodepoints(input).Select(s => (int)_characterValuesReversed[s]).Sum(); - return mapping; - } - private static Dictionary MapStrippedCharacterValueGroupToByte() + private static IEnumerable GetCodepoints(string input) { - Dictionary mapping = new Dictionary(); - - byte i = 0; - do + while (!string.IsNullOrEmpty(input)) { - mapping[_byte_to_stripped_character_value_group[i]] = i; - i++; - } while (i != 0); - - return mapping; + yield return char.ConvertFromUtf32(char.ConvertToUtf32(input, 0)); + input = input.Substring(char.IsHighSurrogate(input, 0) ? 2 : 1); + } } #endregion - #endregion - } } diff --git a/src/Bottom/Bottom.csproj b/src/Bottom/Bottom.csproj index 88a80e7..242f84a 100644 --- a/src/Bottom/Bottom.csproj +++ b/src/Bottom/Bottom.csproj @@ -1,17 +1,17 @@ - netcoreapp3.1 + netstandard2.0 Bottom Bottom - 3.0.0 + 3.1.0 Bottom.NET Bottom Software Foundation Bottom.NET Bottom encoding utility functions for .NET - 2021 + 2024 MIT - 3.0.0.0 + 3.1.0.0 https://github.com/bottom-software-foundation/bottom-dotnet/ logo.png https://github.com/bottom-software-foundation/bottom-dotnet/