From fb8960d9779694afad90b5ee7217c5a592ba9511 Mon Sep 17 00:00:00 2001 From: Executor Date: Sun, 14 Mar 2021 22:38:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0MiraiCodeReader=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mirai-CSharp/Mirai-CSharp.csproj | 1 + Mirai-CSharp/Utility/MiraiCodeReader.cs | 102 ++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Mirai-CSharp/Utility/MiraiCodeReader.cs diff --git a/Mirai-CSharp/Mirai-CSharp.csproj b/Mirai-CSharp/Mirai-CSharp.csproj index 993561d..87fa37f 100644 --- a/Mirai-CSharp/Mirai-CSharp.csproj +++ b/Mirai-CSharp/Mirai-CSharp.csproj @@ -2,6 +2,7 @@ net5.0;netcoreapp3.1;netstandard2.1;netstandard2.0 + true Mirai_CSharp Library true diff --git a/Mirai-CSharp/Utility/MiraiCodeReader.cs b/Mirai-CSharp/Utility/MiraiCodeReader.cs new file mode 100644 index 0000000..31759dd --- /dev/null +++ b/Mirai-CSharp/Utility/MiraiCodeReader.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Mirai_CSharp.Utility +{ + //see https://github.com/mamoe/mirai/blob/dev/docs/Messages.md#mirai-%E7%A0%81 + public ref struct MiraiCodeReader + { + public delegate void NameHandler(ReadOnlySpan name); + + public delegate void ArgumentHandler(ReadOnlySpan argument); + + private readonly ReadOnlySpan _Code; + + private int _Index; + + private ReadOnlySpan Current => GetFromIndex(_Code, _Index); + + public MiraiCodeReader(ReadOnlySpan code) + { + _Code = code; + _Index = 0; + } + + public void Parse(NameHandler nameHandler, ArgumentHandler argumentHandler) + { + //[mirai: + ReadOnlySpan code = Current; + if (code.Length < 9) + { + throw new FormatException(); + } + if (!GetFromLength(code, 7).SequenceEqual("[mirai:".AsSpan())) + { + throw new FormatException(); + } + if (!FindBlock(GetFromIndex(code, 7), ']', out code)) + { + throw new FormatException("Missing end of char ]"); + } + //[mirai:......] + _Index += code.Length + 7; + if (!FindBlock(code, ':', out ReadOnlySpan result)) + { + if (code.Length - 1 == 0) + { + throw new FormatException("Missing name"); + } + nameHandler(GetFromLength(code, code.Length - 1)); + return; + } + nameHandler(GetFromLength(result, result.Length - 1)); + while (FindBlock(code = GetFromIndex(code, result.Length), ':', out result)) + { + argumentHandler(GetFromLength(result, result.Length - 1)); + } + argumentHandler(GetFromLength(code, code.Length - 1)); // do not check empty argument + } + + private static unsafe bool FindBlock(ReadOnlySpan input, char seperator, out ReadOnlySpan span) + { + int end = 0; + ReadOnlySpan code; + do + { + code = CreateReadOnlySpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), end), input.Length - end); + int result = code.IndexOf(seperator); + if (result == -1) + { + span = default; + return false; + } + end += result; + } + while (Unsafe.Add(ref MemoryMarshal.GetReference(code), end++ - 1) == '\\'); + span = CreateReadOnlySpan(ref MemoryMarshal.GetReference(input), end); + return true; + } + + private static unsafe ReadOnlySpan GetFromIndex(ReadOnlySpan span, int index) + { + return CreateReadOnlySpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index), span.Length - index); + } + + private static unsafe ReadOnlySpan GetFromLength(ReadOnlySpan span, int length) + { + return CreateReadOnlySpan(ref MemoryMarshal.GetReference(span), length); + } +#if !NETSTANDARD2_0 + private static unsafe ReadOnlySpan CreateReadOnlySpan(ref char c, int length) + { + return MemoryMarshal.CreateReadOnlySpan(ref c, length); + } +#else + private static unsafe ReadOnlySpan CreateReadOnlySpan(ref char c, int length) + { + return new ReadOnlySpan(Unsafe.AsPointer(ref c), length); + } +#endif + } +}