From 0d7bba7a42a89a3e348c6c27370425b4db33b492 Mon Sep 17 00:00:00 2001 From: devklick <58235000+devklick@users.noreply.github.com> Date: Sun, 18 Aug 2024 14:57:36 +0100 Subject: [PATCH] feat: #954 - TryDequeue and TryPeek Queue extension methods (#957) * feat: #954 - Add TryDequeue and TryPeek Queue extension methods * feat: #954 - Use TryDequeue and TryPeek Queue extension methods * Add license * Use Queue.TryDequeue where possible * Use Queue.TryPeek where possible * Use explicit type over var * Fix tests * Run tests depending on feature flag * Reinstate previous performance optimizations * Fix QueueExtensions exception docs * Remove unnecessary ParamName assertion in QueueExtensionsTests * Add nullable support to QueueExtensions --- Directory.Build.targets | 1 + .../Compound/CompoundWordTokenFilterBase.cs | 6 +- .../Analysis/Shingle/ShingleFilter.cs | 7 +- .../Analysis/Th/ThaiTokenizer.cs | 22 +++-- .../DoubleMetaphoneFilter.cs | 6 +- .../Support/ToParentBlockJoinCollector.cs | 7 +- .../ToParentBlockJoinCollector.cs | 7 +- .../Suggest/Tst/TSTAutocomplete.cs | 2 +- .../Util/Automaton/AutomatonTestUtil.cs | 9 +- .../Support/QueueExtensionsTests.cs | 93 +++++++++++++++++++ .../Index/DocumentsWriterFlushControl.cs | 10 +- .../Index/DocumentsWriterFlushQueue.cs | 6 +- src/Lucene.Net/Index/IndexWriter.cs | 8 +- src/Lucene.Net/Search/NumericRangeQuery.cs | 11 ++- src/Lucene.Net/Support/QueueExtensions.cs | 81 ++++++++++++++++ src/Lucene.Net/Util/Automaton/Automaton.cs | 16 ++-- .../Util/Automaton/BasicOperations.cs | 14 ++- .../Util/Automaton/MinimizationOperations.cs | 8 +- 18 files changed, 240 insertions(+), 74 deletions(-) create mode 100644 src/Lucene.Net.Tests/Support/QueueExtensionsTests.cs create mode 100644 src/Lucene.Net/Support/QueueExtensions.cs diff --git a/Directory.Build.targets b/Directory.Build.targets index 6da2ebe6b3..966808152a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -67,6 +67,7 @@ $(DefineConstants);FEATURE_NUMBER_PARSE_READONLYSPAN $(DefineConstants);FEATURE_STREAM_READ_SPAN $(DefineConstants);FEATURE_STRINGBUILDER_APPEND_READONLYSPAN + $(DefineConstants);FEATURE_QUEUE_TRYDEQUEUE_TRYPEEK diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Compound/CompoundWordTokenFilterBase.cs b/src/Lucene.Net.Analysis.Common/Analysis/Compound/CompoundWordTokenFilterBase.cs index f17e2e0df2..def2884f05 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Compound/CompoundWordTokenFilterBase.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Compound/CompoundWordTokenFilterBase.cs @@ -4,6 +4,7 @@ using Lucene.Net.Analysis.Util; using Lucene.Net.Diagnostics; using Lucene.Net.Util; +using Lucene.Net.Support; using System; using System.Collections.Generic; @@ -109,10 +110,9 @@ protected CompoundWordTokenFilterBase(LuceneVersion matchVersion, TokenStream in public override sealed bool IncrementToken() { - if (m_tokens.Count > 0) + if (m_tokens.TryDequeue(out CompoundToken token)) { if (Debugging.AssertsEnabled) Debugging.Assert(current != null); - CompoundToken token = m_tokens.Dequeue(); RestoreState(current); // keep all other attributes untouched m_termAtt.SetEmpty().Append(token.Text); m_offsetAtt.SetOffset(token.StartOffset, token.EndOffset); @@ -197,4 +197,4 @@ public CompoundToken(CompoundWordTokenFilterBase compoundWordTokenFilterBase, in } } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Shingle/ShingleFilter.cs b/src/Lucene.Net.Analysis.Common/Analysis/Shingle/ShingleFilter.cs index ccd0e0bbe9..9cadafd2d2 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Shingle/ShingleFilter.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Shingle/ShingleFilter.cs @@ -1,6 +1,7 @@ // Lucene version compatibility level 4.8.1 using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Util; +using Lucene.Net.Support; using System; using System.Collections.Generic; using System.IO; @@ -508,11 +509,7 @@ public override void End() /// if there's a problem getting the next token private void ShiftInputWindow() { - InputWindowToken firstToken = null; - if (inputWindow.Count > 0) - { - firstToken = inputWindow.Dequeue(); - } + inputWindow.TryDequeue(out InputWindowToken firstToken); // LUCENENET: firstToken will be null if the queue is empty while (inputWindow.Count < maxShingleSize) { if (null != firstToken) // recycle the firstToken, if available diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Th/ThaiTokenizer.cs b/src/Lucene.Net.Analysis.Common/Analysis/Th/ThaiTokenizer.cs index aba8a4328a..1bede96190 100644 --- a/src/Lucene.Net.Analysis.Common/Analysis/Th/ThaiTokenizer.cs +++ b/src/Lucene.Net.Analysis.Common/Analysis/Th/ThaiTokenizer.cs @@ -5,6 +5,7 @@ using J2N; using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Analysis.Util; +using Lucene.Net.Support; using Lucene.Net.Support.Threading; using Lucene.Net.Util; using System; @@ -236,8 +237,10 @@ public int Current { get { - if (transitions.Count > 0) - return transitions.Peek(); + if (transitions.TryPeek(out int current)) + { + return current; + } return wordBreaker.Current; } @@ -245,11 +248,10 @@ public int Current public int Next() { - if (transitions.Count > 0) - transitions.Dequeue(); - - if (transitions.Count > 0) - return transitions.Peek(); + if (transitions.TryDequeue(out _) && transitions.TryPeek(out int next)) + { + return next; + } return GetNext(); } @@ -297,10 +299,10 @@ private int GetNext() prevWasNonThai = isNonThai; } - if (transitions.Count > 0) + if (transitions.TryPeek(out int transition)) { transitions.Enqueue(current); - return transitions.Peek(); + return transition; } } @@ -308,4 +310,4 @@ private int GetNext() } } } -#endif \ No newline at end of file +#endif diff --git a/src/Lucene.Net.Analysis.Phonetic/DoubleMetaphoneFilter.cs b/src/Lucene.Net.Analysis.Phonetic/DoubleMetaphoneFilter.cs index b4e2592275..bd1d89d34d 100644 --- a/src/Lucene.Net.Analysis.Phonetic/DoubleMetaphoneFilter.cs +++ b/src/Lucene.Net.Analysis.Phonetic/DoubleMetaphoneFilter.cs @@ -1,6 +1,7 @@ // lucene version compatibility level: 4.8.1 using Lucene.Net.Analysis.Phonetic.Language; using Lucene.Net.Analysis.TokenAttributes; +using Lucene.Net.Support; using System; using System.Collections.Generic; @@ -37,7 +38,7 @@ public sealed class DoubleMetaphoneFilter : TokenFilter private readonly IPositionIncrementAttribute posAtt; /// - /// Creates a with the specified maximum code length, + /// Creates a with the specified maximum code length, /// and either adding encoded forms as synonyms (inject=true) or /// replacing them. /// @@ -54,10 +55,9 @@ public override bool IncrementToken() { for (;;) { - if (!(remainingTokens.Count == 0)) + if (remainingTokens.TryDequeue(out State first)) { // clearAttributes(); // not currently necessary - var first = remainingTokens.Dequeue(); RestoreState(first); return true; } diff --git a/src/Lucene.Net.Join/Support/ToParentBlockJoinCollector.cs b/src/Lucene.Net.Join/Support/ToParentBlockJoinCollector.cs index 697027aecc..873de9fc9e 100644 --- a/src/Lucene.Net.Join/Support/ToParentBlockJoinCollector.cs +++ b/src/Lucene.Net.Join/Support/ToParentBlockJoinCollector.cs @@ -351,11 +351,10 @@ public virtual void SetScorer(Scorer scorer) var queue = new Queue(); //System.out.println("\nqueue: add top scorer=" + value); queue.Enqueue(scorer); - while (queue.Count > 0) + // LUCENENET NOTE: This reuses the scorer argument variable, which + // differs from this.scorer. + while (queue.TryDequeue(out scorer)) { - // LUCENENET NOTE: This reuses the scorer argument variable, which - // differs from this.scorer. - scorer = queue.Dequeue(); //System.out.println(" poll: " + value + "; " + value.getWeight().getQuery()); if (scorer is ToParentBlockJoinQuery.BlockJoinScorer blockJoinScorer) { diff --git a/src/Lucene.Net.Join/ToParentBlockJoinCollector.cs b/src/Lucene.Net.Join/ToParentBlockJoinCollector.cs index f37e3c4fb6..d42fe0dac3 100644 --- a/src/Lucene.Net.Join/ToParentBlockJoinCollector.cs +++ b/src/Lucene.Net.Join/ToParentBlockJoinCollector.cs @@ -349,11 +349,10 @@ public virtual void SetScorer(Scorer scorer) var queue = new Queue(); //System.out.println("\nqueue: add top scorer=" + value); queue.Enqueue(scorer); - while (queue.Count > 0) + // LUCENENET NOTE: This reuses the scorer argument variable, which + // differs from this.scorer. + while (queue.TryDequeue(out scorer)) { - // LUCENENET NOTE: This reuses the scorer argument variable, which - // differs from this.scorer. - scorer = queue.Dequeue(); //System.out.println(" poll: " + value + "; " + value.getWeight().getQuery()); if (scorer is ToParentBlockJoinQuery.BlockJoinScorer blockJoinScorer) { diff --git a/src/Lucene.Net.Suggest/Suggest/Tst/TSTAutocomplete.cs b/src/Lucene.Net.Suggest/Suggest/Tst/TSTAutocomplete.cs index 5186e88e24..7bcd57b34a 100644 --- a/src/Lucene.Net.Suggest/Suggest/Tst/TSTAutocomplete.cs +++ b/src/Lucene.Net.Suggest/Suggest/Tst/TSTAutocomplete.cs @@ -203,4 +203,4 @@ public virtual IList PrefixCompletion(TernaryTreeNode root, str return suggest; } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net.TestFramework/Util/Automaton/AutomatonTestUtil.cs b/src/Lucene.Net.TestFramework/Util/Automaton/AutomatonTestUtil.cs index 700b9cd14c..10c1544cdc 100644 --- a/src/Lucene.Net.TestFramework/Util/Automaton/AutomatonTestUtil.cs +++ b/src/Lucene.Net.TestFramework/Util/Automaton/AutomatonTestUtil.cs @@ -1,5 +1,6 @@ using J2N; using J2N.Runtime.CompilerServices; +using Lucene.Net.Support; using Lucene.Net.Diagnostics; using RandomizedTesting.Generators; using System; @@ -305,9 +306,8 @@ public static void DeterminizeSimple(Automaton a, ISet initialset) worklist.Enqueue(initialset); a.initial = new State(); newstate[initialset] = a.initial; - while (worklist.Count > 0) + while (worklist.TryDequeue(out ISet s)) { - ISet s = worklist.Dequeue(); State r = newstate[s]; foreach (State q in s) { @@ -466,9 +466,8 @@ public RandomAcceptedStrings(Automaton a) // Breadth-first search, from accept states, // backwards: - while (q.Count > 0) + while (q.TryDequeue(out State s)) { - State s = q.Dequeue(); if (allArriving.TryGetValue(s, out IList arriving) && arriving != null) { foreach (ArrivingTransition at in arriving) @@ -566,4 +565,4 @@ public int[] GetRandomAcceptedString(Random r) return soFar.ToArray(); // LUCENENET: ArrayUtil.ToIntArray() call unnecessary } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net.Tests/Support/QueueExtensionsTests.cs b/src/Lucene.Net.Tests/Support/QueueExtensionsTests.cs new file mode 100644 index 0000000000..8b11f5a130 --- /dev/null +++ b/src/Lucene.Net.Tests/Support/QueueExtensionsTests.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using NUnit.Framework; + +using Lucene.Net.Attributes; +using Lucene.Net.Util; +using Lucene.Net.Support; +using System; + +using Assert = Lucene.Net.TestFramework.Assert; + + +namespace Lucene.Net +{ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + public class QueueExtensionsTests : LuceneTestCase + { +#if !FEATURE_QUEUE_TRYDEQUEUE_TRYPEEK + [Test, LuceneNetSpecific] + public void TryDequeue_ThrowsWhenQueueNull() + { + Queue queue = null; + Assert.Throws(() => queue.TryDequeue(out int _)); + } + + [Test, LuceneNetSpecific] + public void TryDequeue_QueueEmpty() + { + Queue queue = new Queue(); + bool found = queue.TryDequeue(out int result); + Assert.AreEqual(found, false); + Assert.AreEqual(result, default(int)); + } + + [Test, LuceneNetSpecific] + public void TryDequeue_QueueNotEmpty() + { + Queue queue = new Queue(); + int item = 1; + queue.Enqueue(item); + int countBefore = queue.Count; + bool found = queue.TryDequeue(out int result); + Assert.AreEqual(found, true); + Assert.AreEqual(result, item); + Assert.AreEqual(queue.Count, countBefore - 1); + } + + [Test, LuceneNetSpecific] + public void TryPeek_ThrowsWhenQueueNull() + { + Queue queue = null; + Assert.Throws(() => queue.TryPeek(out int _)); + } + + [Test, LuceneNetSpecific] + public void TryPeek_QueueEmpty() + { + Queue queue = new Queue(); + bool found = queue.TryPeek(out int result); + Assert.AreEqual(found, false); + Assert.AreEqual(result, default(int)); + } + + [Test, LuceneNetSpecific] + public void TryPeek_QueueNotEmpty() + { + Queue queue = new Queue(); + int item = 1; + queue.Enqueue(item); + int countBefore = queue.Count; + bool found = queue.TryPeek(out int result); + Assert.AreEqual(found, true); + Assert.AreEqual(result, item); + Assert.AreEqual(queue.Count, countBefore); + } +#endif + } +} diff --git a/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs b/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs index b26588b5ac..7986cf99a2 100644 --- a/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs +++ b/src/Lucene.Net/Index/DocumentsWriterFlushControl.cs @@ -1,6 +1,7 @@ using J2N.Runtime.CompilerServices; using J2N.Threading.Atomic; using Lucene.Net.Diagnostics; +using Lucene.Net.Support; using Lucene.Net.Support.Threading; using Lucene.Net.Util; using System; @@ -501,8 +502,7 @@ internal DocumentsWriterPerThread NextPendingFlush() UninterruptableMonitor.Enter(this); try { - DocumentsWriterPerThread poll; - if (flushQueue.Count > 0 && (poll = flushQueue.Dequeue()) != null) + if (flushQueue.TryDequeue(out DocumentsWriterPerThread poll)) { UpdateStallState(); return poll; @@ -641,7 +641,7 @@ internal int NumFlushingDWPT } } - public bool GetAndResetApplyAllDeletes() + public bool GetAndResetApplyAllDeletes() { return flushDeletes.GetAndSet(false); } @@ -688,7 +688,7 @@ internal void MarkForFullFlush() if (Debugging.AssertsEnabled) { Debugging.Assert(!fullFlush, "called DWFC#markForFullFlush() while full flush is still running"); - Debugging.Assert(fullFlushBuffer.Count == 0,"full flush buffer should be empty: {0}", fullFlushBuffer); + Debugging.Assert(fullFlushBuffer.Count == 0, "full flush buffer should be empty: {0}", fullFlushBuffer); } fullFlush = true; flushingQueue = documentsWriter.deleteQueue; @@ -765,7 +765,7 @@ private bool AssertActiveDeleteQueue(DocumentsWriterDeleteQueue queue) next.@Lock(); try { - if (Debugging.AssertsEnabled) Debugging.Assert(!next.IsInitialized || next.dwpt.deleteQueue == queue,"isInitialized: {0} numDocs: {1}", next.IsInitialized, (next.IsInitialized ? next.dwpt.NumDocsInRAM : 0)); + if (Debugging.AssertsEnabled) Debugging.Assert(!next.IsInitialized || next.dwpt.deleteQueue == queue, "isInitialized: {0} numDocs: {1}", next.IsInitialized, (next.IsInitialized ? next.dwpt.NumDocsInRAM : 0)); } finally { diff --git a/src/Lucene.Net/Index/DocumentsWriterFlushQueue.cs b/src/Lucene.Net/Index/DocumentsWriterFlushQueue.cs index 0d29d8a4f1..78770e1add 100644 --- a/src/Lucene.Net/Index/DocumentsWriterFlushQueue.cs +++ b/src/Lucene.Net/Index/DocumentsWriterFlushQueue.cs @@ -1,6 +1,7 @@ using J2N.Threading.Atomic; using Lucene.Net.Diagnostics; using Lucene.Net.Support.Threading; +using Lucene.Net.Support; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; @@ -142,7 +143,7 @@ internal virtual bool HasTickets { get { - if (Debugging.AssertsEnabled) Debugging.Assert(ticketCount >= 0,"ticketCount should be >= 0 but was: {0}", ticketCount); + if (Debugging.AssertsEnabled) Debugging.Assert(ticketCount >= 0, "ticketCount should be >= 0 but was: {0}", ticketCount); return ticketCount != 0; } } @@ -158,8 +159,7 @@ private int InnerPurge(IndexWriter writer) UninterruptableMonitor.Enter(this); try { - head = queue.Count <= 0 ? null : queue.Peek(); - canPublish = head != null && head.CanPublish; // do this synced + canPublish = queue.TryPeek(out head) && head.CanPublish; // do this synced } finally { diff --git a/src/Lucene.Net/Index/IndexWriter.cs b/src/Lucene.Net/Index/IndexWriter.cs index 48e651ea92..23c93846ea 100644 --- a/src/Lucene.Net/Index/IndexWriter.cs +++ b/src/Lucene.Net/Index/IndexWriter.cs @@ -2685,7 +2685,7 @@ public virtual ICollection MergingSegments /// /// @lucene.experimental /// - public virtual MergePolicy.OneMerge NextMerge() + public virtual MergePolicy.OneMerge NextMerge() // LUCENENET TODO: API - Revert name to GetNextMerge() to match Java { UninterruptableMonitor.Enter(this); try @@ -3382,7 +3382,7 @@ public virtual void AddIndexes(params Directory[] dirs) JCG.HashSet copiedFiles = new JCG.HashSet(); foreach (SegmentCommitInfo info in sis.Segments) { - if (Debugging.AssertsEnabled) Debugging.Assert(!infos.Contains(info),"dup info dir={0} name={1}", info.Info.Dir, info.Info.Name); + if (Debugging.AssertsEnabled) Debugging.Assert(!infos.Contains(info), "dup info dir={0} name={1}", info.Info.Dir, info.Info.Name); string newSegName = NewSegmentName(); @@ -4439,7 +4439,7 @@ private static void SkipDeletedDoc(DocValuesFieldUpdatesIterator[] updatesIters, // when entering the method, all iterators must already be beyond the // deleted document, or right on it, in which case we advance them over // and they must be beyond it now. - if (Debugging.AssertsEnabled) Debugging.Assert(iter.Doc > deletedDoc,"updateDoc={0} deletedDoc={1}", iter.Doc, deletedDoc); + if (Debugging.AssertsEnabled) Debugging.Assert(iter.Doc > deletedDoc, "updateDoc={0} deletedDoc={1}", iter.Doc, deletedDoc); } } @@ -5986,7 +5986,7 @@ private void StartCommit(SegmentInfos toSync) UninterruptableMonitor.Enter(this); try { - if (Debugging.AssertsEnabled) Debugging.Assert(lastCommitChangeCount <= changeCount,"lastCommitChangeCount={0} changeCount={1}", lastCommitChangeCount, changeCount); + if (Debugging.AssertsEnabled) Debugging.Assert(lastCommitChangeCount <= changeCount, "lastCommitChangeCount={0} changeCount={1}", lastCommitChangeCount, changeCount); if (pendingCommitChangeCount == lastCommitChangeCount) { diff --git a/src/Lucene.Net/Search/NumericRangeQuery.cs b/src/Lucene.Net/Search/NumericRangeQuery.cs index baa59a2c15..28697939ce 100644 --- a/src/Lucene.Net/Search/NumericRangeQuery.cs +++ b/src/Lucene.Net/Search/NumericRangeQuery.cs @@ -1,6 +1,7 @@ using Lucene.Net.Diagnostics; using Lucene.Net.Documents; using Lucene.Net.Util; +using Lucene.Net.Support; using System; using System.Collections.Generic; using System.Text; @@ -35,10 +36,10 @@ namespace Lucene.Net.Search /// /// A that matches numeric values within a /// specified range. To use this, you must first index the - /// numeric values using , - /// , or (expert: + /// numeric values using , + /// , or (expert: /// ). If your terms are instead textual, - /// you should use . + /// you should use . /// is the filter equivalent of this /// query. /// @@ -68,7 +69,7 @@ namespace Lucene.Net.Search /// classes. See below for /// details. /// - /// This query defaults to + /// This query defaults to /// . /// With precision steps of <=4, this query can be run with /// one of the rewrite methods without changing @@ -626,4 +627,4 @@ public static NumericRangeQuery NewSingleRange(string field, float? min, return new NumericRangeQuery(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.SINGLE, min, max, minInclusive, maxInclusive); } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net/Support/QueueExtensions.cs b/src/Lucene.Net/Support/QueueExtensions.cs new file mode 100644 index 0000000000..bef4cb80ef --- /dev/null +++ b/src/Lucene.Net/Support/QueueExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +#nullable enable + +namespace Lucene.Net.Support +{ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /// + /// Extensions to + /// + internal static class QueueExtensions + { +#if !FEATURE_QUEUE_TRYDEQUEUE_TRYPEEK + /// + /// Removes the object at the beginning of the , + /// and copies it to the parameter. + /// + /// The type of element in the + /// The to be checked + /// The removed object + /// true if the object was successfully removed; false if the is empty. + /// is null. + public static bool TryDequeue(this Queue queue, [MaybeNullWhen(false)] out T result) + { + if (queue is null) + throw new ArgumentNullException(nameof(queue)); + + if (queue.Count > 0) + { + result = queue.Dequeue(); + return true; + } + + result = default; + return false; + } + + /// + /// Returns a value that indicates whether there is an object at the beginning of the , + /// and if one is present, copies it to the parameter. The object is not removed from the . + /// + /// The type of element in the + /// The to be checked + /// If present, the object at the beginning of the ; otherwise, the default value of + /// true if there is an object at the beginning of the ; false if the is empty. + /// is null. + public static bool TryPeek(this Queue queue, [MaybeNullWhen(false)] out T result) + { + if (queue is null) + throw new ArgumentNullException(nameof(queue)); + + if (queue.Count > 0) + { + result = queue.Peek(); + return true; + } + + result = default; + return false; + } +#endif + } +} diff --git a/src/Lucene.Net/Util/Automaton/Automaton.cs b/src/Lucene.Net/Util/Automaton/Automaton.cs index e275c28078..784cf24499 100644 --- a/src/Lucene.Net/Util/Automaton/Automaton.cs +++ b/src/Lucene.Net/Util/Automaton/Automaton.cs @@ -264,9 +264,8 @@ public virtual State[] GetNumberedStates() initial.number = upto; states[upto] = initial; upto++; - while (worklist.Count > 0) + while (worklist.TryDequeue(out State s)) { - State s = worklist.Dequeue(); for (int i = 0; i < s.numTransitions; i++) { Transition t = s.TransitionsArray[i]; @@ -336,9 +335,8 @@ public virtual ISet GetAcceptStates() Queue worklist = new Queue(); // LUCENENET specific - Queue is much more performant than LinkedList worklist.Enqueue(initial); visited.Add(initial); - while (worklist.Count > 0) + while (worklist.TryDequeue(out State s)) { - State s = worklist.Dequeue(); if (s.accept) { accepts.Add(s); @@ -626,10 +624,10 @@ public override bool Equals(object obj) //throw UnsupportedOperationException.Create("use BasicOperations.sameLanguage instead"); } - // LUCENENET specific - in .NET, we can't simply throw an exception here because + // LUCENENET specific - in .NET, we can't simply throw an exception here because // collections use this to determine equality. Most of this code was pieced together from // BasicOperations.SubSetOf (which, when done both ways determines equality). - public override int GetHashCode() + public override int GetHashCode() { if (IsSingleton) { @@ -644,12 +642,10 @@ public override int GetHashCode() Queue worklist = new Queue(); // LUCENENET specific - Queue is much more performant than LinkedList JCG.HashSet visited = new JCG.HashSet(); - State current; worklist.Enqueue(this.initial); visited.Add(this.initial); - while (worklist.Count > 0) + while (worklist.TryDequeue(out State current)) { - current = worklist.Dequeue(); hash = 31 * hash + current.accept.GetHashCode(); Transition[] t1 = transitions[current.number]; @@ -962,4 +958,4 @@ public static Automaton Minimize(Automaton a) return a; } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net/Util/Automaton/BasicOperations.cs b/src/Lucene.Net/Util/Automaton/BasicOperations.cs index fb6d7f409f..a2c6f0ea91 100644 --- a/src/Lucene.Net/Util/Automaton/BasicOperations.cs +++ b/src/Lucene.Net/Util/Automaton/BasicOperations.cs @@ -1,5 +1,6 @@ using J2N; using Lucene.Net.Diagnostics; +using Lucene.Net.Support; using System; using System.Collections.Generic; using System.Linq; @@ -404,9 +405,8 @@ public static Automaton Intersection(Automaton a1, Automaton a2) StatePair p = new StatePair(c.initial, a1.initial, a2.initial); worklist.Enqueue(p); newstates[p] = p; - while (worklist.Count > 0) + while (worklist.TryDequeue(out p)) { - p = worklist.Dequeue(); p.s.accept = p.s1.accept && p.s2.accept; Transition[] t1 = transitions1[p.s1.number]; Transition[] t2 = transitions2[p.s2.number]; @@ -497,9 +497,8 @@ public static bool SubsetOf(Automaton a1, Automaton a2) StatePair p = new StatePair(a1.initial, a2.initial); worklist.Enqueue(p); visited.Add(p); - while (worklist.Count > 0) + while (worklist.TryDequeue(out p)) { - p = worklist.Dequeue(); if (p.s1.accept && !p.s2.accept) { return false; @@ -822,9 +821,8 @@ public static void Determinize(Automaton a) // like SortedMap SortedInt32Set statesSet = new SortedInt32Set(5); - while (worklist.Count > 0) + while (worklist.TryDequeue(out SortedInt32Set.FrozenInt32Set s)) { - SortedInt32Set.FrozenInt32Set s = worklist.Dequeue(); //worklist.Remove(s); // Collate all outgoing transitions by min/1+max: @@ -912,7 +910,7 @@ public static void Determinize(Automaton a) points.points[i].starts.count = 0; } points.Reset(); - if (Debugging.AssertsEnabled) Debugging.Assert(statesSet.upto == 0,"upto={0}", statesSet.upto); + if (Debugging.AssertsEnabled) Debugging.Assert(statesSet.upto == 0, "upto={0}", statesSet.upto); } a.deterministic = true; a.SetNumberedStates(newStatesArray, newStateUpto); @@ -1117,4 +1115,4 @@ public static bool Run(Automaton a, string s) } } } -} \ No newline at end of file +} diff --git a/src/Lucene.Net/Util/Automaton/MinimizationOperations.cs b/src/Lucene.Net/Util/Automaton/MinimizationOperations.cs index 630303e309..6cb382c3fe 100644 --- a/src/Lucene.Net/Util/Automaton/MinimizationOperations.cs +++ b/src/Lucene.Net/Util/Automaton/MinimizationOperations.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using JCG = J2N.Collections.Generic; +using Lucene.Net.Support; /* * dk.brics.automaton @@ -85,7 +86,7 @@ public static void MinimizeHopcroft(Automaton a) StateListNode[,] active2 = new StateListNode[statesLen, sigmaLen]; Queue pending = new Queue(); // LUCENENET specific - Queue is much more performant than LinkedList OpenBitSet pending2 = new OpenBitSet(sigmaLen * statesLen); - OpenBitSet split = new OpenBitSet(statesLen), + OpenBitSet split = new OpenBitSet(statesLen), refine = new OpenBitSet(statesLen), refine2 = new OpenBitSet(statesLen); for (int q = 0; q < statesLen; q++) { @@ -137,9 +138,8 @@ public static void MinimizeHopcroft(Automaton a) } // process pending until fixed point int k = 2; - while (pending.Count > 0) + while (pending.TryDequeue(out Int32Pair ip)) { - Int32Pair ip = pending.Dequeue(); int p = ip.n1; int x = ip.n2; pending2.Clear(x * statesLen + p); @@ -323,4 +323,4 @@ internal void Remove() } } } -} \ No newline at end of file +}