pub(crate) fn reserve_graph_items<T>(
buffer: &mut Vec<T>,
additional: usize,
owner: &str,
context: &str,
) -> Result<(), String> {
buffer.try_reserve(additional).map_err(|error| {
format!(
"Fix: {owner} could not reserve {additional} item(s) for {context}: {error}. Split the graph batch or reuse a smaller scratch buffer."
)
})
}
pub(crate) fn reserve_graph_capacity<T>(
buffer: &mut Vec<T>,
len: usize,
owner: &str,
context: &str,
) -> Result<(), String> {
if len > buffer.capacity() {
reserve_graph_items(buffer, len - buffer.len(), owner, context)?;
}
Ok(())
}
#[cfg(feature = "math")]
macro_rules! define_reserve_graph_capacity {
($name:ident, $item:ty, $owner:literal) => {
#[cfg(any(test, feature = "cpu-parity"))]
fn $name(out: &mut Vec<$item>, len: usize, name: &str) -> Result<(), String> {
crate::graph::scratch::reserve_graph_capacity(out, len, $owner, name)
}
};
}
#[cfg(feature = "math")]
pub(crate) use define_reserve_graph_capacity;
pub(crate) fn reserve_graph_items_with<T, E>(
buffer: &mut Vec<T>,
additional: usize,
owner: &str,
context: &str,
map: impl FnOnce(String) -> E,
) -> Result<(), E> {
reserve_graph_items(buffer, additional, owner, context).map_err(map)
}
pub(crate) fn resize_graph_vec<T: Clone>(
buffer: &mut Vec<T>,
len: usize,
value: T,
owner: &str,
context: &str,
) -> Result<(), String> {
if len > buffer.len() {
reserve_graph_items(buffer, len - buffer.len(), owner, context)?;
}
buffer.resize(len, value);
Ok(())
}
#[cfg(test)]
mod tests {
use super::reserve_graph_items;
#[test]
fn reserve_graph_items_reuses_existing_capacity() {
let mut scratch = Vec::<u32>::with_capacity(8);
reserve_graph_items(&mut scratch, 4, "test graph primitive", "frontier")
.expect("Fix: replace expect with fallible API or document caller precondition; panic only on programmer error - existing capacity should satisfy reservation");
assert_eq!(scratch.capacity(), 8);
assert!(scratch.is_empty());
}
#[test]
fn reserve_graph_items_reports_owner_and_context_on_capacity_overflow() {
let mut scratch = Vec::<u8>::new();
let err = reserve_graph_items(
&mut scratch,
usize::MAX,
"test graph primitive",
"adversarial huge scratch",
)
.expect_err("usize::MAX reservation must fail without allocating");
assert!(err.contains("test graph primitive"));
assert!(err.contains("adversarial huge scratch"));
assert!(err.contains("usize::MAX") || err.contains("capacity"));
}
#[test]
fn reserve_graph_items_with_preserves_domain_error_mapping() {
#[derive(Debug, PartialEq, Eq)]
struct DomainError(String);
let mut scratch = Vec::<u8>::new();
let err = super::reserve_graph_items_with(
&mut scratch,
usize::MAX,
"mapped graph primitive",
"mapped scratch",
DomainError,
)
.expect_err("usize::MAX reservation must fail without allocating");
assert!(err.0.contains("mapped graph primitive"));
assert!(err.0.contains("mapped scratch"));
}
}