From 8fd145c77ebafb746a570e0c08b21d3d0dfcb77d Mon Sep 17 00:00:00 2001 From: Jeremy Powell Date: Sun, 6 Oct 2024 23:31:42 +1300 Subject: [PATCH 1/2] Use a Stack to enumerate RBTree Avoid recursion and reduce allocations --- sources/OpenMcdf/RBTree/RBTreeEnumerator.cs | 32 +++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs b/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs index 7d23ec63..478757d6 100644 --- a/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs +++ b/sources/OpenMcdf/RBTree/RBTreeEnumerator.cs @@ -8,31 +8,47 @@ public partial class RBTree // TODO: Make internal in v3 (can seal in v2 since constructor is internal) public sealed class RBTreeEnumerator : IEnumerator { - private readonly List list = new(); - int position = -1; + private readonly IRBNode root; + private readonly Stack stack = new(); internal RBTreeEnumerator(RBTree tree) { - tree.VisitTree(item => list.Add(item)); + root = tree.Root; + PushLeft(root); } public void Dispose() { } - public IRBNode Current => list[position]; + public IRBNode Current { get; private set; } - object IEnumerator.Current => list[position]; + object IEnumerator.Current => Current; public bool MoveNext() { - position++; - return position < list.Count; + if (stack.Count == 0) + return false; + + Current = stack.Pop(); + PushLeft(Current.Right); + return true; } public void Reset() { - position = -1; + Current = null; + stack.Clear(); + PushLeft(root); + } + + private void PushLeft(IRBNode node) + { + while (node is not null) + { + stack.Push(node); + node = node.Left; + } } } } From 8960aa276f0fb777467177a1fbebab796020ec81 Mon Sep 17 00:00:00 2001 From: Jeremy Powell Date: Sun, 6 Oct 2024 23:51:36 +1300 Subject: [PATCH 2/2] Use a Stack for VisitEntries Avoid recursion and multiple List allocations --- sources/OpenMcdf/CFStorage.cs | 37 ++++++++++++----------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/sources/OpenMcdf/CFStorage.cs b/sources/OpenMcdf/CFStorage.cs index 0a238fc7..fa2b3908 100644 --- a/sources/OpenMcdf/CFStorage.cs +++ b/sources/OpenMcdf/CFStorage.cs @@ -423,34 +423,23 @@ public void VisitEntries(Action action, bool recursive) { CheckDisposed(); - if (action != null) - { - List subStorages - = new List(); - - Action internalAction = - delegate (IRBNode targetNode) - { - IDirectoryEntry d = targetNode as IDirectoryEntry; - if (d.StgType == StgType.StgStream) - action(new CFStream(CompoundFile, d)); - else - action(new CFStorage(CompoundFile, d)); + if (action is null) + return; // TODO: Reorder and throw ArgumentNullException in v3 - if (d.Child != DirectoryEntry.NOSTREAM) - subStorages.Add(targetNode); + Stack stack = new(); + stack.Push(this); - return; - }; - - Children.VisitTree(internalAction); - - if (recursive && subStorages.Count > 0) + while (stack.Count > 0) + { + CFItem current = stack.Pop(); + if (current is CFStorage storage) { - foreach (IRBNode n in subStorages) + foreach (IDirectoryEntry de in storage.Children.Cast()) { - IDirectoryEntry d = n as IDirectoryEntry; - new CFStorage(CompoundFile, d).VisitEntries(action, recursive); + CFItem item = de.StgType == StgType.StgStream ? new CFStream(CompoundFile, de) : new CFStorage(CompoundFile, de); + action(item); + if (recursive) + stack.Push(item); } } }