diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index f36f71a..5e1eeec 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -17,8 +17,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | - 6.0.x - 7.0.x + 8.0.x - name: Install dependencies run: dotnet restore ./Src/ - name: Build diff --git a/Src/Protsyk.PMS.FullText.ConsoleUtil/Protsyk.PMS.FullText.ConsoleUtil.csproj b/Src/Protsyk.PMS.FullText.ConsoleUtil/Protsyk.PMS.FullText.ConsoleUtil.csproj index 011db41..fbe5eef 100644 --- a/Src/Protsyk.PMS.FullText.ConsoleUtil/Protsyk.PMS.FullText.ConsoleUtil.csproj +++ b/Src/Protsyk.PMS.FullText.ConsoleUtil/Protsyk.PMS.FullText.ConsoleUtil.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 Petro Protsyk PMS FullText Search false diff --git a/Src/Protsyk.PMS.FullText.Core.UnitTests/BtreePersistentTests.cs b/Src/Protsyk.PMS.FullText.Core.UnitTests/BtreePersistentTests.cs index de51faf..62eebcb 100644 --- a/Src/Protsyk.PMS.FullText.Core.UnitTests/BtreePersistentTests.cs +++ b/Src/Protsyk.PMS.FullText.Core.UnitTests/BtreePersistentTests.cs @@ -59,7 +59,7 @@ public void BtreeP_Append_Test() [Theory] [InlineData(1000)] - public string TestBtreeP(int size) + public void TestBtreeP(int size) { var rnd = Enumerable.Range(1, size).ToArray().Shuffle(); var testFile = Path.Combine(TestFolder, "btree-test.bin"); @@ -94,7 +94,5 @@ public string TestBtreeP(int size) Assert.False(tree.ContainsKey(i)); } } - - return testFile; } } diff --git a/Src/Protsyk.PMS.FullText.Core.UnitTests/Protsyk.PMS.FullText.Core.UnitTests.csproj b/Src/Protsyk.PMS.FullText.Core.UnitTests/Protsyk.PMS.FullText.Core.UnitTests.csproj index 2fe58a2..5a72e7a 100644 --- a/Src/Protsyk.PMS.FullText.Core.UnitTests/Protsyk.PMS.FullText.Core.UnitTests.csproj +++ b/Src/Protsyk.PMS.FullText.Core.UnitTests/Protsyk.PMS.FullText.Core.UnitTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 false false enable @@ -12,9 +12,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Src/Protsyk.PMS.FullText.Core/Automata/FST.cs b/Src/Protsyk.PMS.FullText.Core/Automata/FST.cs index 5729e31..94892d7 100644 --- a/Src/Protsyk.PMS.FullText.Core/Automata/FST.cs +++ b/Src/Protsyk.PMS.FullText.Core/Automata/FST.cs @@ -7,8 +7,6 @@ using Protsyk.PMS.FullText.Core.Common; using Protsyk.PMS.FullText.Core.Common.Persistance; -using StringComparer = System.StringComparer; - namespace Protsyk.PMS.FullText.Core.Automata; public static class FSTExtensions @@ -94,10 +92,10 @@ private sealed class StateWithTransitions public int Id { get; private set; } - // When state is fronzen. Offset in the output file. + // When state is frozen. Offset in the output file. public long Offset { get; set; } - public bool IsFronzen { get; set; } + public bool IsFrozen { get; set; } public bool IsFinal { get; set; } @@ -106,7 +104,7 @@ private sealed class StateWithTransitions public StateWithTransitions() { Id = Interlocked.Increment(ref NextId); - IsFronzen = false; + IsFrozen = false; Offset = 0; IsFinal = false; @@ -169,8 +167,9 @@ private readonly struct Transition private static StateWithTransitions CopyOf(StateWithTransitions s) { - var t = new StateWithTransitions(); - t.IsFinal = s.IsFinal; + var t = new StateWithTransitions { + IsFinal = s.IsFinal + }; t.Arcs.AddRange(s.Arcs); return t; } @@ -180,7 +179,7 @@ private StateWithTransitions FreezeState(StateWithTransitions s) //TODO: Frozen state should only have toStateOffset in Arcs // therefore other type var r = CopyOf(s); - r.IsFronzen = true; + r.IsFrozen = true; r.Offset = WriteState(r); frozenStates.Add(r.Id, r); return r; @@ -188,7 +187,7 @@ private StateWithTransitions FreezeState(StateWithTransitions s) private long WriteState(StateWithTransitions s) { - if (!s.IsFronzen) throw new Exception("What?"); + if (!s.IsFrozen) throw new Exception("What?"); long startOffset = storage.Length; var size = 0; @@ -346,7 +345,7 @@ private StateWithTransitions FindMinimized(StateWithTransitions s) { if (!minimalTransducerStatesDictionary.TryGetValue(dedupHash, out var listToAdd)) { - listToAdd = new List>(); + listToAdd = []; minimalTransducerStatesDictionary.Add(dedupHash, listToAdd); } listToAdd.Add(usageQueue.AddFirst(r.Id)); @@ -358,7 +357,7 @@ private StateWithTransitions FindMinimized(StateWithTransitions s) private void SetTransition(StateWithTransitions from, char c, StateWithTransitions to) { - if (from.IsFronzen) throw new Exception("What?"); + if (from.IsFrozen) throw new Exception("What?"); for (int i = 0; i < from.Arcs.Count; ++i) { @@ -398,7 +397,7 @@ private static T GetOutput(StateWithTransitions from, char c) private static void SetOutput(StateWithTransitions from, char c, T output) { - if (from.IsFronzen) throw new Exception("What?"); + if (from.IsFrozen) throw new Exception("What?"); for (int i = 0; i < from.Arcs.Count; ++i) { @@ -419,7 +418,7 @@ private static void SetOutput(StateWithTransitions from, char c, T output) private static void PropagateOutput(StateWithTransitions from, T output, IFSTOutput outputType) { - if (from.IsFronzen) throw new Exception("What?"); + if (from.IsFrozen) throw new Exception("What?"); for (int i = 0; i < from.Arcs.Count; ++i) { @@ -435,7 +434,7 @@ private static void PropagateOutput(StateWithTransitions from, T output, IFSTOut private static void ClearState(StateWithTransitions s) { - if (s.IsFronzen) throw new Exception("What?"); + if (s.IsFrozen) throw new Exception("What?"); s.IsFinal = false; s.Arcs.Clear(); @@ -443,7 +442,7 @@ private static void ClearState(StateWithTransitions s) private static void SetFinal(StateWithTransitions s) { - if (s.IsFronzen) throw new Exception("What?"); + if (s.IsFrozen) throw new Exception("What?"); s.IsFinal = true; } @@ -492,12 +491,12 @@ public void Add(string currentWord, T currentOutput) tempState = newTemp; } - if (StringComparer.Ordinal.Compare(currentWord, previousWord) <= 0) + if (currentWord.AsSpan().CompareTo(previousWord, StringComparison.Ordinal) <= 0) { throw new Exception($"Input should be ordered and each item should be unique: {previousWord} < {currentWord}"); } - var prefixLengthPlusOne = 1 + Utils.LCP(previousWord, currentWord); + var prefixLengthPlusOne = 1 + previousWord.AsSpan().CommonPrefixLength(currentWord); if (prefixLengthPlusOne == 1 + currentWord.Length) { @@ -1124,7 +1123,6 @@ public class FST : IFST public IFSTOutput OutputType => outputType; #endregion - #region Methods public FST(IFSTOutput outputType) { this.states = new List(); @@ -1134,7 +1132,6 @@ public FST(IFSTOutput outputType) Initial = 0; } - #endregion #region Serialization public byte[] GetBytes() @@ -1747,7 +1744,7 @@ private FSTStringOutput() { } public string Min(string a, string b) { - return a.Substring(0, Utils.LCP(a, b)); + return a[..a.AsSpan().CommonPrefixLength(b)]; } public string Sub(string a, string b) @@ -1789,18 +1786,4 @@ public int WriteTo(string value, Span buffer) return size + byteCount; } -} - -internal static class Utils -{ - // Calculate length of the longest common prefix - public static int LCP(string a, string b) - { - int i = 0; - while (i < a.Length && i < b.Length && a[i] == b[i]) - { - ++i; - } - return i; - } } \ No newline at end of file diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/Btree.cs b/Src/Protsyk.PMS.FullText.Core/Collections/Btree.cs index 99c2855..eb6bb7f 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/Btree.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/Btree.cs @@ -32,10 +32,7 @@ public Btree() public Btree(int order) { - if (order < 1) - { - throw new ArgumentOutOfRangeException(nameof(order)); - } + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(order); this.order = order; this.maxChildren = 2 * order; diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/BtreePersistent.cs b/Src/Protsyk.PMS.FullText.Core/Collections/BtreePersistent.cs index b5be968..0420895 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/BtreePersistent.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/BtreePersistent.cs @@ -38,11 +38,7 @@ public BtreePersistent(int order) public BtreePersistent(IPersistentStorage persistentStorage, int order) { ArgumentNullException.ThrowIfNull(persistentStorage); - - if (order < 1) - { - throw new ArgumentOutOfRangeException(nameof(order)); - } + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(order); keySerializer = DataSerializer.GetDefault(); valueSerializer = DataSerializer.GetDefault(); diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/HeapExtensions.cs b/Src/Protsyk.PMS.FullText.Core/Collections/HeapExtensions.cs index 8160ddb..dc0f72c 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/HeapExtensions.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/HeapExtensions.cs @@ -30,11 +30,7 @@ public static IEnumerable TopN(this IEnumerable items, IComparer com { ArgumentNullException.ThrowIfNull(items); ArgumentNullException.ThrowIfNull(comparer); - - if (n < 0) - { - throw new ArgumentOutOfRangeException(nameof(n)); - } + ArgumentOutOfRangeException.ThrowIfNegative(n); if (n == 0) { diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/LFUCache.cs b/Src/Protsyk.PMS.FullText.Core/Collections/LFUCache.cs index 2e5d81e..f3e5563 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/LFUCache.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/LFUCache.cs @@ -9,10 +9,7 @@ public class LFUCache public LFUCache(int capacity) { - if (capacity <= 0) - { - throw new ArgumentOutOfRangeException(); - } + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity); this.capacity = capacity; this.nextOrder = 0; diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/LRUCache.cs b/Src/Protsyk.PMS.FullText.Core/Collections/LRUCache.cs index f845473..0d23f9c 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/LRUCache.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/LRUCache.cs @@ -13,10 +13,7 @@ public LRUCache() public LRUCache(int capacity) { - if (capacity < 1) - { - throw new ArgumentOutOfRangeException(nameof(capacity)); - } + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity); items = new Dictionary>(); usageQueue = new LinkedList<(TKey, TValue)>(); diff --git a/Src/Protsyk.PMS.FullText.Core/Collections/PersistentDictionary.cs b/Src/Protsyk.PMS.FullText.Core/Collections/PersistentDictionary.cs index c58c543..ccb61e7 100644 --- a/Src/Protsyk.PMS.FullText.Core/Collections/PersistentDictionary.cs +++ b/Src/Protsyk.PMS.FullText.Core/Collections/PersistentDictionary.cs @@ -42,10 +42,8 @@ public TValue this[long index] get { var offset = linearIndex[index]; - if (offset == 0) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } + + ArgumentOutOfRangeException.ThrowIfZero(offset); int dataSize = dataStorage.ReadInt32LittleEndian(offset); diff --git a/Src/Protsyk.PMS.FullText.Core/Common/PackedInts.cs b/Src/Protsyk.PMS.FullText.Core/Common/PackedInts.cs index 8638e3d..47e3981 100644 --- a/Src/Protsyk.PMS.FullText.Core/Common/PackedInts.cs +++ b/Src/Protsyk.PMS.FullText.Core/Common/PackedInts.cs @@ -227,7 +227,7 @@ public class PackedIntN8 : IPackedInts private const int BitsInBaseType = 8; private const int LastBitIndexInBaseType = 7; - private static readonly uint[] mask = new uint[] { + private static ReadOnlySpan mask => [ 0b0000_0000, 0b0000_0001, 0b0000_0011, @@ -237,7 +237,7 @@ public class PackedIntN8 : IPackedInts 0b0011_1111, 0b0111_1111, 0b1111_1111 - }; + ]; private readonly byte[] data; private readonly int bits; @@ -393,7 +393,7 @@ public class PackedIntN16 : IPackedInts private const int BitsInBaseType = 16; private const int LastBitIndexInBaseType = 15; - private static readonly uint[] mask = new uint[] { + private static ReadOnlySpan mask => [ 0b0000_0000_0000_0000, 0b0000_0000_0000_0001, 0b0000_0000_0000_0011, @@ -411,7 +411,7 @@ public class PackedIntN16 : IPackedInts 0b0011_1111_1111_1111, 0b0111_1111_1111_1111, 0b1111_1111_1111_1111 - }; + ]; private readonly ushort[] data; private readonly int bits; @@ -579,7 +579,7 @@ public class PackedIntN32 : IPackedInts private const int BitsInBaseType = 32; private const int LastBitIndexInBaseType = 31; - private static readonly uint[] mask = new uint[] { + private static ReadOnlySpan mask => [ 0b0000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0001, 0b0000_0000_0000_0000_0000_0000_0000_0011, @@ -613,7 +613,7 @@ public class PackedIntN32 : IPackedInts 0b0011_1111_1111_1111_1111_1111_1111_1111, 0b0111_1111_1111_1111_1111_1111_1111_1111, 0b1111_1111_1111_1111_1111_1111_1111_1111 - }; + ]; private readonly uint[] data; private readonly int bits; @@ -786,7 +786,7 @@ public class PackedIntN64 : IPackedInts, IPackedLongs private const int BitsInBaseType = 64; private const int LastBitIndexInBaseType = 63; - private static readonly ulong[] mask = new ulong[] { + private static ReadOnlySpan mask => [ 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001, 0b0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0011, @@ -852,7 +852,7 @@ public class PackedIntN64 : IPackedInts, IPackedLongs 0b0011_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111, 0b0111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111, 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111 - }; + ]; private readonly ulong[] data; private readonly int bits; diff --git a/Src/Protsyk.PMS.FullText.Core/Common/UTF8DfaDecoder.cs b/Src/Protsyk.PMS.FullText.Core/Common/UTF8DfaDecoder.cs index d5954eb..d0efb0c 100644 --- a/Src/Protsyk.PMS.FullText.Core/Common/UTF8DfaDecoder.cs +++ b/Src/Protsyk.PMS.FullText.Core/Common/UTF8DfaDecoder.cs @@ -11,8 +11,8 @@ public class UTF8DfaDecoder // // Type 0 Invalid UTF-8 byte // Type 2 10xxxxxx - private static readonly int[] ByteClass = new int[] - { + private static ReadOnlySpan ByteClass => + [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -29,16 +29,16 @@ public class UTF8DfaDecoder 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0 - }; + ]; // State ID = k * 6, where k = 0, 1, 2, 3 // 6 - number of transitions corresponding to 6 byte types - private static readonly int[] states = new int[]{ + private static ReadOnlySpan states => [ -1, 0, -1, 6, 12, 18, // state 0 -1, -1, 0, -1, -1, -1, // state 1 * 6 -1, -1, 6, -1, -1, -1, // state 2 * 6 -1, -1, 12, -1, -1, -1, // state 3 * 6 - }; + ]; static UTF8DfaDecoder() { diff --git a/Src/Protsyk.PMS.FullText.Core/IndexModels/TextPosition.cs b/Src/Protsyk.PMS.FullText.Core/IndexModels/TextPosition.cs index a8fb38a..f1753c2 100644 --- a/Src/Protsyk.PMS.FullText.Core/IndexModels/TextPosition.cs +++ b/Src/Protsyk.PMS.FullText.Core/IndexModels/TextPosition.cs @@ -8,15 +8,8 @@ namespace Protsyk.PMS.FullText.Core; public TextPosition(int offset, int length) { - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if (length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } + ArgumentOutOfRangeException.ThrowIfNegative(offset); + ArgumentOutOfRangeException.ThrowIfNegative(length); Offset = offset; Length = length; diff --git a/Src/Protsyk.PMS.FullText.Core/IndexTypes/Parser/QueryParser.cs b/Src/Protsyk.PMS.FullText.Core/IndexTypes/Parser/QueryParser.cs index 821cc70..5c37964 100644 --- a/Src/Protsyk.PMS.FullText.Core/IndexTypes/Parser/QueryParser.cs +++ b/Src/Protsyk.PMS.FullText.Core/IndexTypes/Parser/QueryParser.cs @@ -1,39 +1,32 @@ -using System.Text; +using System.Buffers; +using System.Collections.Frozen; +using System.Text; namespace Protsyk.PMS.FullText.Core; public class QueryParser { - private static readonly Dictionary> argumentsParsers = new() - { - { "OR", ParseArguments }, - { "AND", ParseArguments }, - { "SEQ", ParseArguments }, - - { "WORD", ParseWord }, - { "WILD", ParseWildcard }, - { "EDIT", ParseEdit } - }; - - private static readonly HashSet specialChars = new() - { + private static readonly FrozenDictionary> argumentsParsers = ((KeyValuePair>[])[ + new("OR", ParseArguments), + new("AND", ParseArguments), + new("SEQ", ParseArguments), + new("WORD", ParseWord), + new("WILD", ParseWildcard), + new("EDIT", ParseEdit) + ]).ToFrozenDictionary(); + + private static readonly SearchValues s_specialChars = SearchValues.Create( + [ ',', '(', ')', - '\\', - '~', - '*', '?' - }; + ]); - private static readonly HashSet whitespaceCharacters = new() - { - ' ', - '\t' - }; + private static readonly SearchValues s_whitespaceCharacters = SearchValues.Create([' ', '\t']); public AstQuery Parse(string s) { @@ -95,7 +88,7 @@ private static ParseResult ParseArguments(string s, int pos, string name) { var query = new FunctionAstQuery(name); - while (pos < s.Length && !specialChars.Contains(s[pos])) + while (pos < s.Length && !s_specialChars.Contains(s[pos])) { var result = Parse(s, pos); @@ -206,7 +199,7 @@ private static int ParseEscapedCharacter(string s, int pos) { if (pos + 1 < s.Length) { - if (!specialChars.Contains(s[pos + 1])) + if (!s_specialChars.Contains(s[pos + 1])) { throw new QueryParserException("invalid escape character", pos + 1); } @@ -229,12 +222,12 @@ private static void EnsureNotAtEnd(string s, int pos) private static bool IsStopCharacter(char c) { - return whitespaceCharacters.Contains(c) || specialChars.Contains(c); + return s_whitespaceCharacters.Contains(c) || s_specialChars.Contains(c); } private static int SkipWhitespace(string s, int pos) { - while (pos < s.Length && whitespaceCharacters.Contains(s[pos])) + while (pos < s.Length && s_whitespaceCharacters.Contains(s[pos])) { pos++; } diff --git a/Src/Protsyk.PMS.FullText.Core/IndexTypes/Persistent/DeltaVarIntListWriter.cs b/Src/Protsyk.PMS.FullText.Core/IndexTypes/Persistent/DeltaVarIntListWriter.cs index 28b2b59..55ce620 100644 --- a/Src/Protsyk.PMS.FullText.Core/IndexTypes/Persistent/DeltaVarIntListWriter.cs +++ b/Src/Protsyk.PMS.FullText.Core/IndexTypes/Persistent/DeltaVarIntListWriter.cs @@ -65,10 +65,7 @@ public long StartList() public void AddValue(ulong value) { - if (value == 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } + ArgumentOutOfRangeException.ThrowIfZero(value); if (first) { @@ -78,10 +75,7 @@ public void AddValue(ulong value) } else { - if (value <= previous) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, previous); int previousIndex = bufferIndex; bufferIndex += VarInt.WriteVUInt64(value - previous, buffer.AsSpan(bufferIndex)); diff --git a/Src/Protsyk.PMS.FullText.Core/Protsyk.PMS.FullText.Core.csproj b/Src/Protsyk.PMS.FullText.Core/Protsyk.PMS.FullText.Core.csproj index f54963e..b90f4a7 100644 --- a/Src/Protsyk.PMS.FullText.Core/Protsyk.PMS.FullText.Core.csproj +++ b/Src/Protsyk.PMS.FullText.Core/Protsyk.PMS.FullText.Core.csproj @@ -1,8 +1,7 @@ - net6.0 - 11 + net8.0 Petro Protsyk PMS FullText Search false