From b4374174a9845ad4abd73d03a27630df56c6388c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Fri, 7 Aug 2026 19:01:03 +0200 Subject: [PATCH] Add reserve_vertices and reserve_edges to GraphTopologyAddition --- src/models/append_graph.rs | 8 +++ src/models/graph.rs | 8 +++ src/testing/graph_topology_testing.rs | 85 ++++++++++++++++++++++++++- src/traits.rs | 15 ++++- 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs index ac637ea..9526f95 100644 --- a/src/models/append_graph.rs +++ b/src/models/append_graph.rs @@ -212,6 +212,14 @@ impl GraphTopologyAddition for AppendGraph { self.incidences.capacity() / 2 } + fn reserve_vertices(&mut self, additional: usize) { + self.vertices.reserve(additional); + } + + fn reserve_edges(&mut self, additional: usize) { + self.incidences.reserve(additional * 2); + } + fn add_vertex(&mut self) -> Self::Vertex { self.vertices.push(VertexIncidenceHeader { incidence_count: 0, diff --git a/src/models/graph.rs b/src/models/graph.rs index e584cf8..702d244 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -317,6 +317,14 @@ impl GraphTopologyAddition for Graph { self.incidences.capacity() / 2 } + fn reserve_vertices(&mut self, additional: usize) { + self.vertices.reserve(additional); + } + + fn reserve_edges(&mut self, additional: usize) { + self.incidences.reserve(additional * 2); + } + fn add_vertex(&mut self) -> Self::Vertex { self.vertices.insert(VertexIncidenceHeader { incidence_count: 0, diff --git a/src/testing/graph_topology_testing.rs b/src/testing/graph_topology_testing.rs index f7ea567..47d6569 100644 --- a/src/testing/graph_topology_testing.rs +++ b/src/testing/graph_topology_testing.rs @@ -852,7 +852,6 @@ macro_rules! graph_topology_tests { #[test] fn incidence_cursor_copy() { use $crate::traits::{GraphTopology, IncidenceCursor}; - // Constructs a graph with two vertices connected to `v`. let mut graph = <$T>::new(); let v = graph.add_vertex(); for _ in 0..2 { @@ -909,6 +908,90 @@ macro_rules! graph_topology_tests { ); } } + + #[test] + fn reserve_vertices_increases_capacity() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + let capacity_before = graph.vertex_capacity(); + graph.reserve_vertices(capacity_before + 10); + assert!( + graph.vertex_capacity() > capacity_before, + "expected sufficient capacity increase after reserve" + ); + } + + #[test] + fn reserve_vertices_prevents_reallocation_on_add() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + graph.reserve_vertices(10); + let capacity_before = graph.vertex_capacity(); + for _ in 0..10 { + graph.add_vertex(); + } + assert_eq!( + graph.vertex_capacity(), + capacity_before, + "writes within reserved capacity should not reallocate" + ); + } + + #[test] + fn reserve_vertices_does_not_affect_edge_capacity() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + let capacity_before = graph.edge_capacity(); + graph.reserve_vertices(10); + assert_eq!( + graph.edge_capacity(), + capacity_before, + "reserve_vertices must not change edge capacity" + ); + } + + #[test] + fn reserve_edges_increases_capacity() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + let capacity_before = graph.edge_capacity(); + graph.reserve_edges(capacity_before + 10); + assert!( + graph.edge_capacity() > capacity_before, + "expected sufficient capacity increase after reserve" + ); + } + + #[test] + fn reserve_edges_prevents_reallocation_on_add() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + graph.reserve_edges(10); + let capacity_before = graph.edge_capacity(); + for _ in 0..10 { + graph.add_edge(v1, v2); + } + assert_eq!( + graph.edge_capacity(), + capacity_before, + "writes within reserved capacity should not reallocate" + ); + } + + #[test] + fn reserve_edges_does_not_affect_vertex_capacity() { + use $crate::traits::GraphTopologyAddition; + let mut graph = <$T>::new(); + let capacity_before = graph.vertex_capacity(); + graph.reserve_edges(10); + assert_eq!( + graph.vertex_capacity(), + capacity_before, + "reserve_edges must not change vertex capacity" + ); + } }; } diff --git a/src/traits.rs b/src/traits.rs index f459cfa..ca525f8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -174,7 +174,6 @@ pub trait GraphTopology { fn incidence_cursor(&self, v: Self::Vertex) -> Self::IncidenceCursor; } -// TODO: Add functions to reserve memory for vertices and edges. /// A trait that adds construction operations to an undirected graph topology. /// /// This trait provides methods for addition of vertices and edges, and capacity management in an @@ -186,6 +185,20 @@ pub trait GraphTopologyAddition: GraphTopology { /// Returns the total number of edges the graph can hold without reallocating. fn edge_capacity(&self) -> usize; + /// Reserves capacity for at least `additional` more vertices. Does nothing if capacity is + /// already sufficient. + /// + /// Use this before adding multiple vertices to avoid incremental growth on each call to + /// [`add_vertex`]. + fn reserve_vertices(&mut self, additional: usize); + + /// Reserves capacity for at least `additional` more edges. Does nothing if capacity is already + /// sufficient. + /// + /// Use this before adding multiple edges to avoid incremental growth on each call to + /// [`add_edge`]. + fn reserve_edges(&mut self, additional: usize); + /// Adds a new isolated vertex and returns its handle. fn add_vertex(&mut self) -> Self::Vertex;