diff --git a/benches/benches/core/cmap2/basic_ops.rs b/benches/benches/core/cmap2/basic_ops.rs index 6fd0e6aaf..746ce668e 100644 --- a/benches/benches/core/cmap2/basic_ops.rs +++ b/benches/benches/core/cmap2/basic_ops.rs @@ -32,7 +32,10 @@ fn get_sparse_map(n_square: usize) -> CMap2 { map.remove_free_dart(5); // because of the way we built the map in the square_cmap2 function & the ID computation // policy, we can safely remove a vertex we know is defined - assert_eq!(map.remove_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); + assert_eq!( + map.force_remove_vertex(1).unwrap(), + Vertex2::from((0.0, 0.0)) + ); map } @@ -109,7 +112,7 @@ library_benchmark_group!( #[bench::medium(&mut get_map(64))] #[bench::large(&mut get_map(256))] fn read_vertex(map: &mut CMap2) -> Option> { - black_box(map.vertex(1)) + black_box(map.force_read_vertex(1)) } #[library_benchmark] @@ -117,7 +120,7 @@ fn read_vertex(map: &mut CMap2) -> Option> { #[bench::medium(&mut get_sparse_map(64))] #[bench::large(&mut get_sparse_map(256))] fn read_missing_vertex(map: &mut CMap2) -> Option> { - black_box(map.vertex(1)) + black_box(map.force_read_vertex(1)) } #[library_benchmark] @@ -125,7 +128,7 @@ fn read_missing_vertex(map: &mut CMap2) -> Option> #[bench::medium(&mut get_sparse_map(64))] #[bench::large(&mut get_sparse_map(256))] fn insert_vertex(map: &mut CMap2) { - map.insert_vertex(1, (0.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); black_box(map); } @@ -134,7 +137,7 @@ fn insert_vertex(map: &mut CMap2) { #[bench::medium(&mut get_map(64))] #[bench::large(&mut get_map(256))] fn replace_vertex(map: &mut CMap2) -> Option> { - black_box(map.replace_vertex(1, (0.0, 0.0))) + black_box(map.force_write_vertex(1, (0.0, 0.0))) } #[library_benchmark] @@ -142,7 +145,7 @@ fn replace_vertex(map: &mut CMap2) -> Option> { #[bench::medium(&mut get_sparse_map(64))] #[bench::large(&mut get_sparse_map(256))] fn set_vertex(map: &mut CMap2) -> Option> { - black_box(map.replace_vertex(1, (0.0, 0.0))) + black_box(map.force_write_vertex(1, (0.0, 0.0))) } #[library_benchmark] @@ -150,7 +153,7 @@ fn set_vertex(map: &mut CMap2) -> Option> { #[bench::medium(&mut get_map(64))] #[bench::large(&mut get_map(256))] fn remove_vertex(map: &mut CMap2) -> Option> { - black_box(map.remove_vertex(1)) + black_box(map.force_remove_vertex(1)) } #[library_benchmark] @@ -158,7 +161,7 @@ fn remove_vertex(map: &mut CMap2) -> Option> { #[bench::medium(&mut get_sparse_map(64))] #[bench::large(&mut get_sparse_map(256))] fn remove_missing_vertex(map: &mut CMap2) -> Option> { - black_box(map.remove_vertex(1)) + black_box(map.force_remove_vertex(1)) } library_benchmark_group!( diff --git a/benches/benches/core/cmap2/link_and_sew.rs b/benches/benches/core/cmap2/link_and_sew.rs index 818f2b023..3505a10eb 100644 --- a/benches/benches/core/cmap2/link_and_sew.rs +++ b/benches/benches/core/cmap2/link_and_sew.rs @@ -39,8 +39,8 @@ fn get_sew_map(n_square: usize) -> CMap2 { .n_darts(n_square.pow(2) * 4) .build() .unwrap(); - map.insert_vertex(4, (0.0, 0.0)); - map.insert_vertex(6, (1.0, 0.0)); + map.force_write_vertex(4, (0.0, 0.0)); + map.force_write_vertex(6, (1.0, 0.0)); map } diff --git a/honeycomb-core/src/attributes/collections.rs b/honeycomb-core/src/attributes/collections.rs index 870fc5acc..d16d58307 100644 --- a/honeycomb-core/src/attributes/collections.rs +++ b/honeycomb-core/src/attributes/collections.rs @@ -6,9 +6,12 @@ // ------ IMPORTS use super::{AttributeBind, AttributeStorage, AttributeUpdate, UnknownAttributeStorage}; -use crate::prelude::DartIdType; +use crate::{ + cmap::{CMapError, CMapResult}, + prelude::DartIdType, +}; use num_traits::ToPrimitive; -use stm::{atomically, StmError, TVar, Transaction}; +use stm::{atomically, StmResult, TVar, Transaction}; // ------ CONTENT @@ -27,113 +30,35 @@ use stm::{atomically, StmError, TVar, Transaction}; /// /// **This type is not meant to be used directly** but used along the [`AttributeBind`] trait. #[derive(Debug)] -pub struct AttrSparseVec { +pub struct AttrSparseVec { /// Inner storage. data: Vec>>, } #[doc(hidden)] -impl AttrSparseVec { - pub(crate) fn merge_core( - &self, - trans: &mut Transaction, - out: DartIdType, - lhs_inp: DartIdType, - rhs_inp: DartIdType, - ) -> Result<(), StmError> { - let new_v = match ( - self.data[lhs_inp as usize].read(trans)?, - self.data[rhs_inp as usize].read(trans)?, - ) { - (Some(v1), Some(v2)) => Some(AttributeUpdate::merge(v1, v2)), - (Some(v), None) | (None, Some(v)) => Some(AttributeUpdate::merge_incomplete(v)), - (None, None) => AttributeUpdate::merge_from_none(), - }; - if new_v.is_none() { - eprintln!("W: cannot merge two null attribute value"); - eprintln!(" setting new target value to `None`"); - } - self.data[rhs_inp as usize].write(trans, None)?; - self.data[lhs_inp as usize].write(trans, None)?; - self.data[out as usize].write(trans, new_v)?; - Ok(()) - } - - pub(crate) fn split_core( - &self, - trans: &mut Transaction, - lhs_out: DartIdType, - rhs_out: DartIdType, - inp: DartIdType, - ) -> Result<(), StmError> { - if let Some(val) = self.data[inp as usize].read(trans)? { - let (lhs_val, rhs_val) = AttributeUpdate::split(val); - self.data[inp as usize].write(trans, None)?; - self.data[lhs_out as usize].write(trans, Some(lhs_val))?; - self.data[rhs_out as usize].write(trans, Some(rhs_val))?; - } else { - eprintln!("W: cannot split attribute value (not found in storage)"); - eprintln!(" setting both new values to `None`"); - self.data[lhs_out as usize].write(trans, None)?; - self.data[rhs_out as usize].write(trans, None)?; - //self.data[inp as usize].store(None, Ordering::Release); - } - Ok(()) - } - - pub(crate) fn set_core( +impl AttrSparseVec { + fn write_core( &self, trans: &mut Transaction, id: &A::IdentifierType, val: A, - ) -> Result<(), StmError> { - self.data[id.to_usize().unwrap()].write(trans, Some(val))?; - Ok(()) - } - - pub(crate) fn insert_core( - &self, - trans: &mut Transaction, - id: &A::IdentifierType, - val: A, - ) -> Result<(), StmError> { - let tmp = self.data[id.to_usize().unwrap()].replace(trans, Some(val))?; - // assertion prevents the transaction from being validated, so the - // storage will be left unchanged before the crash - assert!(tmp.is_none()); - Ok(()) + ) -> StmResult> { + self.data[id.to_usize().unwrap()].replace(trans, Some(val)) } - pub(crate) fn get_core( - &self, - trans: &mut Transaction, - id: &A::IdentifierType, - ) -> Result, StmError> { + fn read_core(&self, trans: &mut Transaction, id: &A::IdentifierType) -> StmResult> { self.data[id.to_usize().unwrap()].read(trans) } - pub(crate) fn replace_core( - &self, - trans: &mut Transaction, - id: &A::IdentifierType, - val: A, - ) -> Result, StmError> { - self.data[id.to_usize().unwrap()].replace(trans, Some(val)) - } - - pub(crate) fn remove_core( - &self, - trans: &mut Transaction, - id: &A::IdentifierType, - ) -> Result, StmError> { + fn remove_core(&self, trans: &mut Transaction, id: &A::IdentifierType) -> StmResult> { self.data[id.to_usize().unwrap()].replace(trans, None) } } -unsafe impl Send for AttrSparseVec {} -unsafe impl Sync for AttrSparseVec {} +unsafe impl Send for AttrSparseVec {} +unsafe impl Sync for AttrSparseVec {} -impl UnknownAttributeStorage for AttrSparseVec { +impl UnknownAttributeStorage for AttrSparseVec { fn new(length: usize) -> Self where Self: Sized, @@ -154,102 +79,145 @@ impl UnknownAttributeStorage for Attr .count() } - fn merge(&self, out: DartIdType, lhs_inp: DartIdType, rhs_inp: DartIdType) { - atomically(|trans| self.merge_core(trans, out, lhs_inp, rhs_inp)); - } - - fn merge_transac( + fn merge( &self, trans: &mut Transaction, out: DartIdType, lhs_inp: DartIdType, rhs_inp: DartIdType, - ) -> Result<(), StmError> { - self.merge_core(trans, out, lhs_inp, rhs_inp) + ) -> StmResult<()> { + let new_v = match ( + self.data[lhs_inp as usize].read(trans)?, + self.data[rhs_inp as usize].read(trans)?, + ) { + (Some(v1), Some(v2)) => Some(AttributeUpdate::merge(v1, v2)), + (Some(v), None) | (None, Some(v)) => Some(AttributeUpdate::merge_incomplete(v)), + (None, None) => AttributeUpdate::merge_from_none(), + }; + if new_v.is_none() { + eprintln!("W: cannot merge two null attribute value"); + eprintln!(" setting new target value to `None`"); + } + self.data[rhs_inp as usize].write(trans, None)?; + self.data[lhs_inp as usize].write(trans, None)?; + self.data[out as usize].write(trans, new_v)?; + Ok(()) } - fn split(&self, lhs_out: DartIdType, rhs_out: DartIdType, inp: DartIdType) { - atomically(|trans| self.split_core(trans, lhs_out, rhs_out, inp)); + fn try_merge( + &self, + trans: &mut Transaction, + out: DartIdType, + lhs_inp: DartIdType, + rhs_inp: DartIdType, + ) -> CMapResult<()> { + let new_v = match ( + self.data[lhs_inp as usize].read(trans)?, + self.data[rhs_inp as usize].read(trans)?, + ) { + (Some(v1), Some(v2)) => Some(AttributeUpdate::merge(v1, v2)), + (Some(_), None) | (None, Some(_)) => { + return Err(CMapError::FailedAttributeMerge( + "missing one value for merge", + )) + } + (None, None) => { + return Err(CMapError::FailedAttributeMerge( + "missing both values for merge", + )) + } + }; + if new_v.is_none() { + eprintln!("W: cannot merge two null attribute value"); + eprintln!(" setting new target value to `None`"); + } + self.data[rhs_inp as usize].write(trans, None)?; + self.data[lhs_inp as usize].write(trans, None)?; + self.data[out as usize].write(trans, new_v)?; + Ok(()) } - fn split_transac( + fn split( &self, trans: &mut Transaction, lhs_out: DartIdType, rhs_out: DartIdType, inp: DartIdType, - ) -> Result<(), StmError> { - self.split_core(trans, lhs_out, rhs_out, inp) - } -} - -impl AttributeStorage for AttrSparseVec { - fn set(&self, id: A::IdentifierType, val: A) { - atomically(|trans| self.set_core(trans, &id, val)); + ) -> StmResult<()> { + if let Some(val) = self.data[inp as usize].read(trans)? { + let (lhs_val, rhs_val) = AttributeUpdate::split(val); + self.data[inp as usize].write(trans, None)?; + self.data[lhs_out as usize].write(trans, Some(lhs_val))?; + self.data[rhs_out as usize].write(trans, Some(rhs_val))?; + } else { + eprintln!("W: cannot split attribute value (not found in storage)"); + eprintln!(" setting both new values to `None`"); + self.data[lhs_out as usize].write(trans, None)?; + self.data[rhs_out as usize].write(trans, None)?; + } + Ok(()) } - fn set_transac( + fn try_split( &self, trans: &mut Transaction, - id: ::IdentifierType, - val: A, - ) -> Result<(), StmError> { - self.set_core(trans, &id, val) + lhs_out: DartIdType, + rhs_out: DartIdType, + inp: DartIdType, + ) -> CMapResult<()> { + if let Some(val) = self.data[inp as usize].read(trans)? { + let (lhs_val, rhs_val) = AttributeUpdate::split(val); + self.data[inp as usize].write(trans, None)?; + self.data[lhs_out as usize].write(trans, Some(lhs_val))?; + self.data[rhs_out as usize].write(trans, Some(rhs_val))?; + } else { + return Err(CMapError::FailedAttributeSplit("no value to split from")); + } + Ok(()) } +} - fn insert(&self, id: A::IdentifierType, val: A) { - atomically(|trans| self.insert_core(trans, &id, val)); +impl AttributeStorage for AttrSparseVec { + fn force_write(&self, id: ::IdentifierType, val: A) -> Option { + atomically(|trans| self.write_core(trans, &id, val)) } - fn insert_transac( + fn write( &self, trans: &mut Transaction, id: ::IdentifierType, val: A, - ) -> Result<(), StmError> { - self.insert_core(trans, &id, val) + ) -> StmResult> { + self.write_core(trans, &id, val) } - fn get(&self, id: A::IdentifierType) -> Option { - atomically(|trans| self.get_core(trans, &id)) + fn force_read(&self, id: ::IdentifierType) -> Option { + atomically(|trans| self.read_core(trans, &id)) } - fn get_transac( + fn read( &self, trans: &mut Transaction, id: ::IdentifierType, - ) -> Result, StmError> { - self.get_core(trans, &id) - } - - fn replace(&self, id: A::IdentifierType, val: A) -> Option { - atomically(|trans| self.replace_core(trans, &id, val)) - } - - fn replace_transac( - &self, - trans: &mut Transaction, - id: ::IdentifierType, - val: A, - ) -> Result, StmError> { - self.replace_core(trans, &id, val) + ) -> StmResult> { + self.read_core(trans, &id) } - fn remove(&self, id: A::IdentifierType) -> Option { + fn force_remove(&self, id: ::IdentifierType) -> Option { atomically(|trans| self.remove_core(trans, &id)) } - fn remove_transac( + fn remove( &self, trans: &mut Transaction, id: ::IdentifierType, - ) -> Result, StmError> { + ) -> StmResult> { self.remove_core(trans, &id) } } #[cfg(feature = "utils")] -impl AttrSparseVec { +impl AttrSparseVec { /// Return the amount of space allocated for the storage. #[must_use = "returned value is not used, consider removing this method call"] pub fn allocated_size(&self) -> usize { diff --git a/honeycomb-core/src/attributes/manager.rs b/honeycomb-core/src/attributes/manager.rs index 5f9d7cb83..21a70e91a 100644 --- a/honeycomb-core/src/attributes/manager.rs +++ b/honeycomb-core/src/attributes/manager.rs @@ -5,14 +5,47 @@ // ------ IMPORTS -use stm::{StmError, Transaction}; +use stm::{StmResult, Transaction}; -use super::{AttributeBind, AttributeStorage, UnknownAttributeStorage}; -use crate::prelude::{DartIdType, OrbitPolicy}; +use super::{AttributeBind, AttributeStorage, AttributeUpdate, UnknownAttributeStorage}; +use crate::{ + cmap::CMapResult, + prelude::{DartIdType, OrbitPolicy}, +}; use std::{any::TypeId, collections::HashMap}; // ------ CONTENT +// convenience macros + +macro_rules! get_storage { + ($slf: ident, $id: ident) => { + let probably_storage = match A::BIND_POLICY { + OrbitPolicy::Vertex => $slf.vertices.get(&TypeId::of::()), + OrbitPolicy::Edge => $slf.edges.get(&TypeId::of::()), + OrbitPolicy::Face => $slf.faces.get(&TypeId::of::()), + OrbitPolicy::Custom(_) => $slf.others.get(&TypeId::of::()), + }; + let $id = probably_storage + .map(|m| m.downcast_ref::<::StorageType>()) + .flatten(); + }; +} + +macro_rules! get_storage_mut { + ($slf: ident, $id: ident) => { + let probably_storage = match A::BIND_POLICY { + OrbitPolicy::Vertex => $slf.vertices.get_mut(&TypeId::of::()), + OrbitPolicy::Edge => $slf.edges.get_mut(&TypeId::of::()), + OrbitPolicy::Face => $slf.faces.get_mut(&TypeId::of::()), + OrbitPolicy::Custom(_) => $slf.others.get_mut(&TypeId::of::()), + }; + let $id = probably_storage + .map(|m| m.downcast_mut::<::StorageType>()) + .flatten(); + }; +} + /// Main attribute storage structure. /// /// This structure is used to store all generic attributes that the user may add to the @@ -83,8 +116,10 @@ pub struct AttrStorageManager { unsafe impl Send for AttrStorageManager {} unsafe impl Sync for AttrStorageManager {} -/// **Manager-wide methods** +/// **General methods** impl AttrStorageManager { + // attribute-agnostic + /// Extend the size of all storages in the manager. /// /// # Arguments @@ -105,244 +140,8 @@ impl AttrStorageManager { } } - // merges - - /// Execute a merging operation on all attributes associated with a given orbit - /// for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_attributes( - &self, - orbit_policy: &OrbitPolicy, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) { - match orbit_policy { - OrbitPolicy::Vertex => self.merge_vertex_attributes(id_out, id_in_lhs, id_in_rhs), - OrbitPolicy::Edge => self.merge_edge_attributes(id_out, id_in_lhs, id_in_rhs), - OrbitPolicy::Face => self.merge_face_attributes(id_out, id_in_lhs, id_in_rhs), - OrbitPolicy::Custom(_) => { - self.merge_other_attributes(orbit_policy, id_out, id_in_lhs, id_in_rhs); - } - } - } - - /// Execute a merging operation on all attributes associated with vertices for specified cells. - /// - /// # Arguments - /// - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_vertex_attributes( - &self, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) { - for storage in self.vertices.values() { - storage.merge(id_out, id_in_lhs, id_in_rhs); - } - } - - /// Execute a merging operation on all attributes associated with edges for specified cells. - /// - /// # Arguments - /// - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_edge_attributes( - &self, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) { - for storage in self.edges.values() { - storage.merge(id_out, id_in_lhs, id_in_rhs); - } - } - - /// Execute a merging operation on all attributes associated with faces for specified cells. - /// - /// # Arguments - /// - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_face_attributes( - &self, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) { - for storage in self.faces.values() { - storage.merge(id_out, id_in_lhs, id_in_rhs); - } - } - - /// Execute a merging operation on all attributes associated with a given orbit - /// for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_other_attributes( - &self, - _orbit_policy: &OrbitPolicy, - _id_out: DartIdType, - _id_in_lhs: DartIdType, - _id_in_rhs: DartIdType, - ) { - todo!("custom orbit binding is a special case that will be treated later") - } - - // splits - - /// Execute a splitting operation on all attributes associated with a given orbit - /// for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_attributes( - &self, - orbit_policy: &OrbitPolicy, - id_out_lhs: DartIdType, - id_out_rhs: DartIdType, - id_in: DartIdType, - ) { - match orbit_policy { - OrbitPolicy::Vertex => self.split_vertex_attributes(id_out_lhs, id_out_rhs, id_in), - OrbitPolicy::Edge => self.split_edge_attributes(id_out_lhs, id_out_rhs, id_in), - OrbitPolicy::Face => self.split_face_attributes(id_out_lhs, id_out_rhs, id_in), - OrbitPolicy::Custom(_) => { - self.split_other_attributes(orbit_policy, id_out_lhs, id_out_rhs, id_in); - } - } - } - - /// Execute a splitting operation on all attributes associated with vertices - /// for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_vertex_attributes( - &self, - id_out_lhs: DartIdType, - id_out_rhs: DartIdType, - id_in: DartIdType, - ) { - for storage in self.vertices.values() { - storage.split(id_out_lhs, id_out_rhs, id_in); - } - } - - /// Execute a splitting operation on all attributes associated with edges for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_edge_attributes( - &self, - id_out_lhs: DartIdType, - id_out_rhs: DartIdType, - id_in: DartIdType, - ) { - for storage in self.edges.values() { - storage.split(id_out_lhs, id_out_rhs, id_in); - } - } - - /// Execute a splitting operation on all attributes associated with faces for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_face_attributes( - &self, - id_out_lhs: DartIdType, - id_out_rhs: DartIdType, - id_in: DartIdType, - ) { - for storage in self.faces.values() { - storage.split(id_out_lhs, id_out_rhs, id_in); - } - } - - /// Execute a splitting operation on all attributes associated with a given orbit - /// for specified cells. - /// - /// # Arguments - /// - /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_other_attributes( - &self, - _orbit_policy: &OrbitPolicy, - _id_out_lhs: DartIdType, - _id_out_rhs: DartIdType, - _id_in: DartIdType, - ) { - todo!("custom orbit binding is a special case that will be treated later") - } -} - -macro_rules! get_storage { - ($slf: ident, $id: ident) => { - let probably_storage = match A::BIND_POLICY { - OrbitPolicy::Vertex => $slf.vertices.get(&TypeId::of::()), - OrbitPolicy::Edge => $slf.edges.get(&TypeId::of::()), - OrbitPolicy::Face => $slf.faces.get(&TypeId::of::()), - OrbitPolicy::Custom(_) => $slf.others.get(&TypeId::of::()), - }; - let $id = probably_storage - .map(|m| m.downcast_ref::<::StorageType>()) - .flatten(); - }; -} - -macro_rules! get_storage_mut { - ($slf: ident, $id: ident) => { - let probably_storage = match A::BIND_POLICY { - OrbitPolicy::Vertex => $slf.vertices.get_mut(&TypeId::of::()), - OrbitPolicy::Edge => $slf.edges.get_mut(&TypeId::of::()), - OrbitPolicy::Face => $slf.faces.get_mut(&TypeId::of::()), - OrbitPolicy::Custom(_) => $slf.others.get_mut(&TypeId::of::()), - }; - let $id = probably_storage - .map(|m| m.downcast_mut::<::StorageType>()) - .flatten(); - }; -} + // attribute-specific -/// **Attribute-specific methods** -impl AttrStorageManager { - #[allow(clippy::missing_errors_doc)] /// Add a new storage to the manager. /// /// For a breakdown of the principles used for implementation, refer to the *Explanation* @@ -438,394 +237,736 @@ impl AttrStorageManager { OrbitPolicy::Custom(_) => &self.others.remove(&TypeId::of::()), }; } +} - /// Set the value of an attribute. - /// - /// # Arguments +/// Merge variants. +impl AttrStorageManager { + // attribute-agnostic force + + /// Execute a merging operation on all attributes associated with a given orbit + /// for specified cells. /// - /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. - /// - `val: A` -- New value of the attribute for the given ID. + /// This variant is equivalent to `merge_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_merge_attributes( + &self, + orbit_policy: &OrbitPolicy, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) { + match orbit_policy { + OrbitPolicy::Vertex => self.force_merge_vertex_attributes(id_out, id_in_lhs, id_in_rhs), + OrbitPolicy::Edge => self.force_merge_edge_attributes(id_out, id_in_lhs, id_in_rhs), + OrbitPolicy::Face => self.force_merge_face_attributes(id_out, id_in_lhs, id_in_rhs), + OrbitPolicy::Custom(_) => unimplemented!(), + } + } + + /// Execute a merging operation on all attributes associated with vertices for specified cells. /// - /// # Generic - /// - /// - `A: AttributeBind` -- Type of the attribute being set. + /// This variant is equivalent to `merge_vertex_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_merge_vertex_attributes( + &self, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) { + for storage in self.vertices.values() { + storage.force_merge(id_out, id_in_lhs, id_in_rhs); + } + } + + /// Execute a merging operation on all attributes associated with edges for specified cells. /// - /// # Panics + /// This variant is equivalent to `merge_edge_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_merge_edge_attributes( + &self, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) { + for storage in self.edges.values() { + storage.force_merge(id_out, id_in_lhs, id_in_rhs); + } + } + + /// Execute a merging operation on all attributes associated with faces for specified cells. /// - /// This method may panic if: - /// - there's no storage associated with the specified attribute - /// - downcasting `Box` to `::StorageType` fails - /// - the index lands out of bounds - pub fn set_attribute(&self, id: A::IdentifierType, val: A) { - get_storage!(self, storage); - if let Some(st) = storage { - st.set(id, val); - } else { - eprintln!( - "W: could not update storage of attribute {} - storage not found", - std::any::type_name::() - ); + /// This variant is equivalent to `merge_face_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_merge_face_attributes( + &self, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) { + for storage in self.faces.values() { + storage.force_merge(id_out, id_in_lhs, id_in_rhs); } } - /// Set the value of an attribute. + // attribute-agnostic regular + + #[allow(clippy::missing_errors_doc)] + /// Execute a merging operation on all attributes associated with a given orbit + /// for specified cells. /// /// # Arguments /// - /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. - /// - `val: A` -- New value of the attribute for the given ID. + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. + /// - `id_out: DartIdentifier` -- Identifier to write the result to. + /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. + /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. /// - /// # Generic + /// # Return / Errors /// - /// - `A: AttributeBind` -- Type of the attribute being set. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn merge_attributes( + &self, + trans: &mut Transaction, + orbit_policy: &OrbitPolicy, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> StmResult<()> { + match orbit_policy { + OrbitPolicy::Vertex => { + self.merge_vertex_attributes(trans, id_out, id_in_lhs, id_in_rhs) + } + OrbitPolicy::Edge => self.merge_edge_attributes(trans, id_out, id_in_lhs, id_in_rhs), + OrbitPolicy::Face => self.merge_face_attributes(trans, id_out, id_in_lhs, id_in_rhs), + OrbitPolicy::Custom(_) => unimplemented!(), + } + } + + #[allow(clippy::missing_errors_doc)] + /// Execute a merging operation on all attributes associated with vertices for specified cells. /// - /// # Panics + /// # Arguments /// - /// This method may panic if: - /// - **there already is a value associated to the given ID for the specified attribute** - /// - there's no storage associated with the specified attribute - /// - downcasting `Box` to `::StorageType` fails - /// - the index lands out of bounds - pub fn insert_attribute(&self, id: A::IdentifierType, val: A) { - get_storage!(self, storage); - if let Some(st) = storage { - st.insert(id, val); - } else { - eprintln!( - "W: could not update storage of attribute {} - storage not found", - std::any::type_name::() - ); + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out: DartIdentifier` -- Identifier to write the result to. + /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. + /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn merge_vertex_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> StmResult<()> { + for storage in self.vertices.values() { + storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?; } + Ok(()) } - /// Get the value of an attribute. + #[allow(clippy::missing_errors_doc)] + /// Execute a merging operation on all attributes associated with edges for specified cells. /// /// # Arguments /// - /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out: DartIdentifier` -- Identifier to write the result to. + /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. + /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. /// - /// # Generic + /// # Return / Errors /// - /// - `A: AttributeBind` -- Type of the attribute fetched. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn merge_edge_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> StmResult<()> { + for storage in self.edges.values() { + storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?; + } + Ok(()) + } + + #[allow(clippy::missing_errors_doc)] + /// Execute a merging operation on all attributes associated with faces for specified cells. /// - /// # Return + /// # Arguments /// - /// The method may return: - /// - `Some(val: A)` if there is an attribute associated with the specified index, - /// - `None` if there is not. + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out: DartIdentifier` -- Identifier to write the result to. + /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. + /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. /// - /// # Panics + /// # Return / Errors /// - /// This method may panic if: - /// - there's no storage associated with the specified attribute - /// - downcasting `Box` to `::StorageType` fails - /// - the index lands out of bounds - pub fn get_attribute(&self, id: A::IdentifierType) -> Option { - get_storage!(self, storage); - if let Some(st) = storage { - st.get(id) - } else { - eprintln!( - "W: could not update storage of attribute {} - storage not found", - std::any::type_name::() - ); - None + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn merge_face_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> StmResult<()> { + for storage in self.faces.values() { + storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?; + } + Ok(()) + } + + // attribute-agnostic try + + /// Execute a merging operation on all attributes associated with a given orbit + /// for specified cells. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a merge fails (e.g. because one merging value is missing) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_merge_attributes( + &self, + trans: &mut Transaction, + orbit_policy: &OrbitPolicy, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> CMapResult<()> { + match orbit_policy { + OrbitPolicy::Vertex => { + self.try_merge_vertex_attributes(trans, id_out, id_in_lhs, id_in_rhs) + } + OrbitPolicy::Edge => { + self.try_merge_edge_attributes(trans, id_out, id_in_lhs, id_in_rhs) + } + OrbitPolicy::Face => { + self.try_merge_face_attributes(trans, id_out, id_in_lhs, id_in_rhs) + } + OrbitPolicy::Custom(_) => unimplemented!(), } } - /// Set the value of an attribute. + /// Execute a merging operation on all attributes associated with vertices for specified cells. /// - /// # Arguments + /// # Errors /// - /// - `id: A::IdentifierType` -- ID of the cell to which the attribute is associated. - /// - `val: A` -- New value of the attribute for the given ID. + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a merge fails (e.g. because one merging value is missing) /// - /// # Generic + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_merge_vertex_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> CMapResult<()> { + for storage in self.vertices.values() { + storage.try_merge(trans, id_out, id_in_lhs, id_in_rhs)?; + } + Ok(()) + } + + /// Execute a merging operation on all attributes associated with edges for specified cells. /// - /// - `A: AttributeBind` -- Type of the attribute being set. + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a merge fails (e.g. because one merging value is missing) /// - /// # Return + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_merge_edge_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> CMapResult<()> { + for storage in self.edges.values() { + storage.try_merge(trans, id_out, id_in_lhs, id_in_rhs)?; + } + Ok(()) + } + + /// Execute a merging operation on all attributes associated with faces for specified cells. /// - /// The method should return: - /// - `Some(val_old: A)` if there was an attribute associated with the specified index, - /// - `None` if there was not. + /// # Errors /// - /// # Panics + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a merge fails (e.g. because one merging value is missing) /// - /// This method may panic if: - /// - there's no storage associated with the specified attribute - /// - downcasting `Box` to `::StorageType` fails - /// - the index lands out of bounds - pub fn replace_attribute(&self, id: A::IdentifierType, val: A) -> Option { + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_merge_face_attributes( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> CMapResult<()> { + for storage in self.faces.values() { + storage.try_merge(trans, id_out, id_in_lhs, id_in_rhs)?; + } + Ok(()) + } + + // attribute-specific + + /// Merge given attribute values. + /// + /// This variant is equivalent to `merge_attribute`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_merge_attribute( + &self, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) { get_storage!(self, storage); if let Some(st) = storage { - st.replace(id, val) + st.force_merge(id_out, id_in_lhs, id_in_rhs); } else { eprintln!( "W: could not update storage of attribute {} - storage not found", std::any::type_name::() ); - None } } - /// Remove the an item from an attribute storage. + #[allow(clippy::missing_errors_doc)] + /// Merge given attribute values. /// /// # Arguments /// - /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. - /// - /// # Generic + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out: DartIdentifier` -- Identifier to write the result to. + /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. + /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. /// - /// - `A: AttributeBind` -- Type of the attribute fetched. + /// # Return / Errors /// - /// # Return + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn merge_attribute( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> StmResult<()> { + get_storage!(self, storage); + if let Some(st) = storage { + st.merge(trans, id_out, id_in_lhs, id_in_rhs) + } else { + eprintln!( + "W: could not update storage of attribute {} - storage not found", + std::any::type_name::() + ); + Ok(()) + } + } + + /// Merge given attribute values. /// - /// The method may return: - /// - `Some(val: A)` if was is an attribute associated with the specified index, - /// - `None` if there was not. + /// # Errors /// - /// # Panics + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - the merge fails (e.g. because one merging value is missing) /// - /// This method may panic if: - /// - there's no storage associated with the specified attribute - /// - downcasting `Box` to `::StorageType` fails - /// - the index lands out of bounds - pub fn remove_attribute(&self, id: A::IdentifierType) -> Option { + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_merge_attribute( + &self, + trans: &mut Transaction, + id_out: DartIdType, + id_in_lhs: DartIdType, + id_in_rhs: DartIdType, + ) -> CMapResult<()> { get_storage!(self, storage); if let Some(st) = storage { - st.remove(id) + st.try_merge(trans, id_out, id_in_lhs, id_in_rhs) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", std::any::type_name::() ); - None + Ok(()) } } +} - /// Merge given attribute values. +/// Split variants. +impl AttrStorageManager { + // attribute-agnostic force + + /// Execute a splitting operation on all attributes associated with a given orbit + /// for specified cells. /// - /// # Arguments + /// This variant is equivalent to `split_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_split_attributes( + &self, + orbit_policy: &OrbitPolicy, + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) { + match orbit_policy { + OrbitPolicy::Vertex => { + self.force_split_vertex_attributes(id_out_lhs, id_out_rhs, id_in); + } + OrbitPolicy::Edge => self.force_split_edge_attributes(id_out_lhs, id_out_rhs, id_in), + OrbitPolicy::Face => self.force_split_face_attributes(id_out_lhs, id_out_rhs, id_in), + OrbitPolicy::Custom(_) => unimplemented!(), + } + } + + /// Execute a splitting operation on all attributes associated with vertices + /// for specified cells. + /// + /// This variant is equivalent to `split_vertex_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_split_vertex_attributes( + &self, + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) { + for storage in self.vertices.values() { + storage.force_split(id_out_lhs, id_out_rhs, id_in); + } + } + + /// Execute a splitting operation on all attributes associated with edges + /// for specified cells. /// - /// - `id_out: DartIdentifier` -- Identifier to write the result to. - /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge. - /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge. - pub fn merge_attribute( + /// This variant is equivalent to `split_edge_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_split_edge_attributes( &self, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, ) { - get_storage!(self, storage); - if let Some(st) = storage { - st.merge(id_out, id_in_lhs, id_in_rhs); - } else { - eprintln!( - "W: could not update storage of attribute {} - storage not found", - std::any::type_name::() - ); + for storage in self.edges.values() { + storage.force_split(id_out_lhs, id_out_rhs, id_in); } } - /// Split given attribute value. - /// - /// # Arguments + /// Execute a splitting operation on all attributes associated with faces + /// for specified cells. /// - /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. - /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. - pub fn split_attribute( + /// This variant is equivalent to `split_face_attributes`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_split_face_attributes( &self, id_out_lhs: DartIdType, id_out_rhs: DartIdType, id_in: DartIdType, ) { - get_storage!(self, storage); - if let Some(st) = storage { - st.split(id_out_lhs, id_out_rhs, id_in); - } else { - eprintln!( - "W: could not update storage of attribute {} - storage not found", - std::any::type_name::() - ); + for storage in self.faces.values() { + storage.force_split(id_out_lhs, id_out_rhs, id_in); } } -} -/// Transactional methods -#[doc(hidden)] -#[allow(unused)] -impl AttrStorageManager { - // merges + // attribute-agnostic regular - pub(crate) fn merge_attributes_transac( + #[allow(clippy::missing_errors_doc)] + /// Execute a splitting operation on all attributes associated with a given orbit + /// for specified cells. + /// + /// # Arguments + /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes. + /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn split_attributes( &self, trans: &mut Transaction, orbit_policy: &OrbitPolicy, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> StmResult<()> { match orbit_policy { OrbitPolicy::Vertex => { - self.merge_vertex_attributes_transac(trans, id_out, id_in_lhs, id_in_rhs) - } - OrbitPolicy::Edge => { - self.merge_edge_attributes_transac(trans, id_out, id_in_lhs, id_in_rhs) - } - OrbitPolicy::Face => { - self.merge_face_attributes_transac(trans, id_out, id_in_lhs, id_in_rhs) + self.split_vertex_attributes(trans, id_out_lhs, id_out_rhs, id_in) } - OrbitPolicy::Custom(_) => self.merge_other_attributes_transac( - trans, - orbit_policy, - id_out, - id_in_lhs, - id_in_rhs, - ), + OrbitPolicy::Edge => self.split_edge_attributes(trans, id_out_lhs, id_out_rhs, id_in), + OrbitPolicy::Face => self.split_face_attributes(trans, id_out_lhs, id_out_rhs, id_in), + OrbitPolicy::Custom(_) => unimplemented!(), } } - pub(crate) fn merge_vertex_attributes_transac( + #[allow(clippy::missing_errors_doc)] + /// Execute a splitting operation on all attributes associated with vertices + /// for specified cells. + /// + /// # Arguments + /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn split_vertex_attributes( &self, trans: &mut Transaction, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> StmResult<()> { for storage in self.vertices.values() { - storage.merge_transac(trans, id_out, id_in_lhs, id_in_rhs)?; + storage.split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn merge_edge_attributes_transac( + #[allow(clippy::missing_errors_doc)] + /// Execute a splitting operation on all attributes associated with edges for specified cells. + /// + /// # Arguments + /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn split_edge_attributes( &self, trans: &mut Transaction, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> StmResult<()> { for storage in self.edges.values() { - storage.merge_transac(trans, id_out, id_in_lhs, id_in_rhs)?; + storage.split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn merge_face_attributes_transac( + #[allow(clippy::missing_errors_doc)] + /// Execute a splitting operation on all attributes associated with faces for specified cells. + /// + /// # Arguments + /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn split_face_attributes( &self, trans: &mut Transaction, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> StmResult<()> { for storage in self.faces.values() { - storage.merge_transac(trans, id_out, id_in_lhs, id_in_rhs)?; + storage.split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn merge_other_attributes_transac( - &self, - _trans: &mut Transaction, - _orbit_policy: &OrbitPolicy, - _id_out: DartIdType, - _id_in_lhs: DartIdType, - _id_in_rhs: DartIdType, - ) -> Result<(), StmError> { - todo!("custom orbit binding is a special case that will be treated later") - } - - // splits + // attribute-agnostic try - pub(crate) fn split_attributes_transac( + /// Execute a splitting operation on all attributes associated with a given orbit + /// for specified cells. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a split fails (e.g. because there is no value to split from) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_split_attributes( &self, trans: &mut Transaction, orbit_policy: &OrbitPolicy, id_out_lhs: DartIdType, id_out_rhs: DartIdType, id_in: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { match orbit_policy { OrbitPolicy::Vertex => { - self.split_vertex_attributes_transac(trans, id_out_lhs, id_out_rhs, id_in) + self.try_split_vertex_attributes(trans, id_out_lhs, id_out_rhs, id_in) } OrbitPolicy::Edge => { - self.split_edge_attributes_transac(trans, id_out_lhs, id_out_rhs, id_in) + self.try_split_edge_attributes(trans, id_out_lhs, id_out_rhs, id_in) } OrbitPolicy::Face => { - self.split_face_attributes_transac(trans, id_out_lhs, id_out_rhs, id_in) + self.try_split_face_attributes(trans, id_out_lhs, id_out_rhs, id_in) } - OrbitPolicy::Custom(_) => self.split_other_attributes_transac( - trans, - orbit_policy, - id_out_lhs, - id_out_rhs, - id_in, - ), + OrbitPolicy::Custom(_) => unimplemented!(), } } - pub(crate) fn split_vertex_attributes_transac( + /// Execute a splitting operation on all attributes associated with vertices for specified cells. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a split fails (e.g. because there is no value to split from) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_split_vertex_attributes( &self, trans: &mut Transaction, id_out_lhs: DartIdType, id_out_rhs: DartIdType, id_in: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { for storage in self.vertices.values() { - storage.split_transac(trans, id_out_lhs, id_out_rhs, id_in)?; + storage.try_split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn split_edge_attributes_transac( + /// Execute a splitting operation on all attributes associated with edges for specified cells. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a split fails (e.g. because there is no value to split from) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_split_edge_attributes( &self, trans: &mut Transaction, id_out_lhs: DartIdType, id_out_rhs: DartIdType, id_in: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { for storage in self.edges.values() { - storage.split_transac(trans, id_out_lhs, id_out_rhs, id_in)?; + storage.try_split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn split_face_attributes_transac( + /// Execute a splitting operation on all attributes associated with faces for specified cells. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - a split fails (e.g. because there is no value to split from) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_split_face_attributes( &self, trans: &mut Transaction, id_out_lhs: DartIdType, id_out_rhs: DartIdType, id_in: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { for storage in self.faces.values() { - storage.split_transac(trans, id_out_lhs, id_out_rhs, id_in)?; + storage.try_split(trans, id_out_lhs, id_out_rhs, id_in)?; } Ok(()) } - pub(crate) fn split_other_attributes_transac( + // attribute-specific + + /// Split given attribute value. + /// + /// This variant is equivalent to `split_attribute`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_split_attribute( &self, - _trans: &mut Transaction, - _orbit_policy: &OrbitPolicy, - _id_out_lhs: DartIdType, - _id_out_rhs: DartIdType, - _id_in: DartIdType, - ) -> Result<(), StmError> { - todo!("custom orbit binding is a special case that will be treated later") + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) { + get_storage!(self, storage); + if let Some(st) = storage { + st.force_split(id_out_lhs, id_out_rhs, id_in); + } else { + eprintln!( + "W: could not update storage of attribute {} - storage not found", + std::any::type_name::() + ); + } } - pub(crate) fn set_attribute_transac( + #[allow(clippy::missing_errors_doc)] + /// Split given attribute value. + /// + /// # Arguments + /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. + /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to. + /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually. + pub fn split_attribute( &self, trans: &mut Transaction, - id: A::IdentifierType, - val: A, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> StmResult<()> { get_storage!(self, storage); if let Some(st) = storage { - st.set_transac(trans, id, val) + st.split(trans, id_out_lhs, id_out_rhs, id_in) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", @@ -835,15 +976,27 @@ impl AttrStorageManager { } } - pub(crate) fn insert_attribute_transac( + /// Split given attribute value. + /// + /// # Errors + /// + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - the split fails (e.g. because there is no value to split from) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + pub fn try_split_attribute( &self, trans: &mut Transaction, - id: A::IdentifierType, - val: A, - ) -> Result<(), StmError> { + id_out_lhs: DartIdType, + id_out_rhs: DartIdType, + id_in: DartIdType, + ) -> CMapResult<()> { get_storage!(self, storage); if let Some(st) = storage { - st.insert_transac(trans, id, val) + st.try_split(trans, id_out_lhs, id_out_rhs, id_in) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", @@ -852,15 +1005,43 @@ impl AttrStorageManager { Ok(()) } } +} - pub(crate) fn get_attribute_transac( +/// **Attribute read & write methods** +impl AttrStorageManager { + // regular + + #[allow(clippy::missing_errors_doc)] + /// Get the value of an attribute. + /// + /// # Arguments + /// + /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. + /// + /// # Generic + /// + /// - `A: AttributeBind` -- Type of the attribute fetched. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// # Panics + /// + /// This method may panic if: + /// - there's no storage associated with the specified attribute + /// - downcasting `Box` to `::StorageType` fails + /// - the index lands out of bounds + pub fn read_attribute( &self, trans: &mut Transaction, id: A::IdentifierType, - ) -> Result, StmError> { + ) -> StmResult> { get_storage!(self, storage); if let Some(st) = storage { - st.get_transac(trans, id) + st.read(trans, id) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", @@ -870,15 +1051,39 @@ impl AttrStorageManager { } } - pub(crate) fn replace_attribute_transac( + #[allow(clippy::missing_errors_doc)] + /// Set the value of an attribute, and return the old one. + /// + /// # Arguments + /// + /// - `id: A::IdentifierType` -- ID of the cell to which the attribute is associated. + /// - `val: A` -- New value of the attribute for the given ID. + /// + /// # Generic + /// + /// - `A: AttributeBind` -- Type of the attribute being set. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// # Panics + /// + /// This method may panic if: + /// - there's no storage associated with the specified attribute + /// - downcasting `Box` to `::StorageType` fails + /// - the index lands out of bounds + pub fn write_attribute( &self, trans: &mut Transaction, id: A::IdentifierType, val: A, - ) -> Result, StmError> { + ) -> StmResult> { get_storage!(self, storage); if let Some(st) = storage { - st.replace_transac(trans, id, val) + st.write(trans, id, val) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", @@ -888,14 +1093,37 @@ impl AttrStorageManager { } } - pub(crate) fn remove_attribute_transac( + #[allow(clippy::missing_errors_doc)] + /// Remove the an item from an attribute storage, and return it. + /// + /// # Arguments + /// + /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated. + /// + /// # Generic + /// + /// - `A: AttributeBind` -- Type of the attribute fetched. + /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// # Panics + /// + /// This method may panic if: + /// - there's no storage associated with the specified attribute + /// - downcasting `Box` to `::StorageType` fails + /// - the index lands out of bounds + pub(crate) fn remove_attribute( &self, trans: &mut Transaction, id: A::IdentifierType, - ) -> Result, StmError> { + ) -> StmResult> { get_storage!(self, storage); if let Some(st) = storage { - st.remove_transac(trans, id) + st.remove(trans, id) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", @@ -905,41 +1133,58 @@ impl AttrStorageManager { } } - pub(crate) fn merge_attribute_transac( - &self, - trans: &mut Transaction, - id_out: DartIdType, - id_in_lhs: DartIdType, - id_in_rhs: DartIdType, - ) -> Result<(), StmError> { + /// Get the value of an attribute. + /// + /// This variant is equivalent to `read_attribute`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_read_attribute(&self, id: A::IdentifierType) -> Option { get_storage!(self, storage); if let Some(st) = storage { - st.merge_transac(trans, id_out, id_in_lhs, id_in_rhs) + st.force_read(id) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", std::any::type_name::() ); - Ok(()) + None } } - pub(crate) fn split_attribute_transac( + /// Set the value of an attribute, and return the old one. + /// + /// This variant is equivalent to `write_attribute`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_write_attribute( &self, - trans: &mut Transaction, - id_out_lhs: DartIdType, - id_out_rhs: DartIdType, - id_in: DartIdType, - ) -> Result<(), StmError> { + id: A::IdentifierType, + val: A, + ) -> Option { get_storage!(self, storage); if let Some(st) = storage { - st.split_transac(trans, id_out_lhs, id_out_rhs, id_in) + st.force_write(id, val) } else { eprintln!( "W: could not update storage of attribute {} - storage not found", std::any::type_name::() ); - Ok(()) + None + } + } + + /// Remove the an item from an attribute storage, and return it. + /// + /// This variant is equivalent to `remove_attribute`, but internally uses a transaction + /// that will be retried until validated. + pub fn force_remove_attribute(&self, id: A::IdentifierType) -> Option { + get_storage!(self, storage); + if let Some(st) = storage { + st.force_remove(id) + } else { + eprintln!( + "W: could not update storage of attribute {} - storage not found", + std::any::type_name::() + ); + None } } } diff --git a/honeycomb-core/src/attributes/tests.rs b/honeycomb-core/src/attributes/tests.rs index 83e652e6a..6ab44fddf 100644 --- a/honeycomb-core/src/attributes/tests.rs +++ b/honeycomb-core/src/attributes/tests.rs @@ -135,82 +135,79 @@ fn temperature_map() { .n_darts(6) .add_attribute::(); let map: CMap2 = builder.build().unwrap(); + map.force_two_link(1, 2); map.force_two_link(3, 4); map.force_two_link(5, 6); map.force_one_link(1, 3); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(4, (1.5, 0.0)); - map.insert_vertex(5, (2.5, 0.0)); - map.insert_vertex(6, (3.0, 0.0)); - map.set_attribute::(1, Temperature::from(273.)); - map.set_attribute::(2, Temperature::from(275.)); - map.set_attribute::(4, Temperature::from(277.)); - map.set_attribute::(5, Temperature::from(273.)); - map.set_attribute::(6, Temperature::from(273.)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(4, (1.5, 0.0)); + map.force_write_vertex(5, (2.5, 0.0)); + map.force_write_vertex(6, (3.0, 0.0)); + map.force_write_attribute::(1, Temperature::from(273.)); + map.force_write_attribute::(2, Temperature::from(275.)); + map.force_write_attribute::(4, Temperature::from(277.)); + map.force_write_attribute::(5, Temperature::from(273.)); + map.force_write_attribute::(6, Temperature::from(273.)); + // test the map assert_eq!( - map.get_attribute::(map.vertex_id(4)), + map.force_read_attribute::(map.vertex_id(4)), Some(Temperature::from(277.)) ); assert_eq!( - map.get_attribute::(map.vertex_id(5)), + map.force_read_attribute::(map.vertex_id(5)), Some(Temperature::from(273.)) ); // sew one segment map.force_one_sew(3, 5); assert_eq!(map.vertex_id(4), map.vertex_id(5)); assert_eq!( - map.get_attribute::(map.vertex_id(4)), + map.force_read_attribute::(map.vertex_id(4)), Some(Temperature::from(275.)) ); - assert_eq!(map.vertex(map.vertex_id(4)), Some(Vertex2::from((2., 0.)))); + assert_eq!( + map.force_read_vertex(map.vertex_id(4)), + Some(Vertex2::from((2., 0.))) + ); // unsew another map.force_one_unsew(1); assert_ne!(map.vertex_id(2), map.vertex_id(3)); assert_eq!( - map.get_attribute::(map.vertex_id(2)), + map.force_read_attribute::(map.vertex_id(2)), Some(Temperature::from(275.)) ); assert_eq!( - map.get_attribute::(map.vertex_id(3)), + map.force_read_attribute::(map.vertex_id(3)), Some(Temperature::from(275.)) ); - assert_eq!(map.vertex(map.vertex_id(2)), Some(Vertex2::from((1., 0.)))); - assert_eq!(map.vertex(map.vertex_id(3)), Some(Vertex2::from((1., 0.)))); + assert_eq!( + map.force_read_vertex(map.vertex_id(2)), + Some(Vertex2::from((1., 0.))) + ); + assert_eq!( + map.force_read_vertex(map.vertex_id(3)), + Some(Vertex2::from((1., 0.))) + ); } + #[test] fn test_attribute_operations() { let mut manager = AttrStorageManager::default(); manager.add_storage::(5); // Test set and get - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); assert_eq!( - manager.get_attribute::(0), + manager.force_read_attribute::(0), Some(Temperature::from(25.0)) ); - // Test insert - manager.insert_attribute(1, Temperature::from(30.0)); - assert_eq!( - manager.get_attribute::(1), - Some(Temperature::from(30.0)) - ); - - // Test replace - let old_val = manager.replace_attribute(0, Temperature::from(27.0)); - assert_eq!(old_val, Some(Temperature::from(25.0))); - assert_eq!( - manager.get_attribute::(0), - Some(Temperature::from(27.0)) - ); - // Test remove - let removed = manager.remove_attribute::(0); - assert_eq!(removed, Some(Temperature::from(27.0))); - assert_eq!(manager.get_attribute::(0), None); + let removed = manager.force_remove_attribute::(0); + assert_eq!(removed, Some(Temperature::from(25.0))); + assert_eq!(manager.force_read_attribute::(0), None); } #[test] @@ -219,15 +216,15 @@ fn test_merge_attributes() { manager.add_storage::(5); // Setup initial values - manager.set_attribute(0, Temperature::from(20.0)); - manager.set_attribute(1, Temperature::from(30.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(1, Temperature::from(30.0)); // Test merge - manager.merge_attribute::(2, 0, 1); + manager.force_merge_attribute::(2, 0, 1); // The exact result depends on how merge is implemented in AttributeStorage // Just verify that something was stored at the output location - assert!(manager.get_attribute::(2).is_some()); + assert!(manager.force_read_attribute::(2).is_some()); } #[test] @@ -236,15 +233,15 @@ fn test_split_attributes() { manager.add_storage::(5); // Setup initial value - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); // Test split - manager.split_attribute::(1, 2, 0); + manager.force_split_attribute::(1, 2, 0); // The exact results depend on how split is implemented in AttributeStorage // Just verify that something was stored at both output locations - assert!(manager.get_attribute::(1).is_some()); - assert!(manager.get_attribute::(2).is_some()); + assert!(manager.force_read_attribute::(1).is_some()); + assert!(manager.force_read_attribute::(2).is_some()); } #[test] @@ -272,13 +269,13 @@ fn test_orbit_specific_merges() { manager.add_storage::(5); // Setup values - manager.set_attribute(0, Temperature::from(20.0)); - manager.set_attribute(1, Temperature::from(30.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(1, Temperature::from(30.0)); // Test vertex-specific merge - manager.merge_vertex_attributes(2, 0, 1); + manager.force_merge_vertex_attributes(2, 0, 1); - assert!(manager.get_attribute::(2).is_some()); + assert!(manager.force_read_attribute::(2).is_some()); } #[test] @@ -287,13 +284,13 @@ fn test_orbit_specific_splits() { manager.add_storage::(5); // Setup value - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); // Test vertex-specific split - manager.split_vertex_attributes(1, 2, 0); + manager.force_split_vertex_attributes(1, 2, 0); - assert!(manager.get_attribute::(1).is_some()); - assert!(manager.get_attribute::(2).is_some()); + assert!(manager.force_read_attribute::(1).is_some()); + assert!(manager.force_read_attribute::(2).is_some()); } // --- unit tests @@ -307,32 +304,32 @@ fn setup_manager() -> AttrStorageManager { } #[test] -fn test_merge_vertex_attributes_transac() { +fn test_merge_vertex_attributes() { let manager = setup_manager(); // Set initial values - manager.set_attribute(0, Temperature::from(20.0)); - manager.set_attribute(1, Temperature::from(30.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(1, Temperature::from(30.0)); - atomically(|trans| manager.merge_vertex_attributes_transac(trans, 2, 0, 1)); + atomically(|trans| manager.merge_vertex_attributes(trans, 2, 0, 1)); // Verify merged result - let merged = manager.get_attribute::(2); + let merged = manager.force_read_attribute::(2); assert!(merged.is_some()); assert_eq!(merged.unwrap(), Temperature::from(25.0)); } #[test] -fn test_split_vertex_attributes_transac() { +fn test_split_vertex_attributes() { let manager = setup_manager(); // Set initial value - manager.set_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); - atomically(|trans| manager.split_vertex_attributes_transac(trans, 1, 2, 0)); + atomically(|trans| manager.split_vertex_attributes(trans, 1, 2, 0)); // Verify split results - let split1 = manager.get_attribute::(1); - let split2 = manager.get_attribute::(2); + let split1 = manager.force_read_attribute::(1); + let split2 = manager.force_read_attribute::(2); assert!(split1.is_some()); assert!(split2.is_some()); @@ -341,101 +338,71 @@ fn test_split_vertex_attributes_transac() { } #[test] -fn test_set_attribute_transac() { +fn test_write_attribute() { let manager = setup_manager(); - atomically(|trans| manager.set_attribute_transac(trans, 0, Temperature::from(25.0))); + atomically(|trans| manager.write_attribute(trans, 0, Temperature::from(25.0))); - let value = manager.get_attribute::(0); + let value = manager.force_read_attribute::(0); assert!(value.is_some()); assert_eq!(value.unwrap().val, 25.0); } #[test] -fn test_insert_attribute_transac() { - let manager = setup_manager(); - - atomically(|trans| manager.insert_attribute_transac(trans, 0, Temperature::from(25.0))); - - let value = manager.get_attribute::(0); - assert!(value.is_some()); - assert_eq!(value.unwrap().val, 25.0); -} - -#[test] -fn test_get_attribute_transac() { +fn test_read_attribute() { let manager = setup_manager(); // Set initial value - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); - let value = atomically(|trans| manager.get_attribute_transac::(trans, 0)); + let value = atomically(|trans| manager.read_attribute::(trans, 0)); assert!(value.is_some()); assert_eq!(value.unwrap().val, 25.0); } #[test] -fn test_replace_attribute_transac() { - let manager = setup_manager(); - - // Set initial value - manager.set_attribute(0, Temperature::from(25.0)); - - let old_value = - atomically(|trans| manager.replace_attribute_transac(trans, 0, Temperature::from(30.0))); - - assert!(old_value.is_some()); - assert_eq!(old_value.unwrap().val, 25.0); - - let new_value = manager.get_attribute::(0); - assert!(new_value.is_some()); - assert_eq!(new_value.unwrap().val, 30.0); -} - -#[test] -fn test_remove_attribute_transac() { +fn test_remove_attribute() { let manager = setup_manager(); // Set initial value - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); - let removed_value = - atomically(|trans| manager.remove_attribute_transac::(trans, 0)); + let removed_value = atomically(|trans| manager.remove_attribute::(trans, 0)); assert!(removed_value.is_some()); assert_eq!(removed_value.unwrap().val, 25.0); - let value = manager.get_attribute::(0); + let value = manager.force_read_attribute::(0); assert!(value.is_none()); } #[test] -fn test_merge_attribute_transac() { +fn test_merge_attribute() { let manager = setup_manager(); // Set initial values - manager.set_attribute(0, Temperature::from(20.0)); - manager.set_attribute(1, Temperature::from(30.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(1, Temperature::from(30.0)); - atomically(|trans| manager.merge_attribute_transac::(trans, 2, 0, 1)); + atomically(|trans| manager.merge_attribute::(trans, 2, 0, 1)); - let merged = manager.get_attribute::(2); + let merged = manager.force_read_attribute::(2); assert!(merged.is_some()); assert_eq!(merged.unwrap().val, 25.0); // Assuming merge averages values } #[test] -fn test_split_attribute_transac() { +fn test_split_attribute() { let manager = setup_manager(); // Set initial value - manager.set_attribute(0, Temperature::from(20.0)); + manager.force_write_attribute(0, Temperature::from(20.0)); - atomically(|trans| manager.split_attribute_transac::(trans, 1, 2, 0)); + atomically(|trans| manager.split_attribute::(trans, 1, 2, 0)); - let split1 = manager.get_attribute::(1); - let split2 = manager.get_attribute::(2); + let split1 = manager.force_read_attribute::(1); + let split2 = manager.force_read_attribute::(2); assert!(split1.is_some()); assert!(split2.is_some()); @@ -448,21 +415,21 @@ fn test_attribute_operations_with_failed_transaction() { let manager = setup_manager(); // Set initial value - manager.set_attribute(0, Temperature::from(25.0)); + manager.force_write_attribute(0, Temperature::from(25.0)); let _: Option<()> = Transaction::with_control( |_err| TransactionControl::Abort, |trans| { - manager.set_attribute_transac(trans, 0, Temperature::from(30.0))?; - manager.insert_attribute_transac(trans, 1, Temperature::from(35.0))?; + manager.write_attribute(trans, 0, Temperature::from(30.0))?; + manager.write_attribute(trans, 1, Temperature::from(35.0))?; Err(StmError::Failure) }, ); // Verify original values remained unchanged - let value0 = manager.get_attribute::(0); - let value1 = manager.get_attribute::(1); + let value0 = manager.force_read_attribute::(0); + let value1 = manager.force_read_attribute::(1); assert!(value0.is_some()); assert_eq!(value0.unwrap().val, 25.0); @@ -503,16 +470,16 @@ macro_rules! generate_sparse { ($name: ident) => { #[allow(unused_mut)] let mut $name = AttrSparseVec::::new(10); - $name.insert(0, Temperature::from(273.0)); - $name.insert(1, Temperature::from(275.0)); - $name.insert(2, Temperature::from(277.0)); - $name.insert(3, Temperature::from(279.0)); - $name.insert(4, Temperature::from(281.0)); - $name.insert(5, Temperature::from(283.0)); - $name.insert(6, Temperature::from(285.0)); - $name.insert(7, Temperature::from(287.0)); - $name.insert(8, Temperature::from(289.0)); - $name.insert(9, Temperature::from(291.0)); + $name.force_write(0, Temperature::from(273.0)); + $name.force_write(1, Temperature::from(275.0)); + $name.force_write(2, Temperature::from(277.0)); + $name.force_write(3, Temperature::from(279.0)); + $name.force_write(4, Temperature::from(281.0)); + $name.force_write(5, Temperature::from(283.0)); + $name.force_write(6, Temperature::from(285.0)); + $name.force_write(7, Temperature::from(287.0)); + $name.force_write(8, Temperature::from(289.0)); + $name.force_write(9, Temperature::from(291.0)); }; } @@ -520,123 +487,91 @@ macro_rules! generate_sparse { fn sparse_vec_n_attributes() { generate_sparse!(storage); assert_eq!(storage.n_attributes(), 10); - let _ = storage.remove(3); + let _ = storage.force_remove(3); assert_eq!(storage.n_attributes(), 9); // extend does not affect the number of attributes storage.extend(10); - assert!(storage.get(15).is_none()); + assert!(storage.force_read(15).is_none()); assert_eq!(storage.n_attributes(), 9); } #[test] fn sparse_vec_merge() { generate_sparse!(storage); - assert_eq!(storage.get(3), Some(Temperature::from(279.0))); - assert_eq!(storage.get(6), Some(Temperature::from(285.0))); - assert_eq!(storage.get(8), Some(Temperature::from(289.0))); - storage.merge(8, 3, 6); - assert_eq!(storage.get(3), None); - assert_eq!(storage.get(6), None); - assert_eq!(storage.get(8), Some(Temperature::from(282.0))); + assert_eq!(storage.force_read(3), Some(Temperature::from(279.0))); + assert_eq!(storage.force_read(6), Some(Temperature::from(285.0))); + assert_eq!(storage.force_read(8), Some(Temperature::from(289.0))); + storage.force_merge(8, 3, 6); + assert_eq!(storage.force_read(3), None); + assert_eq!(storage.force_read(6), None); + assert_eq!(storage.force_read(8), Some(Temperature::from(282.0))); } #[test] fn sparse_vec_merge_undefined() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - assert_eq!(storage.remove(6), Some(Temperature::from(285.0))); - assert_eq!(storage.remove(8), Some(Temperature::from(289.0))); + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); + assert_eq!(storage.force_remove(6), Some(Temperature::from(285.0))); + assert_eq!(storage.force_remove(8), Some(Temperature::from(289.0))); // merge from two undefined value - storage.merge(8, 3, 6); - assert_eq!(storage.get(3), None); - assert_eq!(storage.get(6), None); - assert_eq!(storage.get(8), Some(Temperature::from(0.0))); + storage.force_merge(8, 3, 6); + assert_eq!(storage.force_read(3), None); + assert_eq!(storage.force_read(6), None); + assert_eq!(storage.force_read(8), Some(Temperature::from(0.0))); // merge from one undefined value - assert_eq!(storage.get(4), Some(Temperature::from(281.0))); - storage.merge(6, 3, 4); - assert_eq!(storage.get(3), None); - assert_eq!(storage.get(4), None); - assert_eq!(storage.get(6), Some(Temperature::from(281.0 / 2.0))); + assert_eq!(storage.force_read(4), Some(Temperature::from(281.0))); + storage.force_merge(6, 3, 4); + assert_eq!(storage.force_read(3), None); + assert_eq!(storage.force_read(4), None); + assert_eq!(storage.force_read(6), Some(Temperature::from(281.0 / 2.0))); } #[test] fn sparse_vec_split() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - assert_eq!(storage.remove(6), Some(Temperature::from(285.0))); - assert_eq!(storage.get(8), Some(Temperature::from(289.0))); - storage.split(3, 6, 8); - assert_eq!(storage.get(3), Some(Temperature::from(289.0))); - assert_eq!(storage.get(6), Some(Temperature::from(289.0))); - assert_eq!(storage.get(8), None); -} - -#[test] -fn sparse_vec_get_set_get() { - generate_sparse!(storage); - assert_eq!(storage.get(3), Some(Temperature::from(279.0))); - storage.set(3, Temperature::from(280.0)); - assert_eq!(storage.get(3), Some(Temperature::from(280.0))); -} - -#[test] -fn sparse_vec_get_replace_get() { - generate_sparse!(storage); - assert_eq!(storage.get(3), Some(Temperature::from(279.0))); - storage.replace(3, Temperature::from(280.0)); - assert_eq!(storage.get(3), Some(Temperature::from(280.0))); + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); + assert_eq!(storage.force_remove(6), Some(Temperature::from(285.0))); + assert_eq!(storage.force_read(8), Some(Temperature::from(289.0))); + storage.force_split(3, 6, 8); + assert_eq!(storage.force_read(3), Some(Temperature::from(289.0))); + assert_eq!(storage.force_read(6), Some(Temperature::from(289.0))); + assert_eq!(storage.force_read(8), None); } #[test] -#[should_panic(expected = "assertion failed: tmp.is_none()")] -fn sparse_vec_insert_already_existing() { +fn sparse_vec_read_set_read() { generate_sparse!(storage); - assert_eq!(storage.get(3), Some(Temperature::from(279.0))); - storage.insert(3, Temperature::from(280.0)); // panic + assert_eq!(storage.force_read(3), Some(Temperature::from(279.0))); + storage.force_write(3, Temperature::from(280.0)); + assert_eq!(storage.force_read(3), Some(Temperature::from(280.0))); } #[test] fn sparse_vec_remove() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); } #[test] fn sparse_vec_remove_remove() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - assert!(storage.remove(3).is_none()); + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); + assert!(storage.force_remove(3).is_none()); } #[test] -fn sparse_vec_remove_get() { +fn sparse_vec_remove_read() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - assert!(storage.get(3).is_none()); + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); + assert!(storage.force_read(3).is_none()); } #[test] fn sparse_vec_remove_set() { generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - storage.set(3, Temperature::from(280.0)); - assert!(storage.get(3).is_some()); -} - -#[test] -fn sparse_vec_remove_insert() { - generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - storage.insert(3, Temperature::from(280.0)); - assert!(storage.get(3).is_some()); -} - -#[test] -#[should_panic(expected = "called `Option::unwrap()` on a `None` value")] -fn sparse_vec_replace_already_removed() { - generate_sparse!(storage); - assert_eq!(storage.remove(3), Some(Temperature::from(279.0))); - storage.replace(3, Temperature::from(280.0)).unwrap(); // panic + assert_eq!(storage.force_remove(3), Some(Temperature::from(279.0))); + storage.force_write(3, Temperature::from(280.0)); + assert!(storage.force_read(3).is_some()); } // storage manager @@ -645,16 +580,16 @@ macro_rules! generate_manager { ($name: ident) => { let mut $name = AttrStorageManager::default(); $name.add_storage::(10); - $name.insert_attribute(0, Temperature::from(273.0)); - $name.insert_attribute(1, Temperature::from(275.0)); - $name.insert_attribute(2, Temperature::from(277.0)); - $name.insert_attribute(3, Temperature::from(279.0)); - $name.insert_attribute(4, Temperature::from(281.0)); - $name.insert_attribute(5, Temperature::from(283.0)); - $name.insert_attribute(6, Temperature::from(285.0)); - $name.insert_attribute(7, Temperature::from(287.0)); - $name.insert_attribute(8, Temperature::from(289.0)); - $name.insert_attribute(9, Temperature::from(291.0)); + $name.force_write_attribute(0, Temperature::from(273.0)); + $name.force_write_attribute(1, Temperature::from(275.0)); + $name.force_write_attribute(2, Temperature::from(277.0)); + $name.force_write_attribute(3, Temperature::from(279.0)); + $name.force_write_attribute(4, Temperature::from(281.0)); + $name.force_write_attribute(5, Temperature::from(283.0)); + $name.force_write_attribute(6, Temperature::from(285.0)); + $name.force_write_attribute(7, Temperature::from(287.0)); + $name.force_write_attribute(8, Temperature::from(289.0)); + $name.force_write_attribute(9, Temperature::from(291.0)); }; } @@ -671,8 +606,9 @@ fn manager_extend() { manager.get_storage::().unwrap().n_attributes(), 10 ); - (10..20) - .for_each(|id| manager.insert_attribute(id, Temperature::from(273.0 + 2.0 * id as f32))); + (10..20).for_each(|id| { + manager.force_write_attribute(id, Temperature::from(273.0 + 2.0 * id as f32)); + }); assert_eq!( manager.get_storage::().unwrap().n_attributes(), 20 @@ -687,103 +623,111 @@ fn manager_set_oob() { manager.get_storage::().unwrap().n_attributes(), 10 ); - manager.insert_attribute(15, Temperature::from(0.0)); // panic -} - -#[test] -fn manager_get_set_get() { - generate_manager!(manager); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(279.0))); - manager.set_attribute(3, Temperature::from(280.0)); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(280.0))); + manager.force_write_attribute(15, Temperature::from(0.0)); // panic } #[test] -fn manager_vec_get_replace_get() { +fn manager_read_set_read() { generate_manager!(manager); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(279.0))); - manager.replace_attribute(3, Temperature::from(280.0)); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(280.0))); -} - -// expect tmp.is_none since Temperate::StorageType is AttrSparseVec -#[test] -#[should_panic(expected = "assertion failed: tmp.is_none()")] -fn manager_vec_insert_already_existing() { - generate_manager!(manager); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(279.0))); - manager.insert_attribute(3, Temperature::from(280.0)); // panic + assert_eq!( + manager.force_read_attribute(3), + Some(Temperature::from(279.0)) + ); + manager.force_write_attribute(3, Temperature::from(280.0)); + assert_eq!( + manager.force_read_attribute(3), + Some(Temperature::from(280.0)) + ); } #[test] fn manager_vec_remove_remove() { generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - assert!(manager.remove_attribute::(3).is_none()); + assert_eq!( + manager.force_remove_attribute(3), + Some(Temperature::from(279.0)) + ); + assert!(manager.force_remove_attribute::(3).is_none()); } #[test] -fn manager_vec_remove_get() { +fn manager_vec_remove_read() { generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - assert!(manager.get_attribute::(3).is_none()); + assert_eq!( + manager.force_remove_attribute(3), + Some(Temperature::from(279.0)) + ); + assert!(manager.force_read_attribute::(3).is_none()); } #[test] fn manager_vec_remove_set() { generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - manager.set_attribute(3, Temperature::from(280.0)); - assert!(manager.get_attribute::(3).is_some()); -} - -#[test] -fn manager_vec_remove_insert() { - generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - manager.insert_attribute(3, Temperature::from(280.0)); - assert!(manager.get_attribute::(3).is_some()); -} - -#[test] -fn manager_vec_replace_already_removed() { - generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - assert!(manager - .replace_attribute(3, Temperature::from(280.0)) - .is_none()); + assert_eq!( + manager.force_remove_attribute(3), + Some(Temperature::from(279.0)) + ); + manager.force_write_attribute(3, Temperature::from(280.0)); + assert!(manager.force_read_attribute::(3).is_some()); } #[test] fn manager_merge_attribute() { generate_manager!(manager); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(279.0))); - assert_eq!(manager.get_attribute(6), Some(Temperature::from(285.0))); - assert_eq!(manager.get_attribute(8), Some(Temperature::from(289.0))); - manager.merge_attribute::(8, 3, 6); - assert_eq!(manager.get_attribute::(3), None); - assert_eq!(manager.get_attribute::(6), None); - assert_eq!(manager.get_attribute(8), Some(Temperature::from(282.0))); + assert_eq!( + manager.force_read_attribute(3), + Some(Temperature::from(279.0)) + ); + assert_eq!( + manager.force_read_attribute(6), + Some(Temperature::from(285.0)) + ); + assert_eq!( + manager.force_read_attribute(8), + Some(Temperature::from(289.0)) + ); + manager.force_merge_attribute::(8, 3, 6); + assert_eq!(manager.force_read_attribute::(3), None); + assert_eq!(manager.force_read_attribute::(6), None); + assert_eq!( + manager.force_read_attribute(8), + Some(Temperature::from(282.0)) + ); } #[test] fn manager_merge_undefined_attribute() { generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - assert_eq!(manager.remove_attribute(6), Some(Temperature::from(285.0))); - assert_eq!(manager.remove_attribute(8), Some(Temperature::from(289.0))); + assert_eq!( + manager.force_remove_attribute(3), + Some(Temperature::from(279.0)) + ); + assert_eq!( + manager.force_remove_attribute(6), + Some(Temperature::from(285.0)) + ); + assert_eq!( + manager.force_remove_attribute(8), + Some(Temperature::from(289.0)) + ); // merge from two undefined value - manager.merge_attribute::(8, 3, 6); - assert_eq!(manager.get_attribute::(3), None); - assert_eq!(manager.get_attribute::(6), None); - assert_eq!(manager.get_attribute(8), Some(Temperature::from(0.0))); + manager.force_merge_attribute::(8, 3, 6); + assert_eq!(manager.force_read_attribute::(3), None); + assert_eq!(manager.force_read_attribute::(6), None); + assert_eq!( + manager.force_read_attribute(8), + Some(Temperature::from(0.0)) + ); // merge from one undefined value - assert_eq!(manager.get_attribute(4), Some(Temperature::from(281.0))); - manager.merge_attribute::(6, 3, 4); - assert_eq!(manager.get_attribute::(3), None); - assert_eq!(manager.get_attribute::(4), None); assert_eq!( - manager.get_attribute(6), + manager.force_read_attribute(4), + Some(Temperature::from(281.0)) + ); + manager.force_merge_attribute::(6, 3, 4); + assert_eq!(manager.force_read_attribute::(3), None); + assert_eq!(manager.force_read_attribute::(4), None); + assert_eq!( + manager.force_read_attribute(6), Some(Temperature::from(281.0 / 2.0)) ); } @@ -791,13 +735,28 @@ fn manager_merge_undefined_attribute() { #[test] fn manager_split_attribute() { generate_manager!(manager); - assert_eq!(manager.remove_attribute(3), Some(Temperature::from(279.0))); - assert_eq!(manager.remove_attribute(6), Some(Temperature::from(285.0))); - assert_eq!(manager.get_attribute(8), Some(Temperature::from(289.0))); - manager.split_attribute::(3, 6, 8); - assert_eq!(manager.get_attribute(3), Some(Temperature::from(289.0))); - assert_eq!(manager.get_attribute(6), Some(Temperature::from(289.0))); - assert_eq!(manager.get_attribute::(8), None); + assert_eq!( + manager.force_remove_attribute(3), + Some(Temperature::from(279.0)) + ); + assert_eq!( + manager.force_remove_attribute(6), + Some(Temperature::from(285.0)) + ); + assert_eq!( + manager.force_read_attribute(8), + Some(Temperature::from(289.0)) + ); + manager.force_split_attribute::(3, 6, 8); + assert_eq!( + manager.force_read_attribute(3), + Some(Temperature::from(289.0)) + ); + assert_eq!( + manager.force_read_attribute(6), + Some(Temperature::from(289.0)) + ); + assert_eq!(manager.force_read_attribute::(8), None); } // --- parallel @@ -813,17 +772,17 @@ fn manager_ordering() { manager.add_storage::(4); manager.add_storage::(4); - manager.set_attribute(1, Temperature::from(20.0)); - manager.set_attribute(3, Temperature::from(30.0)); + manager.force_write_attribute(1, Temperature::from(20.0)); + manager.force_write_attribute(3, Temperature::from(30.0)); - manager.set_attribute(1, Length(3.0)); - manager.set_attribute(3, Length(2.0)); + manager.force_write_attribute(1, Length(3.0)); + manager.force_write_attribute(3, Length(2.0)); - manager.set_attribute(1, Weight(10)); - manager.set_attribute(3, Weight(15)); + manager.force_write_attribute(1, Weight(10)); + manager.force_write_attribute(3, Weight(15)); - manager.set_attribute(1, Color(255, 0, 0)); - manager.set_attribute(3, Color(0, 0, 255)); + manager.force_write_attribute(1, Color(255, 0, 0)); + manager.force_write_attribute(3, Color(0, 0, 255)); let arc = Arc::new(manager); let c1 = arc.clone(); @@ -837,18 +796,18 @@ fn manager_ordering() { let t1 = loom::thread::spawn(move || { atomically(|trans| { - c1.merge_vertex_attributes_transac(trans, 2, 1, 3)?; - c1.merge_edge_attributes_transac(trans, 2, 1, 3)?; - c1.merge_face_attributes_transac(trans, 2, 1, 3)?; + c1.merge_vertex_attributes(trans, 2, 1, 3)?; + c1.merge_edge_attributes(trans, 2, 1, 3)?; + c1.merge_face_attributes(trans, 2, 1, 3)?; Ok(()) }); }); let t2 = loom::thread::spawn(move || { atomically(|trans| { - c2.split_vertex_attributes_transac(trans, 2, 3, 2)?; - c2.split_edge_attributes_transac(trans, 2, 3, 2)?; - c2.split_face_attributes_transac(trans, 2, 3, 2)?; + c2.split_vertex_attributes(trans, 2, 3, 2)?; + c2.split_edge_attributes(trans, 2, 3, 2)?; + c2.split_face_attributes(trans, 2, 3, 2)?; Ok(()) }); }); @@ -857,39 +816,39 @@ fn manager_ordering() { t2.join().unwrap(); // in both cases - let slot_1_is_empty = arc.get_attribute::(1).is_none() - && arc.get_attribute::(1).is_none() - && arc.get_attribute::(1).is_none() - && arc.get_attribute::(1).is_none(); + let slot_1_is_empty = arc.force_read_attribute::(1).is_none() + && arc.force_read_attribute::(1).is_none() + && arc.force_read_attribute::(1).is_none() + && arc.force_read_attribute::(1).is_none(); assert!(slot_1_is_empty); // path 1: merge before split let p1_2_temp = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|val| val == Temperature::from(25.0)); let p1_3_temp = arc - .get_attribute::(3) + .force_read_attribute::(3) .is_some_and(|val| val == Temperature::from(25.0)); let p1_2_weight = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Weight(13)); let p1_3_weight = arc - .get_attribute::(3) + .force_read_attribute::(3) .is_some_and(|v| v == Weight(12)); let p1_2_len = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Length(2.5)); let p1_3_len = arc - .get_attribute::(3) + .force_read_attribute::(3) .is_some_and(|v| v == Length(2.5)); let p1_2_col = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Color(127, 0, 127)); let p1_3_col = arc - .get_attribute::(3) + .force_read_attribute::(3) .is_some_and(|v| v == Color(127, 0, 127)); let p1 = slot_1_is_empty @@ -904,24 +863,24 @@ fn manager_ordering() { // path 2: split before merge let p2_2_temp = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|val| val == Temperature::from(5.0)); - let p2_3_temp = arc.get_attribute::(3).is_none(); + let p2_3_temp = arc.force_read_attribute::(3).is_none(); let p2_2_weight = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Weight(10)); - let p2_3_weight = arc.get_attribute::(3).is_none(); + let p2_3_weight = arc.force_read_attribute::(3).is_none(); let p2_2_len = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Length(3.0)); - let p2_3_len = arc.get_attribute::(3).is_none(); + let p2_3_len = arc.force_read_attribute::(3).is_none(); let p2_2_col = arc - .get_attribute::(2) + .force_read_attribute::(2) .is_some_and(|v| v == Color(255, 0, 0)); - let p2_3_col = arc.get_attribute::(3).is_none(); + let p2_3_col = arc.force_read_attribute::(3).is_none(); let p2 = slot_1_is_empty && p2_2_temp diff --git a/honeycomb-core/src/attributes/traits.rs b/honeycomb-core/src/attributes/traits.rs index 761c8a030..ead9ba67d 100644 --- a/honeycomb-core/src/attributes/traits.rs +++ b/honeycomb-core/src/attributes/traits.rs @@ -5,11 +5,14 @@ // ------ IMPORTS -use crate::prelude::{DartIdType, OrbitPolicy}; +use crate::{ + cmap::CMapResult, + prelude::{DartIdType, OrbitPolicy}, +}; use downcast_rs::{impl_downcast, Downcast}; use std::any::Any; use std::fmt::Debug; -use stm::{StmError, Transaction}; +use stm::{atomically, StmResult, Transaction}; // ------ CONTENT @@ -57,7 +60,7 @@ use stm::{StmError, Transaction}; /// /// assert_eq!(Temperature::split(t_new), (t_ref, t_ref)); // or Temperature::_ /// ``` -pub trait AttributeUpdate: Sized + Send + Sync { +pub trait AttributeUpdate: Sized + Send + Sync + Clone + Copy { /// Merging routine, i.e. how to obtain the new attribute value from the two existing ones. fn merge(attr1: Self, attr2: Self) -> Self; @@ -140,7 +143,19 @@ pub trait AttributeBind: Debug + Sized + Any { /// /// This trait contain attribute-agnostic function & methods. /// -/// The documentation of this trait describe the behavior each function & method should have. +/// ### Note on force / regular / try semantics +/// +/// We define three variants of split and merge methods (same as sews / unsews): `force`, regular, +/// and `try`. Their goal is to provide different degrees of control vs convenience when using +/// these operations. Documentation of each method shortly explains their individual quirks, +/// below is a table summarizing the differences: +/// +/// | variant | description | +/// |---------| ----------- | +/// | `try` | defensive impl, only succeding if the attribute operation is successful & the transaction isn't invalidated | +/// | regular | regular impl, which uses attribute fallback policies and will fail only if the transaction is invalidated | +/// | `force` | convenience impl, which wraps the regular impl in a transaction that retries until success | +/// pub trait UnknownAttributeStorage: Any + Debug + Downcast { /// Constructor /// @@ -170,19 +185,19 @@ pub trait UnknownAttributeStorage: Any + Debug + Downcast { #[must_use = "returned value is not used, consider removing this method call"] fn n_attributes(&self) -> usize; + // regular + + #[allow(clippy::missing_errors_doc)] /// Merge attributes at specified index /// - /// This method should serve as a wire to either `AttributeUpdate::merge` - /// or `AttributeUpdate::merge_undefined` after removing the values we wish to merge from - /// the storage. - /// /// # Arguments /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. /// - `out: DartIdentifier` -- Identifier to associate the result with. /// - `lhs_inp: DartIdentifier` -- Identifier of one attribute value to merge. /// - `rhs_inp: DartIdentifier` -- Identifier of the other attribute value to merge. /// - /// # Behavior pseudo-code + /// # Behavior (pseudo-code) /// /// ```text /// let new_val = match (attributes.remove(lhs_inp), attributes.remove(rhs_inp)) { @@ -192,30 +207,25 @@ pub trait UnknownAttributeStorage: Any + Debug + Downcast { /// } /// attributes.set(out, new_val); /// ``` - fn merge(&self, out: DartIdType, lhs_inp: DartIdType, rhs_inp: DartIdType); - - #[allow(clippy::missing_errors_doc)] - /// Transactional `merge` /// - /// # Result / Errors + /// # Return / Errors /// /// This method is meant to be called in a context where the returned `Result` is used to /// validate the transacction passed as argument. The result should not be processed manually. - fn merge_transac( + fn merge( &self, trans: &mut Transaction, out: DartIdType, lhs_inp: DartIdType, rhs_inp: DartIdType, - ) -> Result<(), StmError>; + ) -> StmResult<()>; + #[allow(clippy::missing_errors_doc)] /// Split attribute to specified indices /// - /// This method should serve as a wire to `AttributeUpdate::split` after removing the value - /// we want to split from the storage. - /// /// # Arguments /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. /// - `lhs_out: DartIdentifier` -- Identifier to associate the result with. /// - `rhs_out: DartIdentifier` -- Identifier to associate the result with. /// - `inp: DartIdentifier` -- Identifier of the attribute value to split. @@ -227,201 +237,183 @@ pub trait UnknownAttributeStorage: Any + Debug + Downcast { /// attributes[lhs_out] = val_lhs; /// attributes[rhs_out] = val_rhs; /// ``` - fn split(&self, lhs_out: DartIdType, rhs_out: DartIdType, inp: DartIdType); - - #[allow(clippy::missing_errors_doc)] - /// Transactional `split` /// - /// # Result / Errors + /// # Return / Errors /// /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn split_transac( + /// validate the transaction passed as argument. The result should not be processed manually. + fn split( &self, trans: &mut Transaction, lhs_out: DartIdType, rhs_out: DartIdType, inp: DartIdType, - ) -> Result<(), StmError>; -} + ) -> StmResult<()>; -impl_downcast!(UnknownAttributeStorage); + // force -/// Common trait implemented by generic attribute storages. -/// -/// This trait contain attribute-specific methods. -/// -/// The documentation of this trait describe the behavior each function & method should have. "ID" -/// and "index" are used interchangeably. -pub trait AttributeStorage: UnknownAttributeStorage { - /// Setter - /// - /// Set the value of an element at a given index. This operation is not affected by the initial - /// state of the edited entry. - /// - /// # Arguments - /// - /// - `index: A::IdentifierType` -- Cell index. - /// - `val: A` -- Attribute value. + /// Merge attributes at specified index /// - /// # Panics + /// This variant is equivalent to `merge`, but internally uses a transaction that will be + /// retried until validated. + fn force_merge(&self, out: DartIdType, lhs_inp: DartIdType, rhs_inp: DartIdType) { + atomically(|trans| self.merge(trans, out, lhs_inp, rhs_inp)); + } + + /// Split attribute to specified indices /// - /// The method: - /// - should panic if the index lands out of bounds - /// - may panic if the index cannot be converted to `usize` - fn set(&self, id: A::IdentifierType, val: A); + /// This variant is equivalent to `split`, but internally uses a transaction that will be + /// retried until validated. + fn force_split(&self, lhs_out: DartIdType, rhs_out: DartIdType, inp: DartIdType) { + atomically(|trans| self.split(trans, lhs_out, rhs_out, inp)); + } - #[allow(clippy::missing_errors_doc)] - /// Transactional `set` + // try + + /// Merge attributes at specified index /// - /// # Result / Errors + /// # Errors /// - /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn set_transac( + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - the merge fails (e.g. because one merging value is missing) + /// + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + fn try_merge( &self, trans: &mut Transaction, - id: A::IdentifierType, - val: A, - ) -> Result<(), StmError>; + out: DartIdType, + lhs_inp: DartIdType, + rhs_inp: DartIdType, + ) -> CMapResult<()>; - /// Setter - /// - /// Insert a value at a given empty index. - /// Otherwise, see [#Panics] section for more information. - /// - /// # Arguments - /// - /// - `index: A::IdentifierType` -- Cell index. - /// - `val: A` -- Attribute value. - /// - /// # Panics + /// Split attribute to specified indices /// - /// The method: - /// - **should panic if there is already a value associated to the specified index** - /// - should panic if the index lands out of bounds - /// - may panic if the index cannot be converted to `usize` - fn insert(&self, id: A::IdentifierType, val: A) { - assert!(self.get(id.clone()).is_none()); - self.set(id, val); - } - - #[allow(clippy::missing_errors_doc)] - /// Transactional `insert` + /// # Errors /// - /// # Result / Errors + /// This method will fail, returning an error, if: + /// - the transaction cannot be completed + /// - the split fails (e.g. because there is no value to split from) /// - /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn insert_transac( + /// The returned error can be used in conjunction with transaction control to avoid any + /// modifications in case of failure at attribute level. The user can then choose, through its + /// transaction control policy, to retry or abort as he wishes. + fn try_split( &self, trans: &mut Transaction, - id: A::IdentifierType, - val: A, - ) -> Result<(), StmError> { - assert!(self.get(id.clone()).is_none()); - self.set_transac(trans, id, val) - } + lhs_out: DartIdType, + rhs_out: DartIdType, + inp: DartIdType, + ) -> CMapResult<()>; +} - /// Getter +impl_downcast!(UnknownAttributeStorage); + +/// Common trait implemented by generic attribute storages. +/// +/// This trait contain attribute-specific methods. +/// +/// The documentation of this trait describe the behavior each function & method should have. "ID" +/// and "index" are used interchangeably. +/// +/// ### Note on force / regular semantics +/// +/// We define two variants of read / write / remove methods: `force` and regular. Their goal is to +/// provide different degrees of control vs convenience when using these operations. Documentation +/// of each method shortly explains their individual quirks, below is a table summarizing the +/// differences: +/// +/// | variant | description | +/// |---------| ------------------------------------------------------------------------------------------ | +/// | regular | regular impl, which will fail if the transaction fails | +/// | `force` | convenience impl, which wraps the regular impl in a transaction that retries until success | +pub trait AttributeStorage: UnknownAttributeStorage { + #[allow(clippy::missing_errors_doc)] + /// Read the value of an element at a given index. /// /// # Arguments /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. /// - `index: A::IdentifierType` -- Cell index. /// - /// # Return + /// # Return / Errors /// - /// The method should return: - /// - `Some(val: A)` if there is an attribute associated with the specified index, - /// - `None` if there is not. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// /// # Panics /// /// The method: /// - should panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - fn get(&self, id: A::IdentifierType) -> Option; + fn read(&self, trans: &mut Transaction, id: A::IdentifierType) -> StmResult>; #[allow(clippy::missing_errors_doc)] - /// Transactional `get` - /// - /// # Result / Errors - /// - /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn get_transac( - &self, - trans: &mut Transaction, - id: A::IdentifierType, - ) -> Result, StmError>; - - /// Setter - /// - /// Replace the value of an element at a given index. + /// Write the value of an element at a given index and return the old value. /// /// # Arguments /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. /// - `index: A::IdentifierType` -- Cell index. /// - `val: A` -- Attribute value. /// - /// # Return - /// - /// The method should return: - /// - `Some(val_old: A)` if there was an attribute associated with the specified index, - /// - `None` if there is not. + /// # Return / Errors /// - /// In both cases, the new value should be set to the one specified as argument. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// /// # Panics /// /// The method: /// - should panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - fn replace(&self, id: A::IdentifierType, val: A) -> Option; + fn write(&self, trans: &mut Transaction, id: A::IdentifierType, val: A) + -> StmResult>; #[allow(clippy::missing_errors_doc)] - /// Transactional `replace` - /// - /// # Result / Errors - /// - /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn replace_transac( - &self, - trans: &mut Transaction, - id: A::IdentifierType, - val: A, - ) -> Result, StmError>; - - /// Remove an item from the storage and return it + /// Remove the value at a given index and return it. /// /// # Arguments /// + /// - `trans: &mut Transaction` -- Transaction used for synchronization. /// - `index: A::IdentifierType` -- Cell index. /// - /// # Return + /// # Return / Errors /// - /// The method should return: - /// - `Some(val: A)` if there was an attribute associated with the specified index, - /// - `None` if there is not. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// /// # Panics /// /// The method: /// - should panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - fn remove(&self, id: A::IdentifierType) -> Option; + fn remove(&self, trans: &mut Transaction, id: A::IdentifierType) -> StmResult>; - #[allow(clippy::missing_errors_doc)] - /// Transactional `remove` + /// Read the value of an element at a given index. + /// + /// This variant is equivalent to `read`, but internally uses a transaction that will be + /// retried until validated. + fn force_read(&self, id: A::IdentifierType) -> Option { + atomically(|trans| self.read(trans, id.clone())) + } + + /// Write the value of an element at a given index and return the old value. /// - /// # Result / Errors + /// This variant is equivalent to `write`, but internally uses a transaction that will be + /// retried until validated. + fn force_write(&self, id: A::IdentifierType, val: A) -> Option; + + /// Remove the value at a given index and return it. /// - /// This method is meant to be called in a context where the returned `Result` is used to - /// validate the transacction passed as argument. The result should not be processed manually. - fn remove_transac( - &self, - trans: &mut Transaction, - id: A::IdentifierType, - ) -> Result, StmError>; + /// This variant is equivalent to `remove`, but internally uses a transaction that will be + /// retried until validated. + fn force_remove(&self, id: A::IdentifierType) -> Option { + atomically(|trans| self.remove(trans, id.clone())) + } } diff --git a/honeycomb-core/src/cmap/builder/grid/building_routines.rs b/honeycomb-core/src/cmap/builder/grid/building_routines.rs index 68d4ccd6e..a93636ebe 100644 --- a/honeycomb-core/src/cmap/builder/grid/building_routines.rs +++ b/honeycomb-core/src/cmap/builder/grid/building_routines.rs @@ -31,7 +31,7 @@ pub fn build_2d_grid( // edit geometry let vertex_id = map.vertex_id(d1); // bottom left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -53,7 +53,7 @@ pub fn build_2d_grid( // edit geometry let vertex_id = map.vertex_id(d1); // bottom left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -62,7 +62,7 @@ pub fn build_2d_grid( ), ); let vertex_id = map.vertex_id(d4); // top left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -84,7 +84,7 @@ pub fn build_2d_grid( // edit geometry let vertex_id = map.vertex_id(d1); // bottom left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -93,7 +93,7 @@ pub fn build_2d_grid( ), ); let vertex_id = map.vertex_id(d2); // bottom right - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -112,7 +112,7 @@ pub fn build_2d_grid( // edit geometry let vertex_id = map.vertex_id(d1); // bottom left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -121,7 +121,7 @@ pub fn build_2d_grid( ), ); let vertex_id = map.vertex_id(d2); // bottom right - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -130,7 +130,7 @@ pub fn build_2d_grid( ), ); let vertex_id = map.vertex_id(d4); // top left - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -139,7 +139,7 @@ pub fn build_2d_grid( ), ); let vertex_id = map.vertex_id(d3); // top right - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -225,7 +225,7 @@ pub fn build_2d_splitgrid( // edit geometry let vertex_id = map.vertex_id(d1); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -247,7 +247,7 @@ pub fn build_2d_splitgrid( // edit geometry let vertex_id = map.vertex_id(d1); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -256,7 +256,7 @@ pub fn build_2d_splitgrid( ), ); let vertex_id = map.vertex_id(d3); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -278,7 +278,7 @@ pub fn build_2d_splitgrid( // edit geometry let vertex_id = map.vertex_id(d1); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -287,7 +287,7 @@ pub fn build_2d_splitgrid( ), ); let vertex_id = map.vertex_id(d5); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -305,7 +305,7 @@ pub fn build_2d_splitgrid( let [d1, _, d3, _, d5, d6] = build_tris_core(&map, n_square_x, [x_idx, y_idx]); let vertex_id = map.vertex_id(d1); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -314,7 +314,7 @@ pub fn build_2d_splitgrid( ), ); let vertex_id = map.vertex_id(d3); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -323,7 +323,7 @@ pub fn build_2d_splitgrid( ), ); let vertex_id = map.vertex_id(d5); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( @@ -332,7 +332,7 @@ pub fn build_2d_splitgrid( ), ); let vertex_id = map.vertex_id(d6); - map.insert_vertex( + map.force_write_vertex( vertex_id, origin + Vector2( diff --git a/honeycomb-core/src/cmap/builder/io/mod.rs b/honeycomb-core/src/cmap/builder/io/mod.rs index 35cc21421..1e19c31e2 100644 --- a/honeycomb-core/src/cmap/builder/io/mod.rs +++ b/honeycomb-core/src/cmap/builder/io/mod.rs @@ -205,9 +205,18 @@ pub fn build_2d_from_vtk( // build the triangle let d0 = cmap.add_free_darts(3); let (d1, d2) = (d0 + 1, d0 + 2); - cmap.insert_vertex(d0 as VertexIdType, vertices[vids[0]]); - cmap.insert_vertex(d1 as VertexIdType, vertices[vids[1]]); - cmap.insert_vertex(d2 as VertexIdType, vertices[vids[2]]); + cmap.force_write_vertex( + d0 as VertexIdType, + vertices[vids[0]], + ); + cmap.force_write_vertex( + d1 as VertexIdType, + vertices[vids[1]], + ); + cmap.force_write_vertex( + d2 as VertexIdType, + vertices[vids[2]], + ); cmap.force_one_link(d0, d1); // edge d0 links vertices vids[0] & vids[1] cmap.force_one_link(d1, d2); // edge d1 links vertices vids[1] & vids[2] cmap.force_one_link(d2, d0); // edge d2 links vertices vids[2] & vids[0] @@ -229,7 +238,7 @@ pub fn build_2d_from_vtk( let di = d0 + i as DartIdType; let dip1 = if i == n_vertices - 1 { d0 } else { di + 1 }; - cmap.insert_vertex( + cmap.force_write_vertex( di as VertexIdType, vertices[vids[i]], ); @@ -252,10 +261,22 @@ pub fn build_2d_from_vtk( // build the quad let d0 = cmap.add_free_darts(4); let (d1, d2, d3) = (d0 + 1, d0 + 2, d0 + 3); - cmap.insert_vertex(d0 as VertexIdType, vertices[vids[0]]); - cmap.insert_vertex(d1 as VertexIdType, vertices[vids[1]]); - cmap.insert_vertex(d2 as VertexIdType, vertices[vids[2]]); - cmap.insert_vertex(d3 as VertexIdType, vertices[vids[3]]); + cmap.force_write_vertex( + d0 as VertexIdType, + vertices[vids[0]], + ); + cmap.force_write_vertex( + d1 as VertexIdType, + vertices[vids[1]], + ); + cmap.force_write_vertex( + d2 as VertexIdType, + vertices[vids[2]], + ); + cmap.force_write_vertex( + d3 as VertexIdType, + vertices[vids[3]], + ); cmap.force_one_link(d0, d1); // edge d0 links vertices vids[0] & vids[1] cmap.force_one_link(d1, d2); // edge d1 links vertices vids[1] & vids[2] cmap.force_one_link(d2, d3); // edge d2 links vertices vids[2] & vids[3] diff --git a/honeycomb-core/src/cmap/dim2/embed.rs b/honeycomb-core/src/cmap/dim2/embed.rs index a98ff0f60..2d0794a35 100644 --- a/honeycomb-core/src/cmap/dim2/embed.rs +++ b/honeycomb-core/src/cmap/dim2/embed.rs @@ -6,6 +6,8 @@ // ------ IMPORT +use stm::{StmResult, Transaction}; + use crate::prelude::{AttributeBind, AttributeUpdate, CMap2, Vertex2, VertexIdType}; use crate::{ attributes::{AttributeStorage, UnknownAttributeStorage}, @@ -22,13 +24,18 @@ impl CMap2 { self.vertices.n_attributes() } - /// Fetch vertex value associated to a given identifier. + #[allow(clippy::missing_errors_doc)] + /// Read vertex associated to a given identifier. /// /// # Arguments /// /// - `vertex_id: VertexIdentifier` -- Identifier of the given vertex. /// - /// # Return + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// /// This method return a `Option` taking the following values: /// - `Some(v: Vertex2)` if there is a vertex associated to this ID. @@ -40,11 +47,16 @@ impl CMap2 { /// - the index lands out of bounds /// - the index cannot be converted to `usize` #[must_use = "returned value is not used, consider removing this method call"] - pub fn vertex(&self, vertex_id: VertexIdType) -> Option> { - self.vertices.get(vertex_id) + pub fn read_vertex( + &self, + trans: &mut Transaction, + vertex_id: VertexIdType, + ) -> StmResult>> { + self.vertices.read(trans, vertex_id) } - /// Insert a vertex in the combinatorial map. + #[allow(clippy::missing_errors_doc)] + /// Write a vertex to a given identifier, and return its old value. /// /// This method can be interpreted as giving a value to the vertex of a specific ID. Vertices /// implicitly exist through topology, but their spatial representation is not automatically @@ -55,26 +67,45 @@ impl CMap2 { /// - `vertex_id: VertexIdentifier` -- Vertex identifier to attribute a value to. /// - `vertex: impl Into` -- Value used to create a [Vertex2] value. /// + /// # Return / Errors + /// + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// The result contains an `Option` taking the following values: + /// - `Some(v: Vertex2)` -- The old value was successfull replaced & returned + /// - `None` -- The value was successfully set + /// /// # Panics /// /// The method may panic if: /// - **there is already a vertex associated to the specified index** /// - the index lands out of bounds /// - the index cannot be converted to `usize` - pub fn insert_vertex(&self, vertex_id: VertexIdType, vertex: impl Into>) { - self.vertices.insert(vertex_id, vertex.into()); + pub fn write_vertex( + &self, + trans: &mut Transaction, + vertex_id: VertexIdType, + vertex: impl Into>, + ) -> StmResult>> { + self.vertices.write(trans, vertex_id, vertex.into()) } - #[allow(clippy::must_use_candidate)] - /// Remove a vertex from the combinatorial map. + #[allow(clippy::missing_errors_doc)] + /// Remove vertex associated to a given identifier and return it. /// /// # Arguments /// /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to remove. /// - /// # Return + /// # Return / Errors /// - /// This method return a `Option` taking the following values: + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// The result contains an `Option` taking the following values: /// - `Some(v: Vertex2)` -- The vertex was successfully removed & its value was returned /// - `None` -- The vertex was not found in the internal storage /// @@ -83,181 +114,184 @@ impl CMap2 { /// The method may panic if: /// - the index lands out of bounds /// - the index cannot be converted to `usize` - pub fn remove_vertex(&self, vertex_id: VertexIdType) -> Option> { - self.vertices.remove(vertex_id) + pub fn remove_vertex( + &self, + trans: &mut Transaction, + vertex_id: VertexIdType, + ) -> StmResult>> { + self.vertices.remove(trans, vertex_id) } - /// Try to overwrite the given vertex with a new value. - /// - /// # Arguments - /// - /// - `vertex_id: VertexIdentifier` -- Identifier of the vertex to replace. - /// - `vertex: impl>` -- New value for the vertex. - /// - /// # Return - /// - /// This method return an `Option` taking the following values: - /// - `Some(v: Vertex2)` -- The vertex was successfully overwritten & its previous value was - /// returned - /// - `None` -- The vertex was set, but no value were overwritten + #[must_use = "returned value is not used, consider removing this method call"] + /// Read vertex associated to a given identifier. /// - /// # Panics + /// This variant is equivalent to `read_vertex`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_read_vertex(&self, vertex_id: VertexIdType) -> Option> { + self.vertices.force_read(vertex_id) + } + + /// Write a vertex to a given identifier, and return its old value. /// - /// The method may panic if: - /// - the index lands out of bounds - /// - the index cannot be converted to `usize` - pub fn replace_vertex( + /// This variant is equivalent to `write_vertex`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_write_vertex( &self, vertex_id: VertexIdType, vertex: impl Into>, ) -> Option> { - self.vertices.replace(vertex_id, vertex.into()) + self.vertices.force_write(vertex_id, vertex.into()) + } + + #[allow(clippy::must_use_candidate)] + /// Remove vertex associated to a given identifier and return it. + /// + /// This variant is equivalent to `remove_vertex`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_remove_vertex(&self, vertex_id: VertexIdType) -> Option> { + self.vertices.force_remove(vertex_id) } } /// **Generic attribute-related methods** impl CMap2 { - /// Setter - /// - /// Set the value of an attribute for a given index. This operation is not affected by - /// the initial state of the edited entry. + #[allow(clippy::missing_errors_doc)] + /// Read a given attribute's value associated to a given identifier. /// /// # Arguments /// /// - `index: A::IdentifierType` -- Cell index. - /// - `val: A` -- Attribute value. /// /// ## Generic /// - /// - `A: AttributeBind + AttributeUpdate` -- Attribute kind to edit. + /// - `A: AttributeBind + AttributeUpdate` -- Attribute to read. /// - /// # Panics - /// - /// The method: - /// - should panic if the index lands out of bounds - /// - may panic if the index cannot be converted to `usize` - pub fn set_attribute(&self, id: A::IdentifierType, val: A) { - self.attributes.set_attribute::(id, val); - } - - /// Setter + /// # Return / Errors /// - /// Insert an attribute value at a given undefined index. See the panics section information - /// on behavior if the value is already defined. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// - /// # Arguments - /// - /// - `index: A::IdentifierType` -- Cell index. - /// - `val: A` -- Attribute value. - /// - /// ## Generic - /// - /// - `A: AttributeBind + AttributeUpdate` -- Attribute kind to edit. + /// The result contains an `Option` taking the following values: + /// - `Some(v: Vertex2)` -- The old value was successfull replaced & returned + /// - `None` -- The value was successfully set /// /// # Panics /// /// The method: - /// - **should panic if there is already a value associated to the specified index** /// - should panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - pub fn insert_attribute( + pub fn read_attribute( &self, + trans: &mut Transaction, id: A::IdentifierType, - val: A, - ) { - self.attributes.insert_attribute::(id, val); + ) -> StmResult> { + self.attributes.read_attribute::(trans, id) } - /// Getter + #[allow(clippy::missing_errors_doc)] + /// Write a given attribute's value to a given identifier, and return its old value. /// /// # Arguments /// /// - `index: A::IdentifierType` -- Cell index. + /// - `val: A` -- Attribute value. /// /// ## Generic /// - /// - `A: AttributeBind + AttributeUpdate` -- Attribute kind to edit. + /// - `A: AttributeBind + AttributeUpdate` -- Attribute to edit. /// - /// # Return + /// # Return / Errors /// - /// The method should return: - /// - `Some(val: A)` if there is an attribute associated with the specified index, - /// - `None` if there is not. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. + /// + /// The result contains an `Option` taking the following values: + /// - `Some(v: Vertex2)` -- The old value was successfull replaced & returned + /// - `None` -- The value was successfully set /// /// # Panics /// /// The method: /// - should panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - pub fn get_attribute( + pub fn write_attribute( &self, + trans: &mut Transaction, id: A::IdentifierType, - ) -> Option { - self.attributes.get_attribute::(id) + val: A, + ) -> StmResult> { + self.attributes.write_attribute::(trans, id, val) } - /// Setter - /// - /// Replace the value of the attribute for a given index. + #[allow(clippy::missing_errors_doc)] + /// Remove a given attribute's value from the storage and return it. /// /// # Arguments /// /// - `index: A::IdentifierType` -- Cell index. - /// - `val: A` -- Attribute value. /// /// ## Generic /// - /// - `A: AttributeBind + AttributeUpdate` -- Attribute kind to edit. + /// - `A: AttributeBind + AttributeUpdate` -- Attribute to edit. /// - /// # Return + /// # Return / Errors /// - /// The method should return: - /// - `Some(val_old: A)` if there was an attribute associated with the specified index, - /// - `None` if there is not. + /// This method is meant to be called in a context where the returned `Result` is used to + /// validate the transaction passed as argument. The result should not be processed manually, + /// only used via the `?` operator. /// - /// In both cases, the new value should be set to the one specified as argument. + /// The result contains an `Option` taking the following values: + /// - `Some(val: A)` -- The vertex was successfully removed & its value was returned + /// - `None` -- The vertex was not found in the internal storage /// /// # Panics /// /// The method: - /// - should panic if the index lands out of bounds + /// - may panic if the index lands out of bounds /// - may panic if the index cannot be converted to `usize` - pub fn replace_attribute( + pub fn remove_attribute( &self, + trans: &mut Transaction, id: A::IdentifierType, - val: A, - ) -> Option { - self.attributes.replace_attribute::(id, val) + ) -> StmResult> { + self.attributes.remove_attribute::(trans, id) } - /// Remove an attribute value from the storage and return it - /// - /// # Arguments - /// - /// - `index: A::IdentifierType` -- Cell index. + /// Read a given attribute's value associated to a given identifier. /// - /// ## Generic - /// - /// - `A: AttributeBind + AttributeUpdate` -- Attribute kind to edit. - /// - /// # Return - /// - /// The method should return: - /// - `Some(val: A)` if there was an attribute associated with the specified index, - /// - `None` if there is not. - /// - /// # Panics + /// This variant is equivalent to `read_attribute`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_read_attribute( + &self, + id: A::IdentifierType, + ) -> Option { + self.attributes.force_read_attribute::(id) + } + + /// Write a given attribute's value to a given identifier, and return its old value. /// - /// The method: - /// - may panic if the index lands out of bounds - /// - may panic if the index cannot be converted to `usize` - pub fn remove_attribute( + /// This variant is equivalent to `write_attribute`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_write_attribute( &self, id: A::IdentifierType, + val: A, ) -> Option { - self.attributes.remove_attribute::(id) + self.attributes.force_write_attribute::(id, val) } + /// Remove a given attribute's value from the storage and return it. + /// + /// This variant is equivalent to `remove_attribute`, but internally uses a transaction that will be + /// retried until validated. + pub fn force_remove_attribute( + &self, + id: A::IdentifierType, + ) -> Option { + self.attributes.force_remove_attribute::(id) + } // --- big guns /// Remove an entire attribute storage from the map. diff --git a/honeycomb-core/src/cmap/dim2/io.rs b/honeycomb-core/src/cmap/dim2/io.rs index 41316dd41..c8f318eda 100644 --- a/honeycomb-core/src/cmap/dim2/io.rs +++ b/honeycomb-core/src/cmap/dim2/io.rs @@ -93,7 +93,7 @@ where let vertices = vertex_ids .iter() .map(|vid| { - map.vertex(*vid) + map.force_read_vertex(*vid) .expect("E: found a topological vertex with no associated coordinates") }) .flat_map(|v| [v.x(), v.y(), T::zero()].into_iter()); diff --git a/honeycomb-core/src/cmap/dim2/links/one.rs b/honeycomb-core/src/cmap/dim2/links/one.rs index 03ded063f..3c720e976 100644 --- a/honeycomb-core/src/cmap/dim2/links/one.rs +++ b/honeycomb-core/src/cmap/dim2/links/one.rs @@ -1,6 +1,6 @@ //! 1D link implementations -use stm::{atomically, StmError, Transaction}; +use stm::{atomically, StmResult, Transaction}; use crate::{ cmap::{CMap2, DartIdType}, @@ -33,7 +33,7 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> StmResult<()> { self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id) } @@ -67,11 +67,7 @@ impl CMap2 { /// # Panics /// /// This method may panic if one of `lhs_dart_id` is already 1-free. - pub fn one_unlink( - &self, - trans: &mut Transaction, - lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + pub fn one_unlink(&self, trans: &mut Transaction, lhs_dart_id: DartIdType) -> StmResult<()> { self.betas.one_unlink_core(trans, lhs_dart_id) } diff --git a/honeycomb-core/src/cmap/dim2/links/two.rs b/honeycomb-core/src/cmap/dim2/links/two.rs index 25bfdc05e..339754985 100644 --- a/honeycomb-core/src/cmap/dim2/links/two.rs +++ b/honeycomb-core/src/cmap/dim2/links/two.rs @@ -1,6 +1,6 @@ //! 2D link implementations -use stm::{atomically, StmError, Transaction}; +use stm::{atomically, StmResult, Transaction}; use crate::{ cmap::{CMap2, DartIdType}, @@ -33,7 +33,7 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> StmResult<()> { self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id) } @@ -66,11 +66,7 @@ impl CMap2 { /// # Panics /// /// This method may panic if one of `lhs_dart_id` is already 2-free. - pub fn two_unlink( - &self, - trans: &mut Transaction, - lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + pub fn two_unlink(&self, trans: &mut Transaction, lhs_dart_id: DartIdType) -> StmResult<()> { self.betas.two_unlink_core(trans, lhs_dart_id) } diff --git a/honeycomb-core/src/cmap/dim2/orbits.rs b/honeycomb-core/src/cmap/dim2/orbits.rs index 7de149643..e50c5aae6 100644 --- a/honeycomb-core/src/cmap/dim2/orbits.rs +++ b/honeycomb-core/src/cmap/dim2/orbits.rs @@ -192,10 +192,10 @@ mod tests { map.force_one_link(5, 6); map.force_one_link(6, 4); map.force_two_link(2, 4); - assert!(map.replace_vertex(1, (0.0, 0.0)).is_none()); - assert!(map.replace_vertex(2, (1.0, 0.0)).is_none()); - assert!(map.replace_vertex(6, (1.0, 1.0)).is_none()); - assert!(map.replace_vertex(3, (0.0, 1.0)).is_none()); + assert!(map.force_write_vertex(1, (0.0, 0.0)).is_none()); + assert!(map.force_write_vertex(2, (1.0, 0.0)).is_none()); + assert!(map.force_write_vertex(6, (1.0, 1.0)).is_none()); + assert!(map.force_write_vertex(3, (0.0, 1.0)).is_none()); map } diff --git a/honeycomb-core/src/cmap/dim2/sews/one.rs b/honeycomb-core/src/cmap/dim2/sews/one.rs index 4776e0b7c..ed9316bea 100644 --- a/honeycomb-core/src/cmap/dim2/sews/one.rs +++ b/honeycomb-core/src/cmap/dim2/sews/one.rs @@ -1,9 +1,10 @@ //! 1D sew implementations -use stm::{atomically, StmError, Transaction}; +use stm::{atomically, StmResult, Transaction}; use crate::{ - cmap::{CMap2, DartIdType, NULL_DART_ID}, + attributes::UnknownAttributeStorage, + cmap::{CMap2, CMapResult, DartIdType, NULL_DART_ID}, prelude::CoordsFloat, }; @@ -42,7 +43,7 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> StmResult<()> { let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; if b2lhs_dart_id == NULL_DART_ID { self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id) @@ -56,13 +57,9 @@ impl CMap2 { // FIXME: VertexIdentifier should be cast to DartIdentifier self.vertices - .merge_core(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( - trans, - new_vid, - b2lhs_vid_old, - rhs_vid_old, - )?; + .merge(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?; + self.attributes + .merge_vertex_attributes(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?; Ok(()) } } @@ -91,10 +88,10 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; if b2lhs_dart_id == NULL_DART_ID { - self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id) + self.betas.one_link_core(trans, lhs_dart_id, rhs_dart_id)?; } else { let b2lhs_vid_old = self.vertex_id_transac(trans, b2lhs_dart_id)?; let rhs_vid_old = self.vertex_id_transac(trans, rhs_dart_id)?; @@ -105,15 +102,15 @@ impl CMap2 { // TODO: these should be attempts, only succeding if it's a full merge self.vertices - .merge_core(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .try_merge(trans, new_vid, b2lhs_vid_old, rhs_vid_old)?; + self.attributes.try_merge_vertex_attributes( trans, new_vid, b2lhs_vid_old, rhs_vid_old, )?; - Ok(()) } + Ok(()) } } @@ -148,11 +145,7 @@ impl CMap2 { /// The method may panic if there's a missing attribute at the splitting step. While the /// implementation could fall back to a simple unlink operation, it probably should have been /// called by the user, instead of unsew, in the first place. - pub fn one_unsew( - &self, - trans: &mut Transaction, - lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + pub fn one_unsew(&self, trans: &mut Transaction, lhs_dart_id: DartIdType) -> StmResult<()> { let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; if b2lhs_dart_id == NULL_DART_ID { self.betas.one_unlink_core(trans, lhs_dart_id)?; @@ -168,9 +161,9 @@ impl CMap2 { self.vertex_id_transac(trans, b2lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.vertices.split_core(trans, new_lhs, new_rhs, vid_old)?; + self.vertices.split(trans, new_lhs, new_rhs, vid_old)?; self.attributes - .split_vertex_attributes_transac(trans, new_lhs, new_rhs, vid_old)?; + .split_vertex_attributes(trans, new_lhs, new_rhs, vid_old)?; } Ok(()) } @@ -198,7 +191,7 @@ impl CMap2 { &self, trans: &mut Transaction, lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { let b2lhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; if b2lhs_dart_id == NULL_DART_ID { self.betas.one_unlink_core(trans, lhs_dart_id)?; @@ -214,9 +207,9 @@ impl CMap2 { self.vertex_id_transac(trans, b2lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.vertices.split_core(trans, new_lhs, new_rhs, vid_old)?; + self.vertices.try_split(trans, new_lhs, new_rhs, vid_old)?; self.attributes - .split_vertex_attributes_transac(trans, new_lhs, new_rhs, vid_old)?; + .try_split_vertex_attributes(trans, new_lhs, new_rhs, vid_old)?; } Ok(()) } diff --git a/honeycomb-core/src/cmap/dim2/sews/two.rs b/honeycomb-core/src/cmap/dim2/sews/two.rs index a1e2d683f..ceff0c918 100644 --- a/honeycomb-core/src/cmap/dim2/sews/two.rs +++ b/honeycomb-core/src/cmap/dim2/sews/two.rs @@ -1,10 +1,10 @@ //! 2D sew implementations -use stm::{atomically, StmError, Transaction}; +use stm::{atomically, StmResult, Transaction}; use crate::{ - attributes::AttributeStorage, - cmap::{CMap2, DartIdType, NULL_DART_ID}, + attributes::{AttributeStorage, UnknownAttributeStorage}, + cmap::{CMap2, CMapResult, DartIdType, NULL_DART_ID}, prelude::CoordsFloat, }; @@ -46,7 +46,7 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> StmResult<()> { let b1lhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?; let b1rhs_dart_id = self.betas[(1, rhs_dart_id)].read(trans)?; // match (is lhs 1-free, is rhs 1-free) @@ -65,23 +65,18 @@ impl CMap2 { // update the topology self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id)?; // merge vertices & attributes from the old IDs to the new one - // FIXME: VertexIdentifier should be cast to DartIdentifier let lhs_vid_new = self.vertex_id_transac(trans, lhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .merge(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; + self.attributes.merge_vertex_attributes( trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( - trans, - eid_new, - lhs_eid_old, - rhs_eid_old, - )?; + self.attributes + .merge_edge_attributes(trans, eid_new, lhs_eid_old, rhs_eid_old)?; } // update vertex associated to b1lhs/rhs (false, true) => { @@ -96,19 +91,15 @@ impl CMap2 { let rhs_vid_new = self.vertex_id_transac(trans, rhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .merge(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; + self.attributes.merge_vertex_attributes( trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( - trans, - eid_new, - lhs_eid_old, - rhs_eid_old, - )?; + self.attributes + .merge_edge_attributes(trans, eid_new, lhs_eid_old, rhs_eid_old)?; } // update both vertices making up the edge (false, false) => { @@ -128,8 +119,8 @@ impl CMap2 { Some(l_vertex), Some(b1r_vertex), // (lhs/b1rhs) vertices Some(b1l_vertex), Some(r_vertex), // (b1lhs/rhs) vertices ) = ( - self.vertices.get_core(trans, &lhs_vid_old)?, self.vertices.get_core(trans, &b1rhs_vid_old)?,// (lhs/b1rhs) - self.vertices.get_core(trans, &b1lhs_vid_old)?, self.vertices.get_core(trans, &rhs_vid_old)? // (b1lhs/rhs) + self.vertices.read(trans, lhs_vid_old)?, self.vertices.read(trans, b1rhs_vid_old)?,// (lhs/b1rhs) + self.vertices.read(trans, b1lhs_vid_old)?, self.vertices.read(trans, rhs_vid_old)? // (b1lhs/rhs) ) { let lhs_vector = b1l_vertex - l_vertex; @@ -147,32 +138,27 @@ impl CMap2 { // update the topology self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id)?; // merge vertices & attributes from the old IDs to the new one - // FIXME: VertexIdentifier should be cast to DartIdentifier let lhs_vid_new = self.vertex_id_transac(trans, lhs_dart_id)?; let rhs_vid_new = self.vertex_id_transac(trans, rhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; + .merge(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; self.vertices - .merge_core(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .merge(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; + self.attributes.merge_vertex_attributes( trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old, )?; - self.attributes.merge_vertex_attributes_transac( + self.attributes.merge_vertex_attributes( trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( - trans, - eid_new, - lhs_eid_old, - rhs_eid_old, - )?; + self.attributes + .merge_edge_attributes(trans, eid_new, lhs_eid_old, rhs_eid_old)?; } } Ok(()) @@ -203,7 +189,7 @@ impl CMap2 { trans: &mut Transaction, lhs_dart_id: DartIdType, rhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { let b1lhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?; let b1rhs_dart_id = self.betas[(1, rhs_dart_id)].read(trans)?; // match (is lhs 1-free, is rhs 1-free) @@ -222,18 +208,17 @@ impl CMap2 { // update the topology self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id)?; // merge vertices & attributes from the old IDs to the new one - // TODO: these should be attempts, only succeding if splitting a value let lhs_vid_new = self.vertex_id_transac(trans, lhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .try_merge(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; + self.attributes.try_merge_vertex_attributes( trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( + self.attributes.try_merge_edge_attributes( trans, eid_new, lhs_eid_old, @@ -250,18 +235,17 @@ impl CMap2 { // update the topology self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id)?; // merge vertices & attributes from the old IDs to the new one - // TODO: these should be attempts, only succeding if splitting a value let rhs_vid_new = self.vertex_id_transac(trans, rhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .try_merge(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; + self.attributes.try_merge_vertex_attributes( trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( + self.attributes.try_merge_edge_attributes( trans, eid_new, lhs_eid_old, @@ -283,11 +267,11 @@ impl CMap2 { // check orientation #[rustfmt::skip] if let ( - Some(l_vertex), Some(b1r_vertex), // (lhs/b1rhs) vertices - Some(b1l_vertex), Some(r_vertex), // (b1lhs/rhs) vertices + Ok(Some(l_vertex)), Ok(Some(b1r_vertex)), // (lhs/b1rhs) vertices + Ok(Some(b1l_vertex)), Ok(Some(r_vertex)), // (b1lhs/rhs) vertices ) = ( - self.vertices.get(lhs_vid_old), self.vertices.get(b1rhs_vid_old),// (lhs/b1rhs) - self.vertices.get(b1lhs_vid_old), self.vertices.get(rhs_vid_old) // (b1lhs/rhs) + self.vertices.read(trans, lhs_vid_old), self.vertices.read(trans, b1rhs_vid_old),// (lhs/b1rhs) + self.vertices.read(trans, b1lhs_vid_old), self.vertices.read(trans, rhs_vid_old) // (b1lhs/rhs) ) { let lhs_vector = b1l_vertex - l_vertex; @@ -305,27 +289,26 @@ impl CMap2 { // update the topology self.betas.two_link_core(trans, lhs_dart_id, rhs_dart_id)?; // merge vertices & attributes from the old IDs to the new one - // TODO: these should be attempts, only succeding if splitting a value let lhs_vid_new = self.vertex_id_transac(trans, lhs_dart_id)?; let rhs_vid_new = self.vertex_id_transac(trans, rhs_dart_id)?; let eid_new = self.edge_id_transac(trans, lhs_dart_id)?; self.vertices - .merge_core(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; + .try_merge(trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old)?; self.vertices - .merge_core(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; - self.attributes.merge_vertex_attributes_transac( + .try_merge(trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old)?; + self.attributes.try_merge_vertex_attributes( trans, lhs_vid_new, lhs_vid_old, b1rhs_vid_old, )?; - self.attributes.merge_vertex_attributes_transac( + self.attributes.try_merge_vertex_attributes( trans, rhs_vid_new, b1lhs_vid_old, rhs_vid_old, )?; - self.attributes.merge_edge_attributes_transac( + self.attributes.try_merge_edge_attributes( trans, eid_new, lhs_eid_old, @@ -368,11 +351,7 @@ impl CMap2 { /// The method may panic if there's a missing attribute at the splitting step. While the /// implementation could fall back to a simple unlink operation, it probably should have been /// called by the user, instead of unsew, in the first place. - pub fn two_unsew( - &self, - trans: &mut Transaction, - lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + pub fn two_unsew(&self, trans: &mut Transaction, lhs_dart_id: DartIdType) -> StmResult<()> { let rhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; let b1lhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?; let b1rhs_dart_id = self.betas[(1, rhs_dart_id)].read(trans)?; @@ -384,13 +363,8 @@ impl CMap2 { // update the topology self.betas.two_unlink_core(trans, lhs_dart_id)?; // split attributes from the old ID to the new ones - // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( - trans, - lhs_dart_id, - rhs_dart_id, - eid_old, - )?; + self.attributes + .split_edge_attributes(trans, lhs_dart_id, rhs_dart_id, eid_old)?; } (true, false) => { // fetch IDs before topology update @@ -399,18 +373,13 @@ impl CMap2 { // update the topology self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertex & attributes from the old ID to the new ones - // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( - trans, - lhs_dart_id, - rhs_dart_id, - eid_old, - )?; + self.attributes + .split_edge_attributes(trans, lhs_dart_id, rhs_dart_id, eid_old)?; let (new_lv_lhs, new_lv_rhs) = ( self.vertex_id_transac(trans, lhs_dart_id)?, self.vertex_id_transac(trans, b1rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.split_vertex_attributes( trans, new_lv_lhs, new_lv_rhs, @@ -424,18 +393,13 @@ impl CMap2 { // update the topology self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertex & attributes from the old ID to the new ones - // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( - trans, - lhs_dart_id, - rhs_dart_id, - eid_old, - )?; + self.attributes + .split_edge_attributes(trans, lhs_dart_id, rhs_dart_id, eid_old)?; let (new_rv_lhs, new_rv_rhs) = ( self.vertex_id_transac(trans, b1lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.split_vertex_attributes( trans, new_rv_lhs, new_rv_rhs, @@ -451,12 +415,8 @@ impl CMap2 { self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertices & attributes from the old ID to the new ones // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( - trans, - lhs_dart_id, - rhs_dart_id, - eid_old, - )?; + self.attributes + .split_edge_attributes(trans, lhs_dart_id, rhs_dart_id, eid_old)?; let (new_lv_lhs, new_lv_rhs) = ( self.vertex_id_transac(trans, lhs_dart_id)?, self.vertex_id_transac(trans, b1rhs_dart_id)?, @@ -465,13 +425,13 @@ impl CMap2 { self.vertex_id_transac(trans, b1lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.split_vertex_attributes( trans, new_lv_lhs, new_lv_rhs, lhs_vid_old, )?; - self.attributes.split_vertex_attributes_transac( + self.attributes.split_vertex_attributes( trans, new_rv_lhs, new_rv_rhs, @@ -505,7 +465,7 @@ impl CMap2 { &self, trans: &mut Transaction, lhs_dart_id: DartIdType, - ) -> Result<(), StmError> { + ) -> CMapResult<()> { let rhs_dart_id = self.betas[(2, lhs_dart_id)].read(trans)?; let b1lhs_dart_id = self.betas[(1, lhs_dart_id)].read(trans)?; let b1rhs_dart_id = self.betas[(1, rhs_dart_id)].read(trans)?; @@ -518,7 +478,7 @@ impl CMap2 { self.betas.two_unlink_core(trans, lhs_dart_id)?; // split attributes from the old ID to the new ones // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( + self.attributes.try_split_edge_attributes( trans, lhs_dart_id, rhs_dart_id, @@ -533,7 +493,7 @@ impl CMap2 { self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertex & attributes from the old ID to the new ones // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( + self.attributes.try_split_edge_attributes( trans, lhs_dart_id, rhs_dart_id, @@ -543,7 +503,7 @@ impl CMap2 { self.vertex_id_transac(trans, lhs_dart_id)?, self.vertex_id_transac(trans, b1rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.try_split_vertex_attributes( trans, new_lv_lhs, new_lv_rhs, @@ -558,7 +518,7 @@ impl CMap2 { self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertex & attributes from the old ID to the new ones // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( + self.attributes.try_split_edge_attributes( trans, lhs_dart_id, rhs_dart_id, @@ -568,7 +528,7 @@ impl CMap2 { self.vertex_id_transac(trans, b1lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.try_split_vertex_attributes( trans, new_rv_lhs, new_rv_rhs, @@ -584,7 +544,7 @@ impl CMap2 { self.betas.two_unlink_core(trans, lhs_dart_id)?; // split vertices & attributes from the old ID to the new ones // FIXME: VertexIdentifier should be cast to DartIdentifier - self.attributes.split_edge_attributes_transac( + self.attributes.try_split_edge_attributes( trans, lhs_dart_id, rhs_dart_id, @@ -598,13 +558,13 @@ impl CMap2 { self.vertex_id_transac(trans, b1lhs_dart_id)?, self.vertex_id_transac(trans, rhs_dart_id)?, ); - self.attributes.split_vertex_attributes_transac( + self.attributes.try_split_vertex_attributes( trans, new_lv_lhs, new_lv_rhs, lhs_vid_old, )?; - self.attributes.split_vertex_attributes_transac( + self.attributes.try_split_vertex_attributes( trans, new_rv_lhs, new_rv_rhs, diff --git a/honeycomb-core/src/cmap/dim2/structure.rs b/honeycomb-core/src/cmap/dim2/structure.rs index 684c4e3e1..34e457943 100644 --- a/honeycomb-core/src/cmap/dim2/structure.rs +++ b/honeycomb-core/src/cmap/dim2/structure.rs @@ -75,9 +75,9 @@ use crate::{ /// map.force_one_link(1, 2); // beta1(1) = 2 & beta0(2) = 1 /// map.force_one_link(2, 3); // beta1(2) = 3 & beta0(3) = 2 /// map.force_one_link(3, 1); // beta1(3) = 1 & beta0(1) = 3 -/// map.insert_vertex(1, (0.0, 0.0)); -/// map.insert_vertex(2, (1.0, 0.0)); -/// map.insert_vertex(3, (0.0, 1.0)); +/// map.force_write_vertex(1, (0.0, 0.0)); +/// map.force_write_vertex(2, (1.0, 0.0)); +/// map.force_write_vertex(3, (0.0, 1.0)); /// /// // we can go through the face using an orbit /// let mut face = Orbit2::new(&map, OrbitPolicy::Face, 1); @@ -92,9 +92,9 @@ use crate::{ /// map.force_one_link(4, 5); /// map.force_one_link(5, 6); /// map.force_one_link(6, 4); -/// map.insert_vertex(4, (0.0, 2.0)); -/// map.insert_vertex(5, (2.0, 0.0)); -/// map.insert_vertex(6, (1.0, 1.0)); +/// map.force_write_vertex(4, (0.0, 2.0)); +/// map.force_write_vertex(5, (2.0, 0.0)); +/// map.force_write_vertex(6, (1.0, 1.0)); /// /// // there should be two faces now /// let faces = map.fetch_faces(); @@ -110,11 +110,11 @@ use crate::{ /// // adjust bottom-right & top-left vertex position /// // the returned values were the average of the sewn vertices /// assert_eq!( -/// map.replace_vertex(2, Vertex2::from((1.0, 0.0))), +/// map.force_write_vertex(2, Vertex2::from((1.0, 0.0))), /// Some(Vertex2(1.5, 0.0)) /// ); /// assert_eq!( -/// map.replace_vertex(3, Vertex2::from((0.0, 1.0))), +/// map.force_write_vertex(3, Vertex2::from((0.0, 1.0))), /// Some(Vertex2(0.0, 1.5)) /// ); /// @@ -136,7 +136,7 @@ use crate::{ /// assert_eq!(&faces.identifiers, &[1]); /// // we can check the vertices /// let vertices = map.fetch_vertices(); -/// let mut value_iterator = vertices.identifiers.iter().map(|vertex_id| map.vertex(*vertex_id).unwrap()); +/// let mut value_iterator = vertices.identifiers.iter().map(|vertex_id| map.force_read_vertex(*vertex_id).unwrap()); /// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 0.0)))); // vertex ID 1 /// assert_eq!(value_iterator.next(), Some(Vertex2::from((0.0, 1.0)))); // vertex ID 3 /// assert_eq!(value_iterator.next(), Some(Vertex2::from((1.0, 0.0)))); // vertex ID 5 diff --git a/honeycomb-core/src/cmap/dim2/tests.rs b/honeycomb-core/src/cmap/dim2/tests.rs index 66f7773d3..0361717b3 100644 --- a/honeycomb-core/src/cmap/dim2/tests.rs +++ b/honeycomb-core/src/cmap/dim2/tests.rs @@ -17,9 +17,9 @@ fn example_test() { map.force_one_link(1, 2); map.force_one_link(2, 3); map.force_one_link(3, 1); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(3, (0.0, 1.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(3, (0.0, 1.0)); // checks let faces = map.fetch_faces(); @@ -36,9 +36,9 @@ fn example_test() { map.force_one_link(4, 5); map.force_one_link(5, 6); map.force_one_link(6, 4); - map.insert_vertex(4, (0.0, 2.0)); - map.insert_vertex(5, (2.0, 0.0)); - map.insert_vertex(6, (1.0, 1.0)); + map.force_write_vertex(4, (0.0, 2.0)); + map.force_write_vertex(5, (2.0, 0.0)); + map.force_write_vertex(6, (1.0, 1.0)); // checks let faces = map.fetch_faces(); @@ -56,24 +56,24 @@ fn example_test() { assert_eq!(map.beta::<2>(2), 4); assert_eq!(map.vertex_id(2), 2); assert_eq!(map.vertex_id(5), 2); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((1.5, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((1.5, 0.0))); assert_eq!(map.vertex_id(3), 3); assert_eq!(map.vertex_id(4), 3); - assert_eq!(map.vertex(3).unwrap(), Vertex2::from((0.0, 1.5))); + assert_eq!(map.force_read_vertex(3).unwrap(), Vertex2::from((0.0, 1.5))); let edges = map.fetch_edges(); assert_eq!(&edges.identifiers, &[1, 2, 3, 5, 6]); // adjust bottom-right & top-left vertex position assert_eq!( - map.replace_vertex(2, Vertex2::from((1.0, 0.0))), + map.force_write_vertex(2, Vertex2::from((1.0, 0.0))), Some(Vertex2::from((1.5, 0.0))) ); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((1.0, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((1.0, 0.0))); assert_eq!( - map.replace_vertex(3, Vertex2::from((0.0, 1.0))), + map.force_write_vertex(3, Vertex2::from((0.0, 1.0))), Some(Vertex2::from((0.0, 1.5))) ); - assert_eq!(map.vertex(3).unwrap(), Vertex2::from((0.0, 1.0))); + assert_eq!(map.force_read_vertex(3).unwrap(), Vertex2::from((0.0, 1.0))); // separate the diagonal from the rest map.force_one_unsew(1); @@ -95,10 +95,10 @@ fn example_test() { assert_eq!(&edges.identifiers, &[1, 3, 5, 6]); let vertices = map.fetch_vertices(); assert_eq!(&vertices.identifiers, &[1, 3, 5, 6]); - assert_eq!(map.vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); - assert_eq!(map.vertex(5).unwrap(), Vertex2::from((1.0, 0.0))); - assert_eq!(map.vertex(6).unwrap(), Vertex2::from((1.0, 1.0))); - assert_eq!(map.vertex(3).unwrap(), Vertex2::from((0.0, 1.0))); + assert_eq!(map.force_read_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); + assert_eq!(map.force_read_vertex(5).unwrap(), Vertex2::from((1.0, 0.0))); + assert_eq!(map.force_read_vertex(6).unwrap(), Vertex2::from((1.0, 1.0))); + assert_eq!(map.force_read_vertex(3).unwrap(), Vertex2::from((0.0, 1.0))); // darts assert_eq!(map.n_unused_darts(), 2); // there are unused darts since we removed the diagonal assert_eq!(map.beta_runtime(1, 1), 5); @@ -113,9 +113,9 @@ fn remove_vertex_twice() { // in its default state, all darts/vertices of a map are considered to be used let mut map: CMap2 = CMap2::new(4); // set vertex 1 as unused - map.remove_vertex(1).unwrap(); + map.force_remove_vertex(1).unwrap(); // set vertex 1 as unused, again - map.remove_vertex(1).unwrap(); // this should panic + map.force_remove_vertex(1).unwrap(); // this should panic } #[test] @@ -137,43 +137,43 @@ fn two_sew_complete() { let mut map: CMap2 = CMap2::new(4); map.force_one_link(1, 2); map.force_one_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); - map.insert_vertex(3, (1.0, 1.0)); - map.insert_vertex(4, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); + map.force_write_vertex(3, (1.0, 1.0)); + map.force_write_vertex(4, (1.0, 0.0)); map.force_two_sew(1, 3); - assert_eq!(map.vertex(1).unwrap(), Vertex2::from((0.5, 0.0))); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); + assert_eq!(map.force_read_vertex(1).unwrap(), Vertex2::from((0.5, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); } #[test] fn two_sew_incomplete() { let mut map: CMap2 = CMap2::new(3); map.force_one_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); - map.insert_vertex(3, (1.0, 1.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); + map.force_write_vertex(3, (1.0, 1.0)); map.force_two_sew(1, 3); // missing beta1 image for dart 3 - assert_eq!(map.vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); + assert_eq!(map.force_read_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); map.force_two_unsew(1); assert_eq!(map.add_free_dart(), 4); map.force_one_link(3, 4); map.force_two_sew(1, 3); // missing vertex for dart 4 - assert_eq!(map.vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); + assert_eq!(map.force_read_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.5, 1.0))); } #[test] fn two_sew_no_b1() { let mut map: CMap2 = CMap2::new(2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 1.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 1.0)); map.force_two_sew(1, 2); - assert_eq!(map.vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((1.0, 1.0))); + assert_eq!(map.force_read_vertex(1).unwrap(), Vertex2::from((0.0, 0.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((1.0, 1.0))); } #[test] @@ -198,10 +198,10 @@ fn two_sew_bad_orientation() { let mut map: CMap2 = CMap2::new(4); map.force_one_link(1, 2); map.force_one_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); // 1->2 goes up - map.insert_vertex(3, (1.0, 0.0)); - map.insert_vertex(4, (1.0, 1.0)); // 3->4 also goes up + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); // 1->2 goes up + map.force_write_vertex(3, (1.0, 0.0)); + map.force_write_vertex(4, (1.0, 1.0)); // 3->4 also goes up map.force_two_sew(1, 3); // panic } @@ -209,30 +209,30 @@ fn two_sew_bad_orientation() { fn one_sew_complete() { let mut map: CMap2 = CMap2::new(3); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); - map.insert_vertex(3, (0.0, 2.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); + map.force_write_vertex(3, (0.0, 2.0)); map.force_one_sew(1, 3); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.0, 1.5))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.0, 1.5))); } #[test] fn one_sew_incomplete_attributes() { let mut map: CMap2 = CMap2::new(3); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); map.force_one_sew(1, 3); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.0, 1.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.0, 1.0))); } #[test] fn one_sew_incomplete_beta() { let mut map: CMap2 = CMap2::new(3); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (0.0, 1.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (0.0, 1.0)); map.force_one_sew(1, 2); - assert_eq!(map.vertex(2).unwrap(), Vertex2::from((0.0, 1.0))); + assert_eq!(map.force_read_vertex(2).unwrap(), Vertex2::from((0.0, 1.0))); } #[test] // #[should_panic] // FIXME: find a way to test what's printed? @@ -293,15 +293,15 @@ fn io_write() { cmap.force_two_link(9, 12); // insert vertices - cmap.insert_vertex(1, (0.0, 0.0)); - cmap.insert_vertex(2, (1.0, 0.0)); - cmap.insert_vertex(6, (2.0, 0.0)); - cmap.insert_vertex(4, (0.0, 1.0)); - cmap.insert_vertex(3, (1.0, 1.0)); - cmap.insert_vertex(7, (2.0, 1.0)); - cmap.insert_vertex(16, (0.0, 2.0)); - cmap.insert_vertex(15, (1.0, 3.0)); - cmap.insert_vertex(14, (2.0, 2.0)); + cmap.force_write_vertex(1, (0.0, 0.0)); + cmap.force_write_vertex(2, (1.0, 0.0)); + cmap.force_write_vertex(6, (2.0, 0.0)); + cmap.force_write_vertex(4, (0.0, 1.0)); + cmap.force_write_vertex(3, (1.0, 1.0)); + cmap.force_write_vertex(7, (2.0, 1.0)); + cmap.force_write_vertex(16, (0.0, 2.0)); + cmap.force_write_vertex(15, (1.0, 3.0)); + cmap.force_write_vertex(14, (2.0, 2.0)); // generate VTK data let mut res = String::new(); @@ -357,9 +357,9 @@ fn sew_ordering() { let map: CMap2 = CMapBuilder::default().n_darts(5).build().unwrap(); map.force_two_link(1, 2); map.force_one_link(4, 5); - map.insert_vertex(2, Vertex2(1.0, 1.0)); - map.insert_vertex(3, Vertex2(1.0, 2.0)); - map.insert_vertex(5, Vertex2(2.0, 2.0)); + map.force_write_vertex(2, Vertex2(1.0, 1.0)); + map.force_write_vertex(3, Vertex2(1.0, 2.0)); + map.force_write_vertex(5, Vertex2(2.0, 2.0)); let arc = loom::sync::Arc::new(map); let (m1, m2) = (arc.clone(), arc.clone()); @@ -383,14 +383,14 @@ fn sew_ordering() { t2.join().unwrap(); // all path should result in the same topological result here - assert!(arc.vertex(2).is_some()); - assert!(arc.vertex(3).is_none()); - assert!(arc.vertex(5).is_none()); + assert!(arc.force_read_vertex(2).is_some()); + assert!(arc.force_read_vertex(3).is_none()); + assert!(arc.force_read_vertex(5).is_none()); assert_eq!(Orbit2::new(arc.as_ref(), OrbitPolicy::Vertex, 2).count(), 3); // the v2 can have two values though - let path1 = arc.vertex(2) == Some(Vertex2(1.5, 1.75)); - let path2 = arc.vertex(2) == Some(Vertex2(1.25, 1.5)); + let path1 = arc.force_read_vertex(2) == Some(Vertex2(1.5, 1.75)); + let path2 = arc.force_read_vertex(2) == Some(Vertex2(1.25, 1.5)); assert!(path1 || path2); }); } @@ -408,8 +408,8 @@ fn unsew_ordering() { map.force_two_link(3, 4); map.force_one_link(1, 3); map.force_one_link(4, 5); - map.insert_vertex(2, Vertex2(0.0, 0.0)); - map.insert_attribute(2, Weight(33)); + map.force_write_vertex(2, Vertex2(0.0, 0.0)); + map.force_write_attribute(2, Weight(33)); let arc = loom::sync::Arc::new(map); let (m1, m2) = (arc.clone(), arc.clone()); @@ -433,12 +433,12 @@ fn unsew_ordering() { t2.join().unwrap(); // all path should result in the same topological result here - assert!(arc.get_attribute::(2).is_some()); - assert!(arc.get_attribute::(3).is_some()); - assert!(arc.get_attribute::(5).is_some()); - let w2 = arc.get_attribute::(2).unwrap(); - let w3 = arc.get_attribute::(3).unwrap(); - let w5 = arc.get_attribute::(5).unwrap(); + assert!(arc.force_read_attribute::(2).is_some()); + assert!(arc.force_read_attribute::(3).is_some()); + assert!(arc.force_read_attribute::(5).is_some()); + let w2 = arc.force_read_attribute::(2).unwrap(); + let w3 = arc.force_read_attribute::(3).unwrap(); + let w5 = arc.force_read_attribute::(5).unwrap(); // check scenarios let path1 = w2.0 == 17 && w3.0 == 8 && w5.0 == 8; diff --git a/honeycomb-core/src/cmap/error.rs b/honeycomb-core/src/cmap/error.rs new file mode 100644 index 000000000..3bdcf34c3 --- /dev/null +++ b/honeycomb-core/src/cmap/error.rs @@ -0,0 +1,32 @@ +//! Main error type + +use stm::StmError; + +/// Convenience type alias. +pub type CMapResult = Result; + +/// `CMap` error enum. +#[derive(Debug, thiserror::Error)] +pub enum CMapError { + /// STM transaction failed. + #[error("transaction failed")] + FailedTransaction(/*#[from]*/ StmError), + /// Attribute merge failed due to missing value(s). + #[error("attribute merge failed: {0}")] + FailedAttributeMerge(&'static str), + /// Attribute split failed due to missing value. + #[error("attribute split failed: {0}")] + FailedAttributeSplit(&'static str), + /// Geometry check failed. + #[error("operation incompatible with map geometry: {0}")] + IncorrectGeometry(&'static str), + /// Accessed attribute isn't in the map storage. + #[error("unknown attribute: {0}")] + UnknownAttribute(&'static str), +} + +impl From for CMapError { + fn from(value: StmError) -> Self { + Self::FailedTransaction(value) + } +} diff --git a/honeycomb-core/src/cmap/mod.rs b/honeycomb-core/src/cmap/mod.rs index 4f9fb25b0..ae5444172 100644 --- a/honeycomb-core/src/cmap/mod.rs +++ b/honeycomb-core/src/cmap/mod.rs @@ -3,6 +3,7 @@ mod builder; mod components; mod dim2; +mod error; pub use builder::{BuilderError, CMapBuilder}; pub use components::{ @@ -16,6 +17,7 @@ pub use dim2::{ orbits::{Orbit2, OrbitPolicy}, structure::CMap2, }; +pub use error::{CMapError, CMapResult}; #[cfg(feature = "utils")] pub use builder::GridDescriptor; diff --git a/honeycomb-kernels/src/grisubal/routines/clip.rs b/honeycomb-kernels/src/grisubal/routines/clip.rs index 9f353b3a1..c3bab8341 100644 --- a/honeycomb-kernels/src/grisubal/routines/clip.rs +++ b/honeycomb-kernels/src/grisubal/routines/clip.rs @@ -45,7 +45,9 @@ fn mark_faces( let mut queue: VecDeque = (1..cmap.n_darts() as DartIdType) .filter_map(|dart_id| { // use darts on the left side of the boundary as starting points to walk through faces - if cmap.get_attribute::(dart_id) == Some(mark) && !cmap.is_free(dart_id) { + if cmap.force_read_attribute::(dart_id) == Some(mark) + && !cmap.is_free(dart_id) + { return Some(cmap.face_id(dart_id)); } None @@ -56,7 +58,7 @@ fn mark_faces( if marked.insert(face_id) { // detect orientation issues / open geometries let mut darts = Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType); - if darts.any(|did| cmap.get_attribute::(did) == Some(other)) { + if darts.any(|did| cmap.force_read_attribute::(did) == Some(other)) { return Err(GrisubalError::InconsistentOrientation( "between-boundary inconsistency", )); @@ -65,7 +67,7 @@ fn mark_faces( let darts = Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType); queue.extend(darts.filter_map(|dart_id| { if matches!( - cmap.get_attribute::(cmap.beta::<2>(dart_id)), + cmap.force_read_attribute::(cmap.beta::<2>(dart_id)), Some(Boundary::None) | None ) { return Some(cmap.face_id(cmap.beta::<2>(dart_id))); @@ -86,10 +88,10 @@ fn delete_darts( ) { let kept_boundary_components: Vec<(DartIdType, Vertex2)> = (1..cmap.n_darts() as DartIdType) .filter_map(|dart_id| { - if cmap.get_attribute::(dart_id) == Some(kept_boundary) { + if cmap.force_read_attribute::(dart_id) == Some(kept_boundary) { return Some(( dart_id, - cmap.vertex(cmap.vertex_id(dart_id)) + cmap.force_read_vertex(cmap.vertex_id(dart_id)) .expect("E: found a topological vertex with no associated coordinates"), )); } @@ -101,7 +103,7 @@ fn delete_darts( let darts: Vec = Orbit2::new(cmap, OrbitPolicy::Face, face_id as DartIdType).collect(); for &dart in &darts { - let _ = cmap.remove_vertex(cmap.vertex_id(dart)); + let _ = cmap.force_remove_vertex(cmap.vertex_id(dart)); cmap.set_betas(dart, [NULL_DART_ID; 3]); cmap.remove_free_dart(dart); } @@ -109,6 +111,6 @@ fn delete_darts( for (dart, vertex) in kept_boundary_components { cmap.set_beta::<2>(dart, NULL_DART_ID); // set beta2(dart) to 0 - cmap.insert_vertex(cmap.vertex_id(dart), vertex); + cmap.force_write_vertex(cmap.vertex_id(dart), vertex); } } diff --git a/honeycomb-kernels/src/grisubal/routines/compute_intersecs.rs b/honeycomb-kernels/src/grisubal/routines/compute_intersecs.rs index 8ca9d442c..27abaff1a 100644 --- a/honeycomb-kernels/src/grisubal/routines/compute_intersecs.rs +++ b/honeycomb-kernels/src/grisubal/routines/compute_intersecs.rs @@ -140,7 +140,7 @@ pub(crate) fn generate_intersection_data( }; // what's the vertex associated to the dart? let v_dart = cmap - .vertex(cmap.vertex_id(dart_id)) + .force_read_vertex(cmap.vertex_id(dart_id)) .expect("E: found a topological vertex with no associated coordinates"); // compute relative position of the intersection on the interecting edges // `s` is relative to the segment `v1v2`, `t` to the grid's segment (the origin being `v_dart`) @@ -188,7 +188,7 @@ pub(crate) fn generate_intersection_data( d_base + 3 }; // vertex associated to the intersected dart - let v_dart = cmap.vertex(cmap.vertex_id(dart_id)) + let v_dart = cmap.force_read_vertex(cmap.vertex_id(dart_id)) .expect("E: found a topological vertex with no associated coordinates"); // compute intersection let (_s, t) = if i.is_positive() { @@ -234,7 +234,7 @@ pub(crate) fn generate_intersection_data( // intersected dart let dart_id = if j.is_positive() { d_base + 2 } else { d_base }; // vertex associated to the intersected dart - let v_dart = cmap.vertex(cmap.vertex_id(dart_id)) + let v_dart = cmap.force_read_vertex(cmap.vertex_id(dart_id)) .expect("E: found a topological vertex with no associated coordinates"); // compute intersection let (_s, t) = if j.is_positive() { @@ -289,9 +289,9 @@ pub(crate) fn generate_intersection_data( }; let hdart_id = if j.is_positive() { d_base + 2 } else { d_base }; // associated vertices - let v_vdart = cmap.vertex(cmap.vertex_id(vdart_id)) + let v_vdart = cmap.force_read_vertex(cmap.vertex_id(vdart_id)) .expect("E: found a topological vertex with no associated coordinates"); - let v_hdart = cmap.vertex(cmap.vertex_id(hdart_id)) + let v_hdart = cmap.force_read_vertex(cmap.vertex_id(hdart_id)) .expect("E: found a topological vertex with no associated coordinates"); // compute (potential) intersections let v_coeffs = if i.is_positive() { diff --git a/honeycomb-kernels/src/grisubal/routines/insert_new_edges.rs b/honeycomb-kernels/src/grisubal/routines/insert_new_edges.rs index 1f7ec355d..71db1cea6 100644 --- a/honeycomb-kernels/src/grisubal/routines/insert_new_edges.rs +++ b/honeycomb-kernels/src/grisubal/routines/insert_new_edges.rs @@ -79,15 +79,15 @@ pub(crate) fn insert_edges_in_map(cmap: &mut CMap2, edges: &[ let mut dart_id = cmap.beta::<1>(edge_id as DartIdType); for v in intermediates { let vid = cmap.vertex_id(dart_id); - let _ = cmap.replace_vertex(vid, *v); + let _ = cmap.force_write_vertex(vid, *v); dart_id = cmap.beta::<1>(dart_id); } } let mut d_boundary = cmap.beta::<1>(*start); while d_boundary != *end { - cmap.set_attribute::(d_boundary, Boundary::Left); - cmap.set_attribute::(cmap.beta::<2>(d_boundary), Boundary::Right); + cmap.force_write_attribute::(d_boundary, Boundary::Left); + cmap.force_write_attribute::(cmap.beta::<2>(d_boundary), Boundary::Right); d_boundary = cmap.beta::<1>(d_boundary); } } diff --git a/honeycomb-kernels/src/grisubal/tests.rs b/honeycomb-kernels/src/grisubal/tests.rs index e94dbf198..79b526f62 100644 --- a/honeycomb-kernels/src/grisubal/tests.rs +++ b/honeycomb-kernels/src/grisubal/tests.rs @@ -195,19 +195,19 @@ fn regular_intersections() { assert_eq!(n_intersec, 4); // check new vertices at intersection assert_eq!( - cmap.vertex(cmap.vertex_id(cmap.beta::<1>(2))), + cmap.force_read_vertex(cmap.vertex_id(cmap.beta::<1>(2))), Some(Vertex2(1.0, 0.5)) ); assert_eq!( - cmap.vertex(cmap.vertex_id(cmap.beta::<1>(7))), + cmap.force_read_vertex(cmap.vertex_id(cmap.beta::<1>(7))), Some(Vertex2(1.5, 1.0)) ); assert_eq!( - cmap.vertex(cmap.vertex_id(cmap.beta::<1>(10))), + cmap.force_read_vertex(cmap.vertex_id(cmap.beta::<1>(10))), Some(Vertex2(1.0, 1.5)) ); assert_eq!( - cmap.vertex(cmap.vertex_id(cmap.beta::<1>(3))), + cmap.force_read_vertex(cmap.vertex_id(cmap.beta::<1>(3))), Some(Vertex2(0.5, 1.0)) ); @@ -363,7 +363,10 @@ fn corner_intersection() { assert_eq!(edges.identifiers.len(), 20); let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face1_vertices.len(), 6); assert!(face1_vertices.contains(&Vertex2(0.0, 0.0))); @@ -374,7 +377,10 @@ fn corner_intersection() { assert!(face1_vertices.contains(&Vertex2(0.0, 1.0))); let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face9_vertices.len(), 4); assert!(face9_vertices.contains(&Vertex2(0.0, 1.0))); @@ -383,7 +389,10 @@ fn corner_intersection() { assert!(face9_vertices.contains(&Vertex2(0.0, 2.0))); let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face13_vertices.len(), 3); assert!(face13_vertices.contains(&Vertex2(1.0, 1.0))); @@ -440,7 +449,10 @@ pub fn successive_straight_intersections() { // bottom row let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face1_vertices.len(), 6); assert!(face1_vertices.contains(&Vertex2(0.0, 0.0))); @@ -451,7 +463,10 @@ pub fn successive_straight_intersections() { assert!(face1_vertices.contains(&Vertex2(0.0, 1.0))); let face3_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 3) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face3_vertices.len(), 4); assert!(face3_vertices.contains(&Vertex2(0.5, 0.5))); @@ -460,7 +475,10 @@ pub fn successive_straight_intersections() { assert!(face3_vertices.contains(&Vertex2(0.5, 1.0))); let face5_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 5) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face5_vertices.len(), 4); assert!(face5_vertices.contains(&Vertex2(1.0, 0.0))); @@ -469,7 +487,10 @@ pub fn successive_straight_intersections() { assert!(face5_vertices.contains(&Vertex2(1.0, 0.5))); let face7_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 7) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face7_vertices.len(), 4); assert!(face7_vertices.contains(&Vertex2(1.0, 1.0))); @@ -478,7 +499,10 @@ pub fn successive_straight_intersections() { assert!(face7_vertices.contains(&Vertex2(1.0, 0.5))); let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face9_vertices.len(), 6); assert!(face9_vertices.contains(&Vertex2(2.0, 0.0))); @@ -489,7 +513,10 @@ pub fn successive_straight_intersections() { assert!(face9_vertices.contains(&Vertex2(2.0, 0.5))); let face12_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 12) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face12_vertices.len(), 4); assert!(face12_vertices.contains(&Vertex2(2.0, 0.5))); @@ -500,7 +527,10 @@ pub fn successive_straight_intersections() { // middle row let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face13_vertices.len(), 4); assert!(face13_vertices.contains(&Vertex2(0.0, 2.0))); @@ -509,7 +539,10 @@ pub fn successive_straight_intersections() { assert!(face13_vertices.contains(&Vertex2(0.5, 2.0))); let face14_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 14) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face14_vertices.len(), 4); assert!(face14_vertices.contains(&Vertex2(0.5, 1.0))); @@ -518,7 +551,10 @@ pub fn successive_straight_intersections() { assert!(face14_vertices.contains(&Vertex2(1.0, 1.0))); let face17_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 17) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face17_vertices.len(), 4); assert!(face17_vertices.contains(&Vertex2(1.0, 1.0))); @@ -527,7 +563,10 @@ pub fn successive_straight_intersections() { assert!(face17_vertices.contains(&Vertex2(2.0, 1.0))); let face21_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 21) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face21_vertices.len(), 4); assert!(face21_vertices.contains(&Vertex2(2.0, 1.0))); @@ -536,7 +575,10 @@ pub fn successive_straight_intersections() { assert!(face21_vertices.contains(&Vertex2(2.0, 2.0))); let face22_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 22) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face22_vertices.len(), 4); assert!(face22_vertices.contains(&Vertex2(2.5, 1.0))); @@ -547,7 +589,10 @@ pub fn successive_straight_intersections() { // top row let face25_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 25) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face25_vertices.len(), 6); assert!(face25_vertices.contains(&Vertex2(0.0, 2.0))); @@ -558,7 +603,10 @@ pub fn successive_straight_intersections() { assert!(face25_vertices.contains(&Vertex2(0.0, 3.0))); let face26_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 26) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face26_vertices.len(), 4); assert!(face26_vertices.contains(&Vertex2(0.5, 2.0))); @@ -567,7 +615,10 @@ pub fn successive_straight_intersections() { assert!(face26_vertices.contains(&Vertex2(0.5, 2.5))); let face29_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 29) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face29_vertices.len(), 4); assert!(face29_vertices.contains(&Vertex2(1.0, 2.0))); @@ -576,7 +627,10 @@ pub fn successive_straight_intersections() { assert!(face29_vertices.contains(&Vertex2(1.0, 2.5))); let face31_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 31) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face31_vertices.len(), 4); assert!(face31_vertices.contains(&Vertex2(2.0, 2.5))); @@ -585,7 +639,10 @@ pub fn successive_straight_intersections() { assert!(face31_vertices.contains(&Vertex2(2.0, 3.0))); let face33_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 33) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face33_vertices.len(), 4); assert!(face33_vertices.contains(&Vertex2(2.0, 2.0))); @@ -594,7 +651,10 @@ pub fn successive_straight_intersections() { assert!(face33_vertices.contains(&Vertex2(2.5, 2.0))); let face34_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 34) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face34_vertices.len(), 6); assert!(face34_vertices.contains(&Vertex2(2.5, 2.0))); @@ -667,91 +727,142 @@ pub fn successive_diag_intersections() { // bottom row let face1_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 1) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face1_vertices.len(), 5); let face3_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 3) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face3_vertices.len(), 3); let face5_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 5) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face5_vertices.len(), 6); let face7_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 7) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face7_vertices.len(), 6); let face9_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 9) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face9_vertices.len(), 5); let face12_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 12) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face12_vertices.len(), 3); // middle row let face13_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 13) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face13_vertices.len(), 6); let face14_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 14) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face14_vertices.len(), 6); let face17_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 17) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face17_vertices.len(), 4); let face21_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 21) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face21_vertices.len(), 6); let face22_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 22) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face22_vertices.len(), 6); // top row let face25_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 25) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face25_vertices.len(), 5); let face26_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 26) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face26_vertices.len(), 3); let face29_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 29) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face29_vertices.len(), 6); let face31_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 31) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face31_vertices.len(), 6); let face33_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 33) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face33_vertices.len(), 3); let face34_vertices: Vec> = Orbit2::new(&cmap, OrbitPolicy::Face, 34) - .map(|d| cmap.vertex(cmap.vertex_id(d)).expect("E: unreachable")) + .map(|d| { + cmap.force_read_vertex(cmap.vertex_id(d)) + .expect("E: unreachable") + }) .collect(); assert_eq!(face34_vertices.len(), 5); } diff --git a/honeycomb-kernels/src/splits/edge_multiple.rs b/honeycomb-kernels/src/splits/edge_multiple.rs index 2799db617..50fd6b8f7 100644 --- a/honeycomb-kernels/src/splits/edge_multiple.rs +++ b/honeycomb-kernels/src/splits/edge_multiple.rs @@ -46,8 +46,8 @@ use honeycomb_core::geometry::CoordsFloat; /// .build() /// .unwrap(); /// map.force_two_link(1, 2); -/// map.insert_vertex(1, (0.0, 0.0)); -/// map.insert_vertex(2, (1.0, 0.0)); +/// map.force_write_vertex(1, (0.0, 0.0)); +/// map.force_write_vertex(2, (1.0, 0.0)); /// // split /// assert!(splitn_edge(&mut map, 1, [0.25, 0.50, 0.75]).is_ok()); /// // after @@ -60,9 +60,9 @@ use honeycomb_core::geometry::CoordsFloat; /// map.beta::<1>(map.beta::<1>(map.beta::<1>(1))), /// ]; /// assert_eq!(&new_darts, &[3, 4, 5]); -/// assert_eq!(map.vertex(3), Some(Vertex2(0.25, 0.0))); -/// assert_eq!(map.vertex(4), Some(Vertex2(0.50, 0.0))); -/// assert_eq!(map.vertex(5), Some(Vertex2(0.75, 0.0))); +/// assert_eq!(map.force_read_vertex(3), Some(Vertex2(0.25, 0.0))); +/// assert_eq!(map.force_read_vertex(4), Some(Vertex2(0.50, 0.0))); +/// assert_eq!(map.force_read_vertex(5), Some(Vertex2(0.75, 0.0))); /// /// assert_eq!(map.beta::<1>(1), 3); /// assert_eq!(map.beta::<1>(3), 4); @@ -206,8 +206,8 @@ fn inner_splitn( let b1d1_old = cmap.beta::<1>(base_dart1); let (Some(v1), Some(v2)) = ( - cmap.vertex(cmap.vertex_id(base_dart1)), - cmap.vertex(cmap.vertex_id(if base_dart2 == NULL_DART_ID { + cmap.force_read_vertex(cmap.vertex_id(base_dart1)), + cmap.force_read_vertex(cmap.vertex_id(if base_dart2 == NULL_DART_ID { b1d1_old } else { base_dart2 @@ -235,7 +235,7 @@ fn inner_splitn( } let new_v = v1 + seg * t; cmap.force_one_link(prev_d, new_d); - cmap.insert_vertex(new_d, new_v); + cmap.force_write_vertex(new_d, new_v); prev_d = new_d; }); cmap.force_one_link(prev_d, b1d1_old); diff --git a/honeycomb-kernels/src/splits/edge_single.rs b/honeycomb-kernels/src/splits/edge_single.rs index d2f1b8b3d..c80694b5a 100644 --- a/honeycomb-kernels/src/splits/edge_single.rs +++ b/honeycomb-kernels/src/splits/edge_single.rs @@ -157,8 +157,8 @@ fn inner_split( let b1d1_old = cmap.beta::<1>(base_dart1); let b1d1_new = new_darts.0; let (Some(v1), Some(v2)) = ( - cmap.vertex(cmap.vertex_id(base_dart1)), - cmap.vertex(cmap.vertex_id(b1d1_old)), + cmap.force_read_vertex(cmap.vertex_id(base_dart1)), + cmap.force_read_vertex(cmap.vertex_id(b1d1_old)), ) else { return Err(SplitEdgeError::UndefinedEdge); }; @@ -170,7 +170,7 @@ fn inner_split( cmap.force_one_link(b1d1_new, b1d1_old); // insert the new vertex let seg = v2 - v1; - cmap.insert_vertex( + cmap.force_write_vertex( cmap.vertex_id(b1d1_new), midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t), ); @@ -180,8 +180,8 @@ fn inner_split( let b1d2_old = cmap.beta::<1>(base_dart2); let (b1d1_new, b1d2_new) = new_darts; let (Some(v1), Some(v2)) = ( - cmap.vertex(cmap.vertex_id(base_dart1)), - cmap.vertex(cmap.vertex_id(base_dart2)), + cmap.force_read_vertex(cmap.vertex_id(base_dart1)), + cmap.force_read_vertex(cmap.vertex_id(base_dart2)), ) else { return Err(SplitEdgeError::UndefinedEdge); }; @@ -204,7 +204,7 @@ fn inner_split( cmap.force_two_link(base_dart2, b1d1_new); // insert the new vertex let seg = v2 - v1; - cmap.insert_vertex( + cmap.force_write_vertex( cmap.vertex_id(b1d1_new), midpoint_vertex.map_or(Vertex2::average(&v1, &v2), |t| v1 + seg * t), ); diff --git a/honeycomb-kernels/src/splits/tests.rs b/honeycomb-kernels/src/splits/tests.rs index 2b76b34a4..5e8ad9d45 100644 --- a/honeycomb-kernels/src/splits/tests.rs +++ b/honeycomb-kernels/src/splits/tests.rs @@ -26,10 +26,10 @@ mod standard { map.force_two_link(1, 6); map.force_two_link(2, 5); map.force_two_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(3, (2.0, 0.0)); - map.insert_vertex(4, (3.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(3, (2.0, 0.0)); + map.force_write_vertex(4, (3.0, 0.0)); // split assert!(split_edge(&mut map, 2, None).is_ok()); // after @@ -49,9 +49,9 @@ mod standard { assert_eq!(map.vertex_id(8), 7); assert_eq!(map.vertex_id(7), 7); - assert_eq!(map.vertex(2), Some(Vertex2::from((1.0, 0.0)))); - assert_eq!(map.vertex(7), Some(Vertex2::from((1.5, 0.0)))); - assert_eq!(map.vertex(3), Some(Vertex2::from((2.0, 0.0)))); + assert_eq!(map.force_read_vertex(2), Some(Vertex2::from((1.0, 0.0)))); + assert_eq!(map.force_read_vertex(7), Some(Vertex2::from((1.5, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((2.0, 0.0)))); } #[test] @@ -62,8 +62,8 @@ mod standard { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split assert!(split_edge(&mut map, 1, Some(0.6)).is_ok()); // after @@ -79,9 +79,9 @@ mod standard { assert_eq!(map.vertex_id(3), 3); assert_eq!(map.vertex_id(4), 3); - assert_eq!(map.vertex(1), Some(Vertex2::from((0.0, 0.0)))); - assert_eq!(map.vertex(3), Some(Vertex2::from((0.6, 0.0)))); - assert_eq!(map.vertex(2), Some(Vertex2::from((1.0, 0.0)))); + assert_eq!(map.force_read_vertex(1), Some(Vertex2::from((0.0, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((0.6, 0.0)))); + assert_eq!(map.force_read_vertex(2), Some(Vertex2::from((1.0, 0.0)))); } #[test] @@ -90,8 +90,8 @@ mod standard { // 1 -----> 2 -> let mut map: CMap2 = newmap(2); map.force_one_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split assert!(split_edge(&mut map, 1, None).is_ok()); // after @@ -99,7 +99,7 @@ mod standard { assert_eq!(map.beta::<1>(1), 3); assert_eq!(map.beta::<1>(3), 2); assert_eq!(map.beta::<2>(3), NULL_DART_ID); - assert_eq!(map.vertex(3), Some(Vertex2::from((0.5, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((0.5, 0.0)))); } #[test] @@ -109,8 +109,8 @@ mod standard { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - // map.insert_vertex(2, (1.0, 0.0)); missing vertex! + map.force_write_vertex(1, (0.0, 0.0)); + // map.force_write_vertex(2, (1.0, 0.0)); missing vertex! // split assert!(split_edge(&mut map, 1, None).is_err_and(|e| e == SplitEdgeError::UndefinedEdge)); } @@ -131,10 +131,10 @@ mod standard { map.force_two_link(1, 6); map.force_two_link(2, 5); map.force_two_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(3, (2.0, 0.0)); - map.insert_vertex(4, (3.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(3, (2.0, 0.0)); + map.force_write_vertex(4, (3.0, 0.0)); // split assert!(splitn_edge(&mut map, 2, [0.25, 0.50, 0.75]).is_ok()); // after @@ -147,9 +147,9 @@ mod standard { map.beta::<1>(map.beta::<1>(map.beta::<1>(2))), ]; assert_eq!(&new_darts, &[7, 8, 9]); - assert_eq!(map.vertex(7), Some(Vertex2(1.25, 0.0))); - assert_eq!(map.vertex(8), Some(Vertex2(1.50, 0.0))); - assert_eq!(map.vertex(9), Some(Vertex2(1.75, 0.0))); + assert_eq!(map.force_read_vertex(7), Some(Vertex2(1.25, 0.0))); + assert_eq!(map.force_read_vertex(8), Some(Vertex2(1.50, 0.0))); + assert_eq!(map.force_read_vertex(9), Some(Vertex2(1.75, 0.0))); assert_eq!(map.beta::<1>(2), 7); assert_eq!(map.beta::<1>(7), 8); @@ -175,8 +175,8 @@ mod standard { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split assert!(splitn_edge(&mut map, 1, [0.25, 0.50, 0.75]).is_ok()); // after @@ -212,8 +212,8 @@ mod standard { // 1 -----> 2 -> let mut map: CMap2 = newmap(2); map.force_one_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split assert!(splitn_edge(&mut map, 1, [0.25, 0.50, 0.75]).is_ok()); let new_darts = [ @@ -224,9 +224,9 @@ mod standard { // after // 1 -> 3 -> 4 -> 5 -> 2 -> assert_eq!(&new_darts, &[3, 4, 5]); - assert_eq!(map.vertex(3), Some(Vertex2(0.25, 0.0))); - assert_eq!(map.vertex(4), Some(Vertex2(0.50, 0.0))); - assert_eq!(map.vertex(5), Some(Vertex2(0.75, 0.0))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2(0.25, 0.0))); + assert_eq!(map.force_read_vertex(4), Some(Vertex2(0.50, 0.0))); + assert_eq!(map.force_read_vertex(5), Some(Vertex2(0.75, 0.0))); assert_eq!(map.beta::<1>(1), 3); assert_eq!(map.beta::<1>(3), 4); @@ -246,8 +246,8 @@ mod standard { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - // map.insert_vertex(2, (1.0, 0.0)); missing vertex! + map.force_write_vertex(1, (0.0, 0.0)); + // map.force_write_vertex(2, (1.0, 0.0)); missing vertex! // split assert!(splitn_edge(&mut map, 1, [0.25, 0.50, 0.75]) .is_err_and(|e| e == SplitEdgeError::UndefinedEdge)); @@ -271,10 +271,10 @@ mod noalloc { map.force_two_link(1, 6); map.force_two_link(2, 5); map.force_two_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(3, (2.0, 0.0)); - map.insert_vertex(4, (3.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(3, (2.0, 0.0)); + map.force_write_vertex(4, (3.0, 0.0)); // split let nds = map.add_free_darts(2); assert!(split_edge_noalloc(&mut map, 2, (nds, nds + 1), None).is_ok()); @@ -295,9 +295,9 @@ mod noalloc { assert_eq!(map.vertex_id(8), 7); assert_eq!(map.vertex_id(7), 7); - assert_eq!(map.vertex(2), Some(Vertex2::from((1.0, 0.0)))); - assert_eq!(map.vertex(7), Some(Vertex2::from((1.5, 0.0)))); - assert_eq!(map.vertex(3), Some(Vertex2::from((2.0, 0.0)))); + assert_eq!(map.force_read_vertex(2), Some(Vertex2::from((1.0, 0.0)))); + assert_eq!(map.force_read_vertex(7), Some(Vertex2::from((1.5, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((2.0, 0.0)))); } #[test] @@ -308,8 +308,8 @@ mod noalloc { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split let nds = map.add_free_darts(2); assert!(split_edge_noalloc(&mut map, 1, (nds, nds + 1), Some(0.6)).is_ok()); @@ -326,9 +326,9 @@ mod noalloc { assert_eq!(map.vertex_id(3), 3); assert_eq!(map.vertex_id(4), 3); - assert_eq!(map.vertex(1), Some(Vertex2::from((0.0, 0.0)))); - assert_eq!(map.vertex(3), Some(Vertex2::from((0.6, 0.0)))); - assert_eq!(map.vertex(2), Some(Vertex2::from((1.0, 0.0)))); + assert_eq!(map.force_read_vertex(1), Some(Vertex2::from((0.0, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((0.6, 0.0)))); + assert_eq!(map.force_read_vertex(2), Some(Vertex2::from((1.0, 0.0)))); } #[test] @@ -337,8 +337,8 @@ mod noalloc { // 1 -----> 2 -> let mut map: CMap2 = newmap(2); map.force_one_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split let nd = map.add_free_dart(); // a single dart is enough in this case assert!(split_edge_noalloc(&mut map, 1, (nd, NULL_DART_ID), None).is_ok()); @@ -347,7 +347,7 @@ mod noalloc { assert_eq!(map.beta::<1>(1), 3); assert_eq!(map.beta::<1>(3), 2); assert_eq!(map.beta::<2>(3), NULL_DART_ID); - assert_eq!(map.vertex(3), Some(Vertex2::from((0.5, 0.0)))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2::from((0.5, 0.0)))); } #[test] @@ -357,8 +357,8 @@ mod noalloc { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - // map.insert_vertex(2, (1.0, 0.0)); missing vertex! + map.force_write_vertex(1, (0.0, 0.0)); + // map.force_write_vertex(2, (1.0, 0.0)); missing vertex! // split let nds = map.add_free_darts(2); assert!(split_edge_noalloc(&mut map, 1, (nds, nds + 1), None) @@ -381,10 +381,10 @@ mod noalloc { map.force_two_link(1, 6); map.force_two_link(2, 5); map.force_two_link(3, 4); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); - map.insert_vertex(3, (2.0, 0.0)); - map.insert_vertex(4, (3.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); + map.force_write_vertex(3, (2.0, 0.0)); + map.force_write_vertex(4, (3.0, 0.0)); // split let nds = map.add_free_darts(6); let new_darts = (nds..nds + 6).collect::>(); @@ -394,9 +394,9 @@ mod noalloc { // 1 2 -7-8-9- 3 4 // ---1--> ---3--> assert_eq!(&new_darts[0..3], &[7, 8, 9]); - assert_eq!(map.vertex(7), Some(Vertex2(1.25, 0.0))); - assert_eq!(map.vertex(8), Some(Vertex2(1.50, 0.0))); - assert_eq!(map.vertex(9), Some(Vertex2(1.75, 0.0))); + assert_eq!(map.force_read_vertex(7), Some(Vertex2(1.25, 0.0))); + assert_eq!(map.force_read_vertex(8), Some(Vertex2(1.50, 0.0))); + assert_eq!(map.force_read_vertex(9), Some(Vertex2(1.75, 0.0))); assert_eq!(map.beta::<1>(2), 7); assert_eq!(map.beta::<1>(7), 8); @@ -422,8 +422,8 @@ mod noalloc { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split let nds = map.add_free_darts(6); let new_darts = (nds..nds + 6).collect::>(); @@ -433,9 +433,9 @@ mod noalloc { // 1 -3-4-5- 2 // >->->-> assert_eq!(&new_darts[0..3], &[3, 4, 5]); - assert_eq!(map.vertex(3), Some(Vertex2(0.25, 0.0))); - assert_eq!(map.vertex(4), Some(Vertex2(0.50, 0.0))); - assert_eq!(map.vertex(5), Some(Vertex2(0.75, 0.0))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2(0.25, 0.0))); + assert_eq!(map.force_read_vertex(4), Some(Vertex2(0.50, 0.0))); + assert_eq!(map.force_read_vertex(5), Some(Vertex2(0.75, 0.0))); assert_eq!(map.beta::<1>(1), 3); assert_eq!(map.beta::<1>(3), 4); @@ -459,8 +459,8 @@ mod noalloc { // 1 -----> 2 -> let mut map: CMap2 = newmap(2); map.force_one_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - map.insert_vertex(2, (1.0, 0.0)); + map.force_write_vertex(1, (0.0, 0.0)); + map.force_write_vertex(2, (1.0, 0.0)); // split let nds = map.add_free_darts(3); assert!(splitn_edge_no_alloc( @@ -480,9 +480,9 @@ mod noalloc { // after // 1 -> 3 -> 4 -> 5 -> 2 -> // assert_eq!(&new_darts, &[3, 4, 5]); - assert_eq!(map.vertex(3), Some(Vertex2(0.25, 0.0))); - assert_eq!(map.vertex(4), Some(Vertex2(0.50, 0.0))); - assert_eq!(map.vertex(5), Some(Vertex2(0.75, 0.0))); + assert_eq!(map.force_read_vertex(3), Some(Vertex2(0.25, 0.0))); + assert_eq!(map.force_read_vertex(4), Some(Vertex2(0.50, 0.0))); + assert_eq!(map.force_read_vertex(5), Some(Vertex2(0.75, 0.0))); assert_eq!(map.beta::<1>(1), 3); assert_eq!(map.beta::<1>(3), 4); @@ -502,8 +502,8 @@ mod noalloc { // ---1--> let mut map: CMap2 = newmap(2); map.force_two_link(1, 2); - map.insert_vertex(1, (0.0, 0.0)); - // map.insert_vertex(2, (1.0, 0.0)); missing vertex! + map.force_write_vertex(1, (0.0, 0.0)); + // map.force_write_vertex(2, (1.0, 0.0)); missing vertex! // split let nds = map.add_free_darts(6); assert!(splitn_edge_no_alloc( diff --git a/honeycomb-kernels/src/triangulation/fan.rs b/honeycomb-kernels/src/triangulation/fan.rs index e4733f472..39712bae2 100644 --- a/honeycomb-kernels/src/triangulation/fan.rs +++ b/honeycomb-kernels/src/triangulation/fan.rs @@ -85,7 +85,7 @@ pub fn process_cell( // if we found a dart from the previous computations, it means the polygon is "fannable" // THIS CANNOT BE PARALLELIZED AS IS let b0_sdart = cmap.beta::<0>(*sdart); - let v0 = cmap.vertex(cmap.vertex_id(*sdart)).unwrap(); + let v0 = cmap.force_read_vertex(cmap.vertex_id(*sdart)).unwrap(); cmap.force_one_unsew(b0_sdart); let mut d0 = *sdart; for sl in new_darts.chunks_exact(2) { @@ -100,7 +100,7 @@ pub fn process_cell( d0 = *d2; } cmap.force_one_sew(cmap.beta::<1>(cmap.beta::<1>(d0)), d0); - cmap.replace_vertex(cmap.vertex_id(*sdart), v0); + cmap.force_write_vertex(cmap.vertex_id(*sdart), v0); } else { // println!("W: face {face_id} isn't fannable -- skipping triangulation"); return Err(TriangulateError::NonFannable); @@ -159,7 +159,7 @@ pub fn process_convex_cell( // THIS CANNOT BE PARALLELIZED AS IS let b0_sdart = cmap.beta::<0>(sdart); - let v0 = cmap.vertex(cmap.vertex_id(sdart)).unwrap(); + let v0 = cmap.force_read_vertex(cmap.vertex_id(sdart)).unwrap(); cmap.force_one_unsew(b0_sdart); let mut d0 = sdart; for sl in new_darts.chunks_exact(2) { @@ -174,7 +174,7 @@ pub fn process_convex_cell( d0 = *d2; } cmap.force_one_sew(cmap.beta::<1>(cmap.beta::<1>(d0)), d0); - cmap.replace_vertex(cmap.vertex_id(sdart), v0); + cmap.force_write_vertex(cmap.vertex_id(sdart), v0); Ok(()) } diff --git a/honeycomb-kernels/src/triangulation/mod.rs b/honeycomb-kernels/src/triangulation/mod.rs index a564d2b41..1fa52a62a 100644 --- a/honeycomb-kernels/src/triangulation/mod.rs +++ b/honeycomb-kernels/src/triangulation/mod.rs @@ -117,7 +117,7 @@ fn fetch_face_vertices( ) -> Result>, TriangulateError> { let tmp = darts .iter() - .map(|dart_id| cmap.vertex(cmap.vertex_id(*dart_id))); + .map(|dart_id| cmap.force_read_vertex(cmap.vertex_id(*dart_id))); if tmp.clone().any(|v| v.is_none()) { Err(TriangulateError::UndefinedFace( "one or more undefined vertices", diff --git a/honeycomb-kernels/src/triangulation/tests.rs b/honeycomb-kernels/src/triangulation/tests.rs index a9ddf3bb1..a9f1d5439 100644 --- a/honeycomb-kernels/src/triangulation/tests.rs +++ b/honeycomb-kernels/src/triangulation/tests.rs @@ -55,23 +55,23 @@ fn generate_map() -> CMap2 { cmap.force_two_link(18, 28); // geometry - cmap.insert_vertex(1, (1.0, 0.0)); - cmap.insert_vertex(2, (2.0, 0.0)); - cmap.insert_vertex(3, (2.5, 0.5)); - cmap.insert_vertex(4, (2.0, 1.0)); - cmap.insert_vertex(5, (1.0, 1.0)); - cmap.insert_vertex(6, (0.5, 0.5)); - cmap.insert_vertex(9, (3.0, 1.0)); - cmap.insert_vertex(10, (3.0, 2.0)); - cmap.insert_vertex(11, (2.5, 1.0)); - cmap.insert_vertex(12, (2.0, 2.0)); - cmap.insert_vertex(16, (1.0, 2.0)); - cmap.insert_vertex(20, (3.0, 3.0)); - cmap.insert_vertex(21, (2.7, 3.0)); - cmap.insert_vertex(22, (2.7, 2.3)); - cmap.insert_vertex(23, (1.3, 2.3)); - cmap.insert_vertex(24, (1.3, 3.0)); - cmap.insert_vertex(25, (1.0, 3.0)); + cmap.force_write_vertex(1, (1.0, 0.0)); + cmap.force_write_vertex(2, (2.0, 0.0)); + cmap.force_write_vertex(3, (2.5, 0.5)); + cmap.force_write_vertex(4, (2.0, 1.0)); + cmap.force_write_vertex(5, (1.0, 1.0)); + cmap.force_write_vertex(6, (0.5, 0.5)); + cmap.force_write_vertex(9, (3.0, 1.0)); + cmap.force_write_vertex(10, (3.0, 2.0)); + cmap.force_write_vertex(11, (2.5, 1.0)); + cmap.force_write_vertex(12, (2.0, 2.0)); + cmap.force_write_vertex(16, (1.0, 2.0)); + cmap.force_write_vertex(20, (3.0, 3.0)); + cmap.force_write_vertex(21, (2.7, 3.0)); + cmap.force_write_vertex(22, (2.7, 2.3)); + cmap.force_write_vertex(23, (1.3, 2.3)); + cmap.force_write_vertex(24, (1.3, 3.0)); + cmap.force_write_vertex(25, (1.0, 3.0)); cmap } diff --git a/honeycomb-render/src/capture/mod.rs b/honeycomb-render/src/capture/mod.rs index 8561fe36a..bdf9678da 100644 --- a/honeycomb-render/src/capture/mod.rs +++ b/honeycomb-render/src/capture/mod.rs @@ -72,7 +72,7 @@ impl Capture { .map(|(idx, vid)| { index_map.insert(*vid, idx); let v = cmap - .vertex(*vid) + .force_read_vertex(*vid) .expect("E: found a topological vertex with no associated coordinates"); // sane unwraps; will crash if the coordinates cannot be converted to f32 Vec3::from((v.0.to_f32().unwrap(), v.1.to_f32().unwrap(), 0.0))