diff --git a/HexDump.Benchmark/FormatBenchmark.cs b/HexDump.Benchmark/FormatBenchmark.cs new file mode 100644 index 0000000..deb940e --- /dev/null +++ b/HexDump.Benchmark/FormatBenchmark.cs @@ -0,0 +1,72 @@ + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace HexDump.Benchmark +{ + // [ClrJob, CoreJob, MonoJob] + //[SimpleJob(RuntimeMoniker.Net472, baseline: true)] + //[SimpleJob(RuntimeMoniker.NetCoreApp30)] + [SimpleJob(RuntimeMoniker.NetCoreApp31)] + //[SimpleJob(RuntimeMoniker.CoreRt30)] + //[SimpleJob(RuntimeMoniker.Mono)] + [RPlotExporter] + [MemoryDiagnoser] + [ShortRunJob] + public class FormatBenchmark + { + private string _dump; + + [Params(10, 100, 1000)] + public int _len; + + [GlobalSetup] + public void Setup() + { + _dump = HexDump.Format(new byte[_len]); + } + + [Benchmark] + public void ParseSimd() + { + HexDump.ParseSimd(_dump); + } + + [Benchmark] + public void ParseLookup1() + { + HexDump.ParseLookup1(_dump); + } + + [Benchmark] + public void ParseLookup2() + { + HexDump.ParseLookup2(_dump); + } + + [Benchmark] + public void ParseStasteMachine() + { + HexDump.ParseStasteMachine(_dump); + } + + [Benchmark] + public void ParseConvert() + { + HexDump.ParseConvert(_dump); + } + + [Benchmark] + public void ParseDic() + { + HexDump.ParseDic(_dump); + } +/** too slow to continue measures + [Benchmark] + public void ParseRegex() + { + HexDump.ParseRegex(_dump); + } +**/ + } +} \ No newline at end of file diff --git a/HexDump.Benchmark/HexDump.Benchmark.csproj b/HexDump.Benchmark/HexDump.Benchmark.csproj new file mode 100644 index 0000000..5d43650 --- /dev/null +++ b/HexDump.Benchmark/HexDump.Benchmark.csproj @@ -0,0 +1,20 @@ + + + + + false + + Exe + + netcoreapp3.1 + + + + + + + + + + + diff --git a/HexDump.Benchmark/Program.cs b/HexDump.Benchmark/Program.cs new file mode 100644 index 0000000..f80a49b --- /dev/null +++ b/HexDump.Benchmark/Program.cs @@ -0,0 +1,12 @@ +using BenchmarkDotNet.Running; + +namespace HexDump.Benchmark +{ + public class Program + { + public static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(); + } + } +} \ No newline at end of file diff --git a/HexDump.Tests/AnalyseTests.cs b/HexDump.Tests/AnalyseTests.cs new file mode 100644 index 0000000..0fbd36b --- /dev/null +++ b/HexDump.Tests/AnalyseTests.cs @@ -0,0 +1,187 @@ +using System; +using System.Linq; +using Xunit; + +namespace HexDump.Tests +{ + public class AnalyseTests + { + + + [Fact] + public void When_Ascii_In_Ascii_Dont_Match() + { + var data = @" +0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(8, config.Offset); + Assert.Equal(48, config.Hex); + Assert.Equal(21, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + [Fact] + public void When_NoOffset_NoAscii() + { + var data = @" +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(48, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + + [Fact] + public void When_NoOffset_NoAscii_OneLine() + { + var data = @" +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(48, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + [Fact] + public void When_NoOffset_NoAscii_23_OneLine() + { + var data = @" +9A 20 31 32 20 9A 9A 9A +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(23, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + + + [Fact] + public void When_NoOffset_Ascii_In_Ascii_Dont_Match() + { + var data = @" +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(48, config.Hex); + Assert.Equal(21, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + [Fact] + public void When_NoOffset_Ascii_In_Ascii_OneLine_Match() + { + var data = @" +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(48, config.Hex); + Assert.Equal(21, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + [Fact] + public void When_NoOffset_Ascii_2_In_Ascii_OneLine_Match() + { + var data = @" +9A +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(2, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + [Fact] + public void When_NoOffset_Ascii_4_In_Ascii_OneLine_Match() + { + var data = @" +9A 9A +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(0, config.Offset); + Assert.Equal(5, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + + [Fact] + public void When_56a_Columns1_Width8_Ascii0_Offset1_Then_ok() + { + + var data = @" +0000 61 61 61 61 61 61 61 61 +0008 61 61 61 61 61 61 61 61 +0010 61 61 61 61 61 61 61 61 +0018 61 61 61 61 61 61 61 61 +0020 61 61 61 61 61 61 61 61 +0028 61 61 61 61 61 61 61 61 +0030 61 61 61 61 61 61 61 61 +".Trim(); + + var config = HexDump.Analyse(data.AsSpan()); + + Assert.Equal(8, config.Offset); + Assert.Equal(23, config.Hex); + Assert.Equal(0, config.Ascii); + Assert.Equal(1, config.NewLine); + } + + + [Fact] + public void When_56a_Columns1_Width8_Ascii1_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 3; + int width = 4; + bool offset = true; + bool ascii = true; + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 aaaa aaaa aaaa +000C 61 61 61 61 61 61 61 61 61 61 61 61 aaaa aaaa aaaa +0018 61 61 61 61 61 61 61 61 61 61 61 61 aaaa aaaa aaaa +0024 61 61 61 61 61 61 61 61 61 61 61 61 aaaa aaaa aaaa +0030 61 61 61 61 61 61 61 61 aaaa aaaa ".TrimStart(); + var result = HexDump.Format(data, width, columns, offset, ascii); + Assert.Equal(expected, result); + + var config = HexDump.Analyse(result.AsSpan()); + Assert.Equal(8, config.Offset); + Assert.Equal(37, config.Hex); + Assert.Equal(18, config.Ascii); + //Assert.Equal(1, config.NewLine); + } + } +} diff --git a/HexDump.Tests/FormatColumnsAsciiOffsetTests.cs b/HexDump.Tests/FormatColumnsAsciiOffsetTests.cs new file mode 100644 index 0000000..64b1935 --- /dev/null +++ b/HexDump.Tests/FormatColumnsAsciiOffsetTests.cs @@ -0,0 +1,307 @@ +using System.Linq; +using Xunit; + +namespace HexDump.Tests +{ + public class FormatColumnsAsciiOffsetTests + { + + + [Fact] + public void When_Encode_56a_Columns1_Width8_Ascii1_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 1; + int width = 8; + bool offset = true; + bool ascii = true; + + var expected = @" +0000 61 61 61 61 61 61 61 61 aaaaaaaa +0008 61 61 61 61 61 61 61 61 aaaaaaaa +0010 61 61 61 61 61 61 61 61 aaaaaaaa +0018 61 61 61 61 61 61 61 61 aaaaaaaa +0020 61 61 61 61 61 61 61 61 aaaaaaaa +0028 61 61 61 61 61 61 61 61 aaaaaaaa +0030 61 61 61 61 61 61 61 61 aaaaaaaa +".Trim(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns1_Width8_Ascii1_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 1; + int width = 8; + bool offset = false; + bool ascii = true; + + var expected = @" +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa +".Trim(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + [Fact] + public void When_Encode_56a_Columns1_Width8_Ascii0_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 1; + int width = 8; + bool offset = true; + bool ascii = false; + + var expected = @" +0000 61 61 61 61 61 61 61 61 +0008 61 61 61 61 61 61 61 61 +0010 61 61 61 61 61 61 61 61 +0018 61 61 61 61 61 61 61 61 +0020 61 61 61 61 61 61 61 61 +0028 61 61 61 61 61 61 61 61 +0030 61 61 61 61 61 61 61 61 +".Trim(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns1_Width8_Ascii0_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 1; + int width = 8; + bool offset = false; + bool ascii = false; + + var expected = @" +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 +".Trim(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + [Fact] + public void When_Encode_56a_Columns2_Width8_Ascii1_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 2; + int width = 8; + bool offset = true; + bool ascii = true; + + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +0010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +0020 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +0030 61 61 61 61 61 61 61 61 aaaaaaaa ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns2_Width8_Ascii1_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 2; + int width = 8; + bool offset = false; + bool ascii = true; + + var expected = @" +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + [Fact] + public void When_Encode_56a_Columns2_Width8_Ascii0_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 2; + int width = 8; + bool offset = true; + bool ascii = false; + + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +0010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +0020 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +0030 61 61 61 61 61 61 61 61 ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns2_Width8_Ascii0_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 2; + int width = 8; + bool offset = false; + bool ascii = false; + + var expected = @" +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns3_Width8_Ascii1_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 3; + int width = 8; + bool offset = true; + bool ascii = true; + + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa aaaaaaaa +0018 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa aaaaaaaa +0030 61 61 61 61 61 61 61 61 aaaaaaaa ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns3_Width8_Ascii1_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 3; + int width = 8; + bool offset = false; + bool ascii = true; + + var expected = @" +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa aaaaaaaa +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa aaaaaaaa +61 61 61 61 61 61 61 61 aaaaaaaa ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns3_Width8_Ascii0_Offset1_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 3; + int width = 8; + bool offset = true; + bool ascii = false; + + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +0018 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +0030 61 61 61 61 61 61 61 61 ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_56a_Columns3_Width8_Ascii0_Offset0_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 56).ToArray(); + int columns = 3; + int width = 8; + bool offset = false; + bool ascii = false; + + var expected = @" +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 +61 61 61 61 61 61 61 61 ".TrimStart(); + + var result = HexDump.Format(data, width, columns, offset, ascii); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed, width, columns, offset, ascii); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + } + +} diff --git a/HexDump.Tests/FormatSpecialCasesTests.cs b/HexDump.Tests/FormatSpecialCasesTests.cs new file mode 100644 index 0000000..9d5e4cd --- /dev/null +++ b/HexDump.Tests/FormatSpecialCasesTests.cs @@ -0,0 +1,104 @@ +using System.Linq; +using Xunit; + +namespace HexDump.Tests +{ + public class FormatSpecialCasesTests + { + + + [Fact] + public void When_Ascii_In_Ascii_Dont_Match() + { + var data = @" +0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +0010 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +0020 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed); + + Assert.Equal(data, result2); + } + + [Fact] + public void When_NoOffest_Work() + { + var data = @" +9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed, includeOffset: false); + + Assert.Equal(data, result2); + } + + [Fact] + public void When_NoAscii_Work() + { + var data = @" +0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed, includeAscii: false); + + Assert.Equal(data, result2); + } + + + [Fact] + public void When_NoAscii_NoOffset_Work() + { + var data = @" +9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed, includeOffset: false, includeAscii: false); + + Assert.Equal(data, result2); + } + + [Fact] + public void When_OneByte_NoAscii_NoOffset_Work() + { + var data = @" +9A +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed, + includeOffset: false, + includeAscii: false, + columnWidth: 1, + columnCount: 1 + ); + + Assert.Equal(data, result2); + } + + + [Fact] + public void When_TwoBytes_NoAscii_NoOffset_Work() + { + var data = @" +9A +9A +".Trim(); + + var parsed = HexDump.Parse(data); + var result2 = HexDump.Format(parsed, + includeOffset: false, + includeAscii: false, + columnWidth: 1, + columnCount: 1 + ); + + Assert.Equal(data, result2); + } + + } +} diff --git a/HexDump.Tests/FormatTests.cs b/HexDump.Tests/FormatTests.cs new file mode 100644 index 0000000..1d6f486 --- /dev/null +++ b/HexDump.Tests/FormatTests.cs @@ -0,0 +1,240 @@ +using System.Linq; +using Xunit; + +namespace HexDump.Tests +{ + public class FormatTests + { + + [Fact] + public void When_Encode_16_Then_ok() + { + var data = new byte[16]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +".Trim(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + [Fact] + public void When_Encode_32_Then_ok() + { + var data = new byte[32]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +".Trim();; + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + + [Fact] + public void When_Encode_48_Then_ok() + { + var data = new byte[48]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +".Trim(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + + [Fact] + public void When_Encode_256_Then_ok() + { + var data = new byte[256]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +".Trim(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + + [Fact] + public void When_Encode_8_Then_ok() + { + var data = new byte[8]; + var expected = @" +0000 00 00 00 00 00 00 00 00 ........ ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + [Fact] + public void When_Encode_0_Then_ok() + { + var data = new byte[0]; + var expected = @""; + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + + [Fact] + public void When_Encode_24_Then_ok() + { + var data = new byte[24]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0010 00 00 00 00 00 00 00 00 ........ ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + [Fact] + public void When_Encode_40_Then_ok() + { + var data = new byte[40]; + var expected = @" +0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ +0020 00 00 00 00 00 00 00 00 ........ ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + [Fact] + public void When_Encode_40_one_Then_ok() + { + var data = Enumerable.Repeat((byte) 1, 40).ToArray(); + + var expected = @" +0000 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ........ ........ +0010 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ........ ........ +0020 01 01 01 01 01 01 01 01 ........ ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + + [Fact] + public void When_Encode_100_ff_Then_ok() + { + var data = Enumerable.Repeat((byte) 0xff, 100).ToArray(); + + var expected = @" +0000 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0010 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0020 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0030 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0040 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0050 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿ +0060 FF FF FF FF ÿÿÿÿ ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + + [Fact] + public void When_Encode_128_9a_Then_ok() + { + var data = Enumerable.Repeat((byte) 0x9a, 128).ToArray(); + + var expected = @" +0000 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0010 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0020 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0030 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0040 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0050 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0060 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +0070 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A ........ ........ +".Trim(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result); + Assert.Equal(expected, result2); + } + + [Fact] + public void When_Encode_40_61_Then_ok() + { + var data = Enumerable.Repeat((byte) 'a', 40).ToArray(); + + var expected = @" +0000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +0010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaa aaaaaaaa +0020 61 61 61 61 61 61 61 61 aaaaaaaa ".TrimStart(); + + var result = HexDump.Format(data); + var parsed = HexDump.Parse(result); + var result2 = HexDump.Format(parsed); + + Assert.Equal(expected, result2); + Assert.Equal(expected, result); + } + } +} diff --git a/HexDump.Tests/HexDump.Tests.csproj b/HexDump.Tests/HexDump.Tests.csproj index 75588cd..30367d6 100644 --- a/HexDump.Tests/HexDump.Tests.csproj +++ b/HexDump.Tests/HexDump.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1;net45 + netcoreapp3.1;net451 false diff --git a/HexDump.sln b/HexDump.sln index a68ca9d..54faceb 100644 --- a/HexDump.sln +++ b/HexDump.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexDump", "HexDump\HexDump. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexDump.Tests", "HexDump.Tests\HexDump.Tests.csproj", "{8F8380EE-289F-4AE6-A44B-720269DFA071}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexDump.Benchmark", "HexDump.Benchmark\HexDump.Benchmark.csproj", "{DF88229C-C00A-4EC0-8C43-F6731F10E75A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {8F8380EE-289F-4AE6-A44B-720269DFA071}.Debug|Any CPU.Build.0 = Debug|Any CPU {8F8380EE-289F-4AE6-A44B-720269DFA071}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F8380EE-289F-4AE6-A44B-720269DFA071}.Release|Any CPU.Build.0 = Release|Any CPU + {DF88229C-C00A-4EC0-8C43-F6731F10E75A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF88229C-C00A-4EC0-8C43-F6731F10E75A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF88229C-C00A-4EC0-8C43-F6731F10E75A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF88229C-C00A-4EC0-8C43-F6731F10E75A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HexDump/HexDump.cs b/HexDump/HexDump.cs index c2fc092..0f96efc 100644 --- a/HexDump/HexDump.cs +++ b/HexDump/HexDump.cs @@ -6,7 +6,7 @@ namespace HexDump { - public static class HexDump + public static partial class HexDump { [Pure] public static string Format(byte[] bytes, int columnWidth = 8, int columnCount = 2, bool includeOffset = true, bool includeAscii = true) diff --git a/HexDump/HexDump.csproj b/HexDump/HexDump.csproj index f0890bb..f4f3c61 100644 --- a/HexDump/HexDump.csproj +++ b/HexDump/HexDump.csproj @@ -1,7 +1,7 @@ - netstandard2.0;net45 + netstandard2.1;net45 true portable 8 @@ -22,4 +22,10 @@ snupkg + + + + + + diff --git a/HexDump/HexDumpParse.cs b/HexDump/HexDumpParse.cs new file mode 100644 index 0000000..1c062ff --- /dev/null +++ b/HexDump/HexDumpParse.cs @@ -0,0 +1,19 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + + public static byte[] Parse(string dump) + { + return ParseStasteMachine(dump); + } + + } +} diff --git a/HexDump/HexDumpParseConvert.cs b/HexDump/HexDumpParseConvert.cs new file mode 100644 index 0000000..3266e06 --- /dev/null +++ b/HexDump/HexDumpParseConvert.cs @@ -0,0 +1,46 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace HexDump +{ + public static partial class HexDump + { + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseConvert(string dump) + { + + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + var result = new List(); + var span = dump.AsSpan(); + for (int i = 0; i < span.Length - 3; i++) + { + + if (span[i] == ' ' && span[i+3] == ' ' + && ((span[i+1] >= '0' && span[i+1] <= '9') + || (span[i+1] >= 'a' && span[i+1] <= 'f') + || (span[i+1] >= 'A' && span[i+1] <= 'F') + || (span[i+2] >= '0' && span[i+2] <= '9') + || (span[i+2] >= 'a' && span[i+2] <= 'f') + || (span[i+2] >= 'A' && span[i+2] <= 'F') + ) + ) + { + var s = span.Slice(i + 1, 2).ToString(); + byte b = Convert.ToByte(s, 16); + result.Add(b); + } + } + + return result.ToArray(); + } + + } +} diff --git a/HexDump/HexDumpParseDic.cs b/HexDump/HexDumpParseDic.cs new file mode 100644 index 0000000..082f454 --- /dev/null +++ b/HexDump/HexDumpParseDic.cs @@ -0,0 +1,64 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + static readonly Dictionary _dic =new Dictionary(); + + static HexDump() + { + var keys = "0123456789ABCEDF"; + for (int i = 0; i < keys.Length; i++) + { + _dic[keys[i]] = i; + if (i > 10) + { + _dic[(char)(keys[i]+0x20)] = i; + } + } + + } + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseDic(string dump) + { + + + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + var result = new List(); + var span = dump.AsSpan(); + for (int i = 0; i < span.Length - 3; i++) + { + + if (span[i] == ' ' && span[i+3] == ' ' + && ((span[i+1] >= '0' && span[i+1] <= '9') + || (span[i+1] >= 'a' && span[i+1] <= 'f') + || (span[i+1] >= 'A' && span[i+1] <= 'F') + || (span[i+2] >= '0' && span[i+2] <= '9') + || (span[i+2] >= 'a' && span[i+2] <= 'f') + || (span[i+2] >= 'A' && span[i+2] <= 'F') + ) + ) + { + + int dec = (_dic[span[i + 1]] * 16 + _dic[span[i + 2]]); + result.Add((byte)dec); + } + } + + return result.ToArray(); + } + + } +} diff --git a/HexDump/HexDumpParseLookup1.cs b/HexDump/HexDumpParseLookup1.cs new file mode 100644 index 0000000..79a6ae8 --- /dev/null +++ b/HexDump/HexDumpParseLookup1.cs @@ -0,0 +1,67 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + private static readonly byte[] _lookup = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseLookup1(string dump) + { + + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + var result = new List(); + var span = dump.AsSpan(); + for (int i = 0; i < span.Length - 3; i++) + { + + if ( span[i] == ' ' + && span[i+3] == ' ' + && _lookup[span[i + 1]] != 0xff + && _lookup[span[i + 2]] != 0xff + ) + { + int dec = ( + _lookup[span[i + 1]] * 16 + + _lookup[span[i + 2]] + ); + + result.Add((byte)dec); + } + } + + return result.ToArray(); + } + + } +} diff --git a/HexDump/HexDumpParseLookup2.cs b/HexDump/HexDumpParseLookup2.cs new file mode 100644 index 0000000..fba15d1 --- /dev/null +++ b/HexDump/HexDumpParseLookup2.cs @@ -0,0 +1,164 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + + private static readonly byte[] _lookup1 = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + private static readonly byte[] _lookup2 = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + + private static readonly byte[] _sep = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseLookup2(string dump) + { + + //0000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //0000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ... ........ + + var result = new List(); + var span = dump.AsSpan(); + var len = span.Length; + bool ascii = false; + for (int i = 0; i < len - 3; i++) + { + if ( + i == 0 + && _sep[span[i+2]] == 0xFF + && _lookup1[span[i + 1]] != 0xff + && _lookup2[span[i + 2]] != 0xff + ) + { + byte dec = (byte)( + _lookup1[span[i + 0]] + + _lookup2[span[i + 1]] + ); + + result.Add(dec); + } + + + // First char without offset (i==0) + if ( + i == 0 + && _sep[span[i+2]] == 0xFF + && _lookup1[span[i + 0]] != 0xff + && _lookup2[span[i + 1]] != 0xff + ) + { + byte dec = (byte)( + _lookup1[span[i + 0]] + + _lookup2[span[i + 1]] + ); + + result.Add(dec); + } + + + // last char without ascii (i + 3 ==length) + else if ( + _sep[span[i+1]] == 0xFF + && i + 4 == len + && _lookup1[span[i + 2]] != 0xff + && _lookup2[span[i + 3]] != 0xff + ) + { + byte dec = (byte)( + _lookup1[span[i + 2]] + + _lookup2[span[i + 3]] + ); + + result.Add(dec); + } + + + else if ( + _sep[span[i]] == 0xFF + && _sep[span[i+3]] == 0xFF + && _lookup1[span[i + 1]] != 0xff + && _lookup2[span[i + 2]] != 0xff + ) + { + byte dec = (byte)( + _lookup1[span[i + 1]] + + _lookup2[span[i + 2]] + ); + + result.Add(dec); + } + + + } + + return result.ToArray(); + } + + } +} diff --git a/HexDump/HexDumpParseRegex.cs b/HexDump/HexDumpParseRegex.cs new file mode 100644 index 0000000..4ec896e --- /dev/null +++ b/HexDump/HexDumpParseRegex.cs @@ -0,0 +1,68 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace HexDump +{ + public static partial class HexDump + { + /// + /// Parse HexDump into a byte array + /// + /// + /// + /// + /// + /// + /// + public static byte[] ParseRegex2(string dump, int columnWidth = 8, int columnCount = 2, bool includeOffset = true, bool includeAscii = true, bool compileRegex = true) + { + + string rio = includeOffset ? "((?[0-9a-f]+)\\s+)" : ""; + string ria = includeAscii ? "(?.+)" : ""; + + var options = RegexOptions.IgnoreCase; + options = compileRegex + ? options | RegexOptions.Compiled + : options; + + int hexaWidth = (columnWidth * 3 * columnCount) + columnCount - 1 - 1; + var _re = new Regex($"^{rio}(?[0-9a-f\\s]{{{hexaWidth}}}){ria}$", + options); + + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + string line; + var result = new List(); + using (var sr = new StringReader(dump)) + { + while ((line = sr.ReadLine()) != null) + { + var capture = _re.Match(line); + if (!capture.Success) continue; + var hexa = capture.Groups["hexa"] + .Value + .Replace(" ", "") + ; + + var bytes = Enumerable.Range(0, hexa.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hexa.Substring(x, 2), 16)) + ; + + result.AddRange(bytes); + } + } + + return result.ToArray(); + } + + } +} diff --git a/HexDump/HexDumpParseSimd.cs b/HexDump/HexDumpParseSimd.cs new file mode 100644 index 0000000..714f0bc --- /dev/null +++ b/HexDump/HexDumpParseSimd.cs @@ -0,0 +1,86 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + private static readonly byte[] _lookupSimd = new byte[] + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseSimd(string dump) + { + + + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //00000000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + var lb = new List(); + var hb = new List(); + var span = dump.AsSpan(); + for (int i = 0; i < span.Length - 3; i++) + { + + if (span[i] == ' ' && span[i+3] == ' ' + && ((span[i+1] >= '0' && span[i+1] <= '9') + || (span[i+1] >= 'a' && span[i+1] <= 'f') + || (span[i+1] >= 'A' && span[i+1] <= 'F') + || (span[i+2] >= '0' && span[i+2] <= '9') + || (span[i+2] >= 'a' && span[i+2] <= 'f') + || (span[i+2] >= 'A' && span[i+2] <= 'F') + ) + ) + { + lb.Add(_lookupSimd[span[i + 1]]); + hb.Add(_lookupSimd[span[i + 2]]); + } + } + + var ints = Simd(lb.ToArray(), hb.ToArray()); + var result = new byte[ints.Length]; + for (int i = 0; i < ints.Length; i++) + { + result[i] = (byte) ints[i]; + } + + return result; + } + + public static int[] Simd(int[] lb, int[] hb) + { + + var simdLength = Vector.Count; + var result = new int[lb.Length]; + var hexs = new Vector(16); + + var i = 0; + for (i = 0; i <= lb.Length - simdLength; i += simdLength) { + + var vl = new Vector(lb, i); + var vh = new Vector(hb, i); + (vl * hexs + vh).CopyTo(result, i); + } + + return result; + } + + } +} diff --git a/HexDump/HexDumpParseStateMachine.cs b/HexDump/HexDumpParseStateMachine.cs new file mode 100644 index 0000000..63195a7 --- /dev/null +++ b/HexDump/HexDumpParseStateMachine.cs @@ -0,0 +1,383 @@ +// Copyright (c) Drew Noakes. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace HexDump +{ + public static partial class HexDump + { + + private static readonly byte[] _smlookup1 = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + private static readonly byte[] _smlookup2 = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + + private static readonly byte[] _smsep = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + public class Config + { + public int Offset; + public int Hex; + public int Ascii; + public int NewLine; + } + + /// + /// Parse HexDump into a byte array + /// + /// + /// + public static byte[] ParseStasteMachine(string dump) + { + + //0000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //0000 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + //0000 9A 20 31 32 20 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A . 12 ........... + + var result = new List(); + var span = dump.AsSpan(); + var len = span.Length; + var config = Analyse(span); + + for (int i = 0; i < len;) + { + + i += config.Offset; + for (int j = 0; j < config.Hex; j += 3) + { + var off = i + j; + if (off > len) + { + break; + } + + if (_smlookup1[span[off + 0]] != 0xff + && _smlookup2[span[off + 1]] != 0xff) + { + byte dec = (byte) ( + _smlookup1[span[off + 0]] + + _smlookup2[span[off + 1]] + ); + + result.Add(dec); + } + + if (off + 3 < len + && span[off + 3] == ' ') + { + j++; + } + } + + i += config.Hex; + i += config.Ascii; + i += config.NewLine; + + } + + return result.ToArray(); + } + + public static Config Analyse(in ReadOnlySpan span) + { + var p = new Process(); + var len = span.Length; + var pm = new Config + { + NewLine = 1, + }; + + for (int i = 0; i < len; i++) + { + if (p.CurrentState == State.Started) + { + i += Started(span, i, len, pm, p); + } + + else if (p.CurrentState == State.Offset) + { + i += Offset(span, i, len, pm, p); + } + + else if (p.CurrentState == State.Hex) + { + i += Hex(span, i, len, pm, p); + } + + else if (p.CurrentState == State.Ascii) + { + i += Ascii(span, i, len,pm, p); + } + + else if (p.CurrentState == State.NewLine) + { + break; + } + else + { + break; + } + + } + + return pm; + } + + private static int Ascii(ReadOnlySpan span, int i, int len, Config pm, Process p) + { + if (span[i] == '\r') + { + pm.NewLine = 2; + p.MoveNext(Command.NewLine); + return 0; + } + + if (span[i] == '\n') + { + pm.NewLine = 1; + p.MoveNext(Command.NewLine); + } + return 0; + } + + private static int Hex(ReadOnlySpan span, int i, int len, Config pm, Process p) + { + if ( + i + 1 == len + ) + { + pm.Ascii = i - pm.Offset - pm.Hex + 1; + p.MoveNext(Command.Ascii); + return 0; + } + + + if ( + i + 1 < len + && ( + span[i + 1] == '\n' + || span[i + 1] == '\r') + ) + { + pm.Ascii = i - pm.Offset - pm.Hex + 1; + p.MoveNext(Command.Ascii); + return 0; + } + + if ( + span[i] == '\n' + || span[i] == '\r' + ) + { + pm.Ascii = i - pm.Offset - pm.Hex; + p.MoveNext(Command.Ascii); + return 0; + + } + return 0; + + } + + private static int Offset(ReadOnlySpan span, int i, int len, Config pm, Process p) + { + if (i + 3 >= len) + { + pm.Hex = len - pm.Offset; + p.MoveNext(Command.Hex); + return 0; + } + + if (span[i] == ' ' + && span[i + 1] == ' ' + && span[i + 2] == ' ' + && span[i + 3] == ' ') + { + pm.Hex = i - pm.Offset; + p.MoveNext(Command.Hex); + return 0; + } + + if (span[i + 3] == '\n' + || span[i + 3] == '\r' + ) + { + pm.Hex = i - pm.Offset + 3; + p.MoveNext(Command.Hex); + return 0; + } + + return 0; + + } + + private static int Started(ReadOnlySpan span, int i, int len, Config pm, Process p) + { + if (len < 8) + { + pm.Offset = 0; + p.MoveNext(Command.Offset); + return 0; + } + + int offset = 0; + if (_smlookup1[span[0]] != 0xff + && _smlookup2[span[0]] != 0xff + && _smlookup1[span[1]] != 0xff + && _smlookup2[span[1]] != 0xff + && _smlookup1[span[2]] != 0xff + && _smlookup2[span[2]] != 0xff + && _smlookup1[span[3]] != 0xff + && _smlookup2[span[3]] != 0xff + && span[4] == ' ' + && span[5] == ' ' + && span[6] == ' ' + && span[7] == ' ' + ) + { + offset = 8; + } + + pm.Offset = offset; + p.MoveNext(Command.Offset); + return offset; + } + + + public enum State : int + { + Started = 0, + Offset, + Hex, + Ascii, + NewLine, + Analysed + } + + public enum Command + { + Offset, + Hex, + Ascii, + NewLine, + Analysed + } + + public class Process + { + class StateTransition + { + readonly State Current; + readonly Command Command; + + public StateTransition(State current, Command command) + { + Current = current; + Command = command; + } + + public override int GetHashCode() + { + return 17 + 31 * Current.GetHashCode() + 31 * Command.GetHashCode(); + } + + public override bool Equals(object obj) + { + StateTransition other = obj as StateTransition; + return other != null && this.Current == other.Current + && this.Command == other.Command; + } + } + + Dictionary transitions; + public State CurrentState { get; private set; } + + public Process() + { + CurrentState = State.Started; + transitions = new Dictionary + { + {new StateTransition(State.Started, Command.Offset), State.Offset}, + {new StateTransition(State.Offset, Command.Hex), State.Hex}, + {new StateTransition(State.Hex, Command.Ascii), State.Ascii}, + {new StateTransition(State.Ascii, Command.NewLine), State.NewLine}, + {new StateTransition(State.Hex, Command.NewLine), State.NewLine}, + {new StateTransition(State.NewLine, Command.Analysed), State.Analysed}, + {new StateTransition(State.Hex, Command.Analysed), State.Analysed}, + }; + } + + public State GetNext(Command command) + { + StateTransition transition = new StateTransition(CurrentState, command); + State nextState; + if (!transitions.TryGetValue(transition, out nextState)) + throw new Exception("Invalid transition: " + CurrentState + " -> " + command); + return nextState; + } + + public State MoveNext(Command command) + { + CurrentState = GetNext(command); + return CurrentState; + } + + } + } +}