diff --git a/Directory.Build.targets b/Directory.Build.targets index e090c18b89..48ad23bc2e 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -94,6 +94,7 @@ $(DefineConstants);FEATURE_ARRAY_FILL $(DefineConstants);FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR $(DefineConstants);FEATURE_CONDITIONALWEAKTABLE_ADDORUPDATE + $(DefineConstants);FEATURE_ENCODING_GETSTRING_READONLYSPAN $(DefineConstants);FEATURE_MEMORYMARSHAL_CREATEREADONLYSPAN $(DefineConstants);FEATURE_NUMBER_PARSE_READONLYSPAN $(DefineConstants);FEATURE_STREAM_READ_SPAN diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/HyphenationTree.cs b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/HyphenationTree.cs index d94f0afcdb..a13f455886 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/HyphenationTree.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/HyphenationTree.cs @@ -1,5 +1,7 @@ // Lucene version compatibility level 4.8.1 using Lucene.Net.Support; +using Lucene.Net.Util; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -220,7 +222,7 @@ public virtual string FindPattern(string pat) /// /// String compare, returns 0 if equal or t is a substring of s /// - protected virtual int HStrCmp(char[] s, int si, char[] t, int ti) + protected virtual int HStrCmp(ReadOnlySpan s, int si, ReadOnlySpan t, int ti) { for (; s[si] == t[ti]; si++, ti++) { @@ -287,7 +289,7 @@ protected virtual byte[] GetValues(int k) /// null terminated word to match /// start index from word /// interletter values array to update - protected virtual void SearchPatterns(char[] word, int index, byte[] il) + protected virtual void SearchPatterns(ReadOnlySpan word, int index, Span il) { byte[] values; int i = index; @@ -375,8 +377,8 @@ protected virtual void SearchPatterns(char[] word, int index, byte[] il) /// hyphenated word or null if word is not hyphenated. public virtual Hyphenation Hyphenate(string word, int remainCharCount, int pushCharCount) { - char[] w = word.ToCharArray(); - return Hyphenate(w, 0, w.Length, remainCharCount, pushCharCount); + // LUCENENET: use Span instead of ToCharArray + return Hyphenate(word.AsSpan(), 0, word.Length, remainCharCount, pushCharCount); } @@ -406,13 +408,14 @@ public virtual Hyphenation Hyphenate(string word, int remainCharCount, int pushC /// hyphenation point. /// a object representing the /// hyphenated word or null if word is not hyphenated. - public virtual Hyphenation Hyphenate(char[] w, int offset, int len, int remainCharCount, int pushCharCount) + public virtual Hyphenation Hyphenate(ReadOnlySpan w, int offset, int len, int remainCharCount, int pushCharCount) { int i; - char[] word = new char[len + 3]; + // LUCENENET: optimized method for Span and stackalloc + Span word = (len + 3) * sizeof(char) > Constants.MaxStackByteLimit ? new char[len + 3] : stackalloc char[len + 3]; // normalize word - char[] c = new char[2]; + Span c = stackalloc char[2]; int iIgnoreAtBeginning = 0; int iLength = len; bool bEndOfLetters = false; @@ -452,11 +455,11 @@ public virtual Hyphenation Hyphenate(char[] w, int offset, int len, int remainCh // word is too short to be hyphenated return null; } - int[] result = new int[len + 1]; + Span result = (len + 1) * sizeof(int) > Constants.MaxStackByteLimit ? new int[len + 1] : stackalloc int[len + 1]; int k = 0; // check exception list first - string sw = new string(word, 1, len); + string sw = word.Slice(1, len).ToString(); // LUCENENET: Eliminated extra lookup by using TryGetValue instead of ContainsKey if (m_stoplist.TryGetValue(sw, out IList hw)) { @@ -484,7 +487,7 @@ public virtual Hyphenation Hyphenate(char[] w, int offset, int len, int remainCh word[0] = '.'; // word start marker word[len + 1] = '.'; // word end marker word[len + 2] = (char)0; // null terminated - byte[] il = new byte[len + 3]; // initialized to zero + Span il = (len + 3) > Constants.MaxStackByteLimit ? new byte[len + 3] : stackalloc byte[len + 3]; // initialized to zero for (i = 0; i < len + 1; i++) { SearchPatterns(word, i, il); @@ -507,7 +510,7 @@ public virtual Hyphenation Hyphenate(char[] w, int offset, int len, int remainCh { // trim result array int[] res = new int[k + 2]; - Arrays.Copy(result, 0, res, 1, k); + result.Slice(0, k).CopyTo(res.AsSpan(1, k)); // We add the synthetical hyphenation points // at the beginning and end of the word res[0] = 0; @@ -535,7 +538,7 @@ public virtual void AddClass(string chargroup) if (chargroup.Length > 0) { char equivChar = chargroup[0]; - char[] key = new char[2]; + Span key = stackalloc char[2]; key[1] = (char)0; for (int i = 0; i < chargroup.Length; i++) { diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/PatternParser.cs b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/PatternParser.cs index 2d3b779414..f55a849e78 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/PatternParser.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/PatternParser.cs @@ -299,11 +299,12 @@ protected virtual IList NormalizeException(IList ex) { res.Add(buf.ToString()); buf.Length = 0; - char[] h = new char[1]; - h[0] = hyphenChar; + // LUCENENET: commented out unnecessary array allocation + //char[] h = new char[1]; + //h[0] = hyphenChar; // we use here hyphenChar which is not necessarily // the one to be printed - res.Add(new Hyphen(new string(h), null, null)); + res.Add(new Hyphen(new string(hyphenChar, 1), null, null)); } } if (buf.Length > 0) diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/TernaryTree.cs b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/TernaryTree.cs index 29512e1245..b19ed66b7a 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/TernaryTree.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Compound/Hyphenation/TernaryTree.cs @@ -1,5 +1,6 @@ // Lucene version compatibility level 4.8.1 using Lucene.Net.Support; +using Lucene.Net.Util; using System; using System.Collections; using System.Collections.Generic; @@ -145,7 +146,19 @@ protected void Init() /// same prefix is inserted. This saves a lot of space, specially for long /// keys. /// - public virtual void Insert(string key, char val) + public void Insert(string key, char val) + => Insert(key.AsSpan(), val); + + /// + /// Branches are initially compressed, needing one node per key plus the size + /// of the string key. They are decompressed as needed when another key with + /// same prefix is inserted. This saves a lot of space, specially for long + /// keys. + /// + /// + /// LUCENENET specific overload for + /// + public virtual void Insert(ReadOnlySpan key, char val) { // make sure we have enough room in the arrays int len = key.Length + 1; // maximum number of nodes that may be generated @@ -153,13 +166,15 @@ public virtual void Insert(string key, char val) { RedimNodeArrays(m_eq.Length + BLOCK_SIZE); } - char[] strkey = new char[len--]; - key.CopyTo(0, strkey, 0, len - 0); + // LUCENENET: add optimization for stackalloc and Span + int strkeyLen = len--; + Span strkey = strkeyLen * sizeof(char) > Constants.MaxStackByteLimit ? new char[strkeyLen] : stackalloc char[strkeyLen]; + key.CopyTo(strkey); strkey[len] = (char)0; m_root = Insert(m_root, strkey, 0, val); } - public virtual void Insert(char[] key, int start, char val) + public virtual void Insert(ReadOnlySpan key, int start, char val) { int len = StrLen(key) + 1; if (m_freenode + len > m_eq.Length) @@ -172,7 +187,7 @@ public virtual void Insert(char[] key, int start, char val) /// /// The actual insertion function, recursive version. /// - private char Insert(char p, char[] key, int start, char val) + private char Insert(char p, ReadOnlySpan key, int start, char val) { int len = StrLen(key, start); if (p == 0) @@ -264,7 +279,7 @@ private char Insert(char p, char[] key, int start, char val) /// /// Compares 2 null terminated char arrays /// - public static int StrCmp(char[] a, int startA, char[] b, int startB) + public static int StrCmp(ReadOnlySpan a, int startA, ReadOnlySpan b, int startB) { for (; a[startA] == b[startB]; startA++, startB++) { @@ -279,7 +294,7 @@ public static int StrCmp(char[] a, int startA, char[] b, int startB) /// /// Compares a string with null terminated char array /// - public static int StrCmp(string str, char[] a, int start) + public static int StrCmp(ReadOnlySpan str, ReadOnlySpan a, int start) { int i, d, len = str.Length; for (i = 0; i < len; i++) @@ -299,10 +314,9 @@ public static int StrCmp(string str, char[] a, int start) return -a[start + i]; } return 0; - } - public static void StrCpy(char[] dst, int di, char[] src, int si) + public static void StrCpy(Span dst, int di, ReadOnlySpan src, int si) { while (src[si] != 0) { @@ -311,7 +325,7 @@ public static void StrCpy(char[] dst, int di, char[] src, int si) dst[di] = (char)0; } - public static int StrLen(char[] a, int start) + public static int StrLen(ReadOnlySpan a, int start) { int len = 0; for (int i = start; i < a.Length && a[i] != 0; i++) @@ -321,7 +335,7 @@ public static int StrLen(char[] a, int start) return len; } - public static int StrLen(char[] a) + public static int StrLen(ReadOnlySpan a) { return StrLen(a, 0); } @@ -329,14 +343,15 @@ public static int StrLen(char[] a) public virtual int Find(string key) { int len = key.Length; - char[] strkey = new char[len + 1]; - key.CopyTo(0, strkey, 0, len - 0); + // LUCENENET: add optimization for stackalloc and Span + Span strkey = (len + 1) * sizeof(char) > Constants.MaxStackByteLimit ? new char[len + 1] : stackalloc char[len + 1]; + key.AsSpan().CopyTo(strkey); strkey[len] = (char)0; return Find(strkey, 0); } - public virtual int Find(char[] key, int start) + public virtual int Find(ReadOnlySpan key, int start) { int d; char p = m_root; @@ -424,7 +439,7 @@ public virtual object Clone() /// upper halves, and so on in order to get a balanced tree. The array of keys /// is assumed to be sorted in ascending order. /// - protected virtual void InsertBalanced(string[] k, char[] v, int offset, int n) + protected virtual void InsertBalanced(ReadOnlySpan k, ReadOnlySpan v, int offset, int n) { int m; if (n < 1) @@ -449,7 +464,8 @@ public virtual void Balance() int i = 0, n = m_length; string[] k = new string[n]; - char[] v = new char[n]; + // LUCENENET: add optimization for stackalloc and Span + Span v = n * sizeof(char) > Constants.MaxStackByteLimit ? new char[n] : stackalloc char[n]; using (Enumerator iter = new Enumerator(this)) { while (iter.MoveNext()) diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Payloads/PayloadHelper.cs b/src/Lucene.Net.Analysis.Common/Analysis/Payloads/PayloadHelper.cs index fb2d3e0740..9e4cc875ff 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Payloads/PayloadHelper.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Payloads/PayloadHelper.cs @@ -1,4 +1,7 @@ // Lucene version compatibility level 4.8.1 + +using System; + namespace Lucene.Net.Analysis.Payloads { /* @@ -62,10 +65,10 @@ public static byte[] EncodeInt32(int payload, byte[] data, int offset) /// /// NOTE: This was decodeFloat() in Lucene /// - /// + /// /// /// the decoded float - public static float DecodeSingle(byte[] bytes) + public static float DecodeSingle(ReadOnlySpan bytes) { return DecodeSingle(bytes, 0); } @@ -81,16 +84,15 @@ public static float DecodeSingle(byte[] bytes) /// The float that was encoded /// /// - public static float DecodeSingle(byte[] bytes, int offset) + public static float DecodeSingle(ReadOnlySpan bytes, int offset) { - return J2N.BitConversion.Int32BitsToSingle(DecodeInt32(bytes, offset)); } /// /// NOTE: This was decodeInt() in Lucene /// - public static int DecodeInt32(byte[] bytes, int offset) + public static int DecodeInt32(ReadOnlySpan bytes, int offset) { return ((bytes[offset] & 0xFF) << 24) | ((bytes[offset + 1] & 0xFF) << 16) | ((bytes[offset + 2] & 0xFF) << 8) | (bytes[offset + 3] & 0xFF); } diff --git a/src/Lucene.Net.Analysis.SmartCn/Hhmm/AbstractDictionary.cs b/src/Lucene.Net.Analysis.SmartCn/Hhmm/AbstractDictionary.cs index cdfb7154dd..95de24752a 100644 --- a/src/Lucene.Net.Analysis.SmartCn/Hhmm/AbstractDictionary.cs +++ b/src/Lucene.Net.Analysis.SmartCn/Hhmm/AbstractDictionary.cs @@ -2,6 +2,10 @@ using System; using System.Text; +#if !FEATURE_ENCODING_GETSTRING_READONLYSPAN +using Lucene.Net.Support.Text; +#endif + namespace Lucene.Net.Analysis.Cn.Smart.Hhmm { /* @@ -97,7 +101,7 @@ public virtual string GetCCByGB2312Id(int ccid) return ""; int cc1 = ccid / 94 + 161; int cc2 = ccid % 94 + 161; - byte[] buffer = new byte[2]; + Span buffer = stackalloc byte[2]; buffer[0] = (byte)cc1; buffer[1] = (byte)cc2; try diff --git a/src/Lucene.Net.Analysis.Stempel/Egothor.Stemmer/Diff.cs b/src/Lucene.Net.Analysis.Stempel/Egothor.Stemmer/Diff.cs index b93468270e..08070937c8 100644 --- a/src/Lucene.Net.Analysis.Stempel/Egothor.Stemmer/Diff.cs +++ b/src/Lucene.Net.Analysis.Stempel/Egothor.Stemmer/Diff.cs @@ -185,7 +185,7 @@ public string Exec(string a, string b) int y; int maxx; int maxy; - int[] go = new int[4]; + Span go = stackalloc int[4]; // LUCENENET: optimize for Span and stackalloc const int X = 1; const int Y = 2; const int R = 3; diff --git a/src/Lucene.Net.Queries/ChainedFilter.cs b/src/Lucene.Net.Queries/ChainedFilter.cs index aaa3540c6d..399c061325 100644 --- a/src/Lucene.Net.Queries/ChainedFilter.cs +++ b/src/Lucene.Net.Queries/ChainedFilter.cs @@ -96,7 +96,8 @@ public ChainedFilter(Filter[] chain, int logic) /// public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs) { - int[] index = new int[1]; // use array as reference to modifiable int; + // LUCENENET specific - use stackalloc and Span instead of new int[1] + Span index = stackalloc int[1]; // use array as reference to modifiable int; index[0] = 0; // an object attribute would not be thread safe. if (logic != -1) { @@ -132,7 +133,7 @@ private static DocIdSetIterator GetDISI(Filter filter, AtomicReaderContext conte } } - private FixedBitSet InitialResult(AtomicReaderContext context, int logic, int[] index) + private FixedBitSet InitialResult(AtomicReaderContext context, int logic, Span index) { AtomicReader reader = context.AtomicReader; FixedBitSet result = new FixedBitSet(reader.MaxDoc); @@ -157,7 +158,7 @@ private FixedBitSet InitialResult(AtomicReaderContext context, int logic, int[] /// Logical operation /// /// DocIdSet - private DocIdSet GetDocIdSet(AtomicReaderContext context, int logic, int[] index) + private DocIdSet GetDocIdSet(AtomicReaderContext context, int logic, Span index) { FixedBitSet result = InitialResult(context, logic, index); for (; index[0] < chain.Length; index[0]++) @@ -175,7 +176,7 @@ private DocIdSet GetDocIdSet(AtomicReaderContext context, int logic, int[] index /// Logical operation /// /// DocIdSet - private DocIdSet GetDocIdSet(AtomicReaderContext context, int[] logic, int[] index) + private DocIdSet GetDocIdSet(AtomicReaderContext context, int[] logic, Span index) { if (logic.Length != chain.Length) { diff --git a/src/Lucene.Net.Tests/Support/Text/TestEncodingExtensions.cs b/src/Lucene.Net.Tests/Support/Text/TestEncodingExtensions.cs index 55123917e2..3fcdd8b444 100644 --- a/src/Lucene.Net.Tests/Support/Text/TestEncodingExtensions.cs +++ b/src/Lucene.Net.Tests/Support/Text/TestEncodingExtensions.cs @@ -3,6 +3,10 @@ using NUnit.Framework; using System.Text; +#if !FEATURE_ENCODING_GETSTRING_READONLYSPAN +using System; +#endif + namespace Lucene.Net.Support.Text { /* @@ -38,5 +42,16 @@ public void TestWithDecoderExceptionFallback() _ = newEncoding.GetString(new byte[] { 0xF0 }); }); } + +#if !FEATURE_ENCODING_GETSTRING_READONLYSPAN + [Test, LuceneNetSpecific] + public void TestGetString_ReadOnlySpan() + { + Encoding encoding = Encoding.UTF8; + Span bytes = stackalloc byte[] { 0x40 }; + string s = encoding.GetString(bytes); + Assert.AreEqual("@", s); + } +#endif } } diff --git a/src/Lucene.Net/Support/Text/EncodingExtensions.cs b/src/Lucene.Net/Support/Text/EncodingExtensions.cs index 5e1c3574cd..fdbe1c0f4b 100644 --- a/src/Lucene.Net/Support/Text/EncodingExtensions.cs +++ b/src/Lucene.Net/Support/Text/EncodingExtensions.cs @@ -1,5 +1,10 @@ using System.Collections.Concurrent; using System.Text; + +#if !FEATURE_ENCODING_GETSTRING_READONLYSPAN +using System; +#endif + #nullable enable namespace Lucene.Net.Support.Text @@ -54,5 +59,15 @@ public static Encoding WithDecoderExceptionFallback(this Encoding encoding) return newEncoding; }); } + +#if !FEATURE_ENCODING_GETSTRING_READONLYSPAN + public static unsafe string GetString(this Encoding encoding, ReadOnlySpan bytes) + { + fixed (byte* bytesPtr = bytes) + { + return encoding.GetString(bytesPtr, bytes.Length); + } + } +#endif } }