From a0bc03078f49d50714e0df85fe35eddd4671d259 Mon Sep 17 00:00:00 2001 From: HarelM Date: Tue, 12 Aug 2025 15:08:48 +0300 Subject: [PATCH] Fix circular reference of relations which causes a stack overflow. --- src/OsmSharp/Complete/Extensions.cs | 22 ++++++++++++++++--- .../OsmSimpleCompleteStreamSourceTests.cs | 22 +++++++++++++++++++ .../data/xml/circular-relation.osm | 11 ++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 test/OsmSharp.Test/Stream/OsmSimpleCompleteStreamSourceTests.cs create mode 100644 test/OsmSharp.Test/data/xml/circular-relation.osm diff --git a/src/OsmSharp/Complete/Extensions.cs b/src/OsmSharp/Complete/Extensions.cs index b1d74719..876fb1bc 100755 --- a/src/OsmSharp/Complete/Extensions.cs +++ b/src/OsmSharp/Complete/Extensions.cs @@ -92,19 +92,35 @@ public static CompleteWay CreateComplete(this Way way, IOsmGeoSource osmGeoSourc /// Creates a complete relation. /// public static CompleteRelation CreateComplete(this Relation relation, IOsmGeoSource osmGeoSource) + { + return relation.CreateComplete(osmGeoSource, new Dictionary()); + } + + private static CompleteRelation CreateComplete(this Relation relation, IOsmGeoSource osmGeoSource, Dictionary createdRelations) { if (relation == null) throw new ArgumentNullException("relation"); if (relation.Id == null) throw new Exception("relation.Id is null"); if (osmGeoSource == null) throw new ArgumentNullException("osmGeoSource"); - var completeRelation = new CompleteRelation(); - completeRelation.Id = relation.Id.Value; + var relationId = relation.Id.Value; + + // If we've already created this relation, return it (handles circular references) + if (createdRelations.TryGetValue(relationId, out var existingRelation)) + { + return existingRelation; + } + // Create the complete relation and add it to the cache immediately + var completeRelation = new CompleteRelation(); + completeRelation.Id = relationId; completeRelation.ChangeSetId = relation.ChangeSetId; if (relation.Tags != null) { completeRelation.Tags = new TagsCollection(relation.Tags); } + // Add to cache before processing members to handle circular references + createdRelations[relationId] = completeRelation; + if (relation.Members != null) { var relationMembers = new List(); @@ -156,7 +172,7 @@ public static CompleteRelation CreateComplete(this Relation relation, IOsmGeoSou { continue; } - var completeMemberRelation = relationMember.CreateComplete(osmGeoSource); + var completeMemberRelation = relationMember.CreateComplete(osmGeoSource, createdRelations); if (completeMemberRelation != null) { member.Member = completeMemberRelation; diff --git a/test/OsmSharp.Test/Stream/OsmSimpleCompleteStreamSourceTests.cs b/test/OsmSharp.Test/Stream/OsmSimpleCompleteStreamSourceTests.cs new file mode 100644 index 00000000..b97bc9ce --- /dev/null +++ b/test/OsmSharp.Test/Stream/OsmSimpleCompleteStreamSourceTests.cs @@ -0,0 +1,22 @@ +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using OsmSharp.Complete; +using OsmSharp.Streams; +using OsmSharp.Streams.Complete; + +namespace OsmSharp.Test.Stream; + +[TestFixture] +public class OsmSimpleCompleteStreamSourceTests +{ + [Test] + public void SimpleComplete() + { + var streamSource = new XmlOsmStreamSource(Assembly.GetExecutingAssembly().GetManifestResourceStream( + "OsmSharp.Test.data.xml.circular-relation.osm")); + var completeSource = new OsmSimpleCompleteStreamSource(streamSource); + var element = completeSource.OfType().ToList(); + Assert.That(element.Count, Is.EqualTo(2)); + } +} \ No newline at end of file diff --git a/test/OsmSharp.Test/data/xml/circular-relation.osm b/test/OsmSharp.Test/data/xml/circular-relation.osm new file mode 100644 index 00000000..5595b7b9 --- /dev/null +++ b/test/OsmSharp.Test/data/xml/circular-relation.osm @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file