perf(acl): O(n) browse/translate filtering compaction
This commit is contained in:
@@ -2943,22 +2943,36 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
base.Browse(context, ref continuationPoint, references);
|
||||
var returned = references.Count - before;
|
||||
|
||||
// Forward two-pointer compaction over the appended range, NOT a per-denial RemoveAt. Removing
|
||||
// from the middle shifts every later element down one slot, so denying a large prefix of a wide
|
||||
// browse costs O(kept × removed) element moves; copying each survivor down to a write cursor and
|
||||
// trimming the tail afterwards is one pass. The tail trim removes from the END, where RemoveAt
|
||||
// moves nothing.
|
||||
var filtered = 0;
|
||||
for (var ii = references.Count - 1; ii >= before; ii--)
|
||||
var write = before;
|
||||
for (var ii = before; ii < references.Count; ii++)
|
||||
{
|
||||
// ExpandedNodeId → NodeId only makes sense for a reference into our own server; a reference
|
||||
// to another server's node is left alone, as this manager has no grants that could speak to it.
|
||||
var target = references[ii].NodeId;
|
||||
if (target is null || target.IsAbsolute) continue;
|
||||
var keep = target is null
|
||||
|| target.IsAbsolute
|
||||
|| ExpandedNodeId.ToNodeId(target, Server.NamespaceUris) is not { } targetId
|
||||
|| !OwnsNamespace(targetId)
|
||||
|| Permits(identity, OpcUaOperation.Browse, targetId);
|
||||
|
||||
var targetId = ExpandedNodeId.ToNodeId(target, Server.NamespaceUris);
|
||||
if (targetId is null || !OwnsNamespace(targetId)) continue;
|
||||
if (Permits(identity, OpcUaOperation.Browse, targetId)) continue;
|
||||
if (!keep)
|
||||
{
|
||||
filtered++;
|
||||
continue;
|
||||
}
|
||||
|
||||
references.RemoveAt(ii);
|
||||
filtered++;
|
||||
if (write != ii) references[write] = references[ii];
|
||||
write++;
|
||||
}
|
||||
|
||||
for (var ii = references.Count - 1; ii >= write; ii--) references.RemoveAt(ii);
|
||||
|
||||
LogAclFiltering("Browse", identity, filtered, returned);
|
||||
}
|
||||
|
||||
@@ -2995,14 +3009,24 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
||||
var before = targetIds.Count;
|
||||
base.TranslateBrowsePath(context, sourceHandle, relativePath, targetIds, unresolvedTargetIds);
|
||||
|
||||
for (var ii = targetIds.Count - 1; ii >= before; ii--)
|
||||
// Forward two-pointer compaction over the appended range — same reasoning as Browse above: a
|
||||
// mid-list RemoveAt per denial is O(kept × removed) element moves, one compacting pass plus a
|
||||
// tail trim is O(n).
|
||||
var write = before;
|
||||
for (var ii = before; ii < targetIds.Count; ii++)
|
||||
{
|
||||
var targetId = ExpandedNodeId.ToNodeId(targetIds[ii], Server.NamespaceUris);
|
||||
if (targetId is null || !OwnsNamespace(targetId)) continue;
|
||||
if (Permits(identity, OpcUaOperation.Browse, targetId)) continue;
|
||||
var keep = targetId is null
|
||||
|| !OwnsNamespace(targetId)
|
||||
|| Permits(identity, OpcUaOperation.Browse, targetId);
|
||||
|
||||
targetIds.RemoveAt(ii);
|
||||
if (!keep) continue;
|
||||
|
||||
if (write != ii) targetIds[write] = targetIds[ii];
|
||||
write++;
|
||||
}
|
||||
|
||||
for (var ii = targetIds.Count - 1; ii >= write; ii--) targetIds.RemoveAt(ii);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user