Skip to content

Commit 600c8d0

Browse files
konardclaude
andcommitted
Add protection from recursion in sequence elements
- Added HashSet<TLinkAddress> to track visited links in SequenceWalkerBase and LeveledSequenceWalker - Modified Walk() method in SequenceWalkerBase to check for already visited links before processing - Modified ToArray() method in LeveledSequenceWalker to prevent infinite recursion in circular link structures - Implementation follows the pattern used in FormatStructure method for recursion protection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 51246e4 commit 600c8d0

File tree

2 files changed

+48
-12
lines changed

2 files changed

+48
-12
lines changed

csharp/Platform.Data.Doublets.Sequences/Walkers/LeveledSequenceWalker.cs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace Platform.Data.Doublets.Sequences.Walkers
2323
public class LeveledSequenceWalker<TLinkAddress> : LinksOperatorBase<TLinkAddress>, ISequenceWalker<TLinkAddress> where TLinkAddress : struct, IUnsignedNumber<TLinkAddress>, IComparisonOperators<TLinkAddress, TLinkAddress, bool>
2424
{
2525
private readonly Func<TLinkAddress, bool> _isElement;
26+
private readonly HashSet<TLinkAddress> _visited;
2627

2728
/// <summary>
2829
/// <para>
@@ -39,7 +40,11 @@ public class LeveledSequenceWalker<TLinkAddress> : LinksOperatorBase<TLinkAddres
3940
/// <para></para>
4041
/// </param>
4142
[MethodImpl(MethodImplOptions.AggressiveInlining)]
42-
public LeveledSequenceWalker(ILinks<TLinkAddress> links, Func<TLinkAddress, bool> isElement) : base(links) => _isElement = isElement;
43+
public LeveledSequenceWalker(ILinks<TLinkAddress> links, Func<TLinkAddress, bool> isElement) : base(links)
44+
{
45+
_isElement = isElement;
46+
_visited = new HashSet<TLinkAddress>();
47+
}
4348

4449
/// <summary>
4550
/// <para>
@@ -52,7 +57,11 @@ public class LeveledSequenceWalker<TLinkAddress> : LinksOperatorBase<TLinkAddres
5257
/// <para></para>
5358
/// </param>
5459
[MethodImpl(MethodImplOptions.AggressiveInlining)]
55-
public LeveledSequenceWalker(ILinks<TLinkAddress> links) : base(links) => _isElement = _links.IsPartialPoint;
60+
public LeveledSequenceWalker(ILinks<TLinkAddress> links) : base(links)
61+
{
62+
_isElement = _links.IsPartialPoint;
63+
_visited = new HashSet<TLinkAddress>();
64+
}
5665

5766
/// <summary>
5867
/// <para>
@@ -88,6 +97,7 @@ public class LeveledSequenceWalker<TLinkAddress> : LinksOperatorBase<TLinkAddres
8897
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8998
public TLinkAddress[] ToArray(TLinkAddress sequence)
9099
{
100+
_visited.Clear();
91101
var length = 1;
92102
var array = new TLinkAddress[length];
93103
array[0] = sequence;
@@ -119,15 +129,22 @@ public TLinkAddress[] ToArray(TLinkAddress sequence)
119129
}
120130
else
121131
{
122-
var links = _links;
123-
var link = links.GetLink(candidate);
124-
var linkSource = links.GetSource(link);
125-
var linkTarget = links.GetTarget(link);
126-
nextArray[doubletOffset] = linkSource;
127-
nextArray[doubletOffset + 1] = linkTarget;
128-
if (!hasElements)
132+
if (_visited.Add(candidate))
133+
{
134+
var links = _links;
135+
var link = links.GetLink(candidate);
136+
var linkSource = links.GetSource(link);
137+
var linkTarget = links.GetTarget(link);
138+
nextArray[doubletOffset] = linkSource;
139+
nextArray[doubletOffset + 1] = linkTarget;
140+
if (!hasElements)
141+
{
142+
hasElements = !(_isElement(linkSource) && _isElement(linkTarget));
143+
}
144+
}
145+
else
129146
{
130-
hasElements = !(_isElement(linkSource) && _isElement(linkTarget));
147+
nextArray[doubletOffset] = candidate;
131148
}
132149
}
133150
}

csharp/Platform.Data.Doublets.Sequences/Walkers/SequenceWalkerBase.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public abstract class SequenceWalkerBase<TLinkAddress> : LinksOperatorBase<TLink
2020
{
2121
private readonly IStack<TLinkAddress> _stack;
2222
private readonly Func<TLinkAddress, bool> _isElement;
23+
private readonly HashSet<TLinkAddress> _visited;
2324

2425
/// <summary>
2526
/// <para>
@@ -44,6 +45,7 @@ protected SequenceWalkerBase(ILinks<TLinkAddress> links, IStack<TLinkAddress> st
4445
{
4546
_stack = stack;
4647
_isElement = isElement;
48+
_visited = new HashSet<TLinkAddress>();
4749
}
4850

4951
/// <summary>
@@ -81,6 +83,7 @@ protected SequenceWalkerBase(ILinks<TLinkAddress> links, IStack<TLinkAddress> st
8183
public IEnumerable<TLinkAddress> Walk(TLinkAddress sequence)
8284
{
8385
_stack.Clear();
86+
_visited.Clear();
8487
var element = sequence;
8588
if (IsElement(element))
8689
{
@@ -105,8 +108,24 @@ public IEnumerable<TLinkAddress> Walk(TLinkAddress sequence)
105108
}
106109
else
107110
{
108-
_stack.Push(element);
109-
element = GetNextElementAfterPush(element);
111+
if (_visited.Add(element))
112+
{
113+
_stack.Push(element);
114+
element = GetNextElementAfterPush(element);
115+
}
116+
else
117+
{
118+
if (_stack.IsEmpty)
119+
{
120+
break;
121+
}
122+
element = _stack.Pop();
123+
foreach (var output in WalkContents(element))
124+
{
125+
yield return output;
126+
}
127+
element = GetNextElementAfterPop(element);
128+
}
110129
}
111130
}
112131
}

0 commit comments

Comments
 (0)