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
}
}