Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ allocator-api2 = { version = "0.2.21", default-features = false, features = ["al
bumpalo = { version = "3.17.0", features = ["allocator-api2", "collections"] }
daft = { version = "0.1.3", default-features = false }
equivalent = "1.0.2"
flex_array = { version = "0.2.5", features = ["alloc_api2"] }
foldhash = "0.1.5"
# We have to turn on hashbrown's allocator-api2 feature even if we don't expose
# it in our public API. There's no way to refer to the hashbrown Allocator trait
Expand Down
1 change: 1 addition & 0 deletions crates/iddqd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ workspace = true
allocator-api2 = { workspace = true }
daft = { workspace = true, optional = true }
equivalent.workspace = true
flex_array.workspace = true
foldhash = { workspace = true, optional = true }
hashbrown.workspace = true
ref-cast = { workspace = true, optional = true }
Expand Down
237 changes: 237 additions & 0 deletions crates/iddqd/src/id_index_map/daft_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
//! `Diffable` implementation.

use super::{IdHashItem, IdIndexMap};
use crate::{
DefaultHashBuilder,
support::{
alloc::{Allocator, Global},
daft_utils::IdLeaf,
},
};
use core::hash::{BuildHasher, Hash};
use daft::Diffable;
use equivalent::Equivalent;

impl<T: IdHashItem, S: Clone + BuildHasher, A: Clone + Allocator> Diffable
for IdIndexMap<T, S, A>
{
type Diff<'a>
= Diff<'a, T, S, A>
where
T: 'a,
S: 'a,
A: 'a;

fn diff<'daft>(&'daft self, other: &'daft Self) -> Self::Diff<'daft> {
// TODO: Implement
todo!()
}
}

/// A diff of two [`IdIndexMap`]s.
///
/// Generated by the [`Diffable`] implementation for [`IdIndexMap`].
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "default-hasher")] {
/// use daft::Diffable;
/// use iddqd::{IdHashItem, IdIndexMap, id_upcast};
///
/// #[derive(Eq, PartialEq)]
/// struct Item {
/// id: String,
/// value: u32,
/// }
///
/// impl IdHashItem for Item {
/// type Key<'a> = &'a str;
/// fn key(&self) -> Self::Key<'_> {
/// &self.id
/// }
/// id_upcast!();
/// }
///
/// // Create two IdIndexMaps with overlapping items.
/// let mut map1 = IdIndexMap::new();
/// map1.insert_unique(Item { id: "a".to_string(), value: 1 });
/// map1.insert_unique(Item { id: "b".to_string(), value: 2 });
///
/// let mut map2 = IdIndexMap::new();
/// map2.insert_unique(Item { id: "b".to_string(), value: 3 });
/// map2.insert_unique(Item { id: "c".to_string(), value: 4 });
///
/// // Compute the diff between the two maps.
/// let diff = map1.diff(&map2);
///
/// // "a" is removed.
/// assert!(diff.removed.contains_key("a"));
/// // "b" is modified (value changed from 2 to 3).
/// assert!(diff.is_modified("b"));
/// // "c" is added.
/// assert!(diff.added.contains_key("c"));
/// # }
/// ```
///
/// [`Diffable`]: daft::Diffable
pub struct Diff<
'daft,
T: ?Sized + IdHashItem,
S = DefaultHashBuilder,
A: Allocator = Global,
> {
/// Entries common to both maps.
///
/// Items are stored as [`IdLeaf`]s to references.
pub common: IdIndexMap<IdLeaf<&'daft T>, S, A>,

/// Added entries.
pub added: IdIndexMap<&'daft T, S, A>,

/// Removed entries.
pub removed: IdIndexMap<&'daft T, S, A>,
}

impl<'daft, T: ?Sized + IdHashItem, S: Default, A: Allocator + Default> Default
for Diff<'daft, T, S, A>
{
fn default() -> Self {
Self {
common: IdIndexMap::default(),
added: IdIndexMap::default(),
removed: IdIndexMap::default(),
}
}
}

#[cfg(all(feature = "default-hasher", feature = "allocator-api2"))]
impl<'daft, T: ?Sized + IdHashItem> Diff<'daft, T> {
/// Creates a new, empty `IdIndexMapDiff`
pub fn new() -> Self {
Self {
common: IdIndexMap::new(),
added: IdIndexMap::new(),
removed: IdIndexMap::new(),
}
}
}

#[cfg(feature = "allocator-api2")]
impl<'daft, T: ?Sized + IdHashItem, S: Clone + BuildHasher> Diff<'daft, T, S> {
/// Creates a new `IdIndexMapDiff` with the given hasher.
pub fn with_hasher(hasher: S) -> Self {
Self {
common: IdIndexMap::with_hasher(hasher.clone()),
added: IdIndexMap::with_hasher(hasher.clone()),
removed: IdIndexMap::with_hasher(hasher),
}
}
}

impl<
'daft,
T: ?Sized + IdHashItem,
S: Clone + BuildHasher,
A: Clone + Allocator,
> Diff<'daft, T, S, A>
{
/// Creates a new `IdIndexMapDiff` with the given hasher and allocator.
pub fn with_hasher_in(hasher: S, alloc: A) -> Self {
Self {
common: IdIndexMap::with_hasher_in(hasher.clone(), alloc.clone()),
added: IdIndexMap::with_hasher_in(hasher.clone(), alloc.clone()),
removed: IdIndexMap::with_hasher_in(hasher, alloc),
}
}
}

impl<'daft, T: ?Sized + IdHashItem + Eq, S: Clone + BuildHasher, A: Allocator>
Diff<'daft, T, S, A>
{
/// Returns an iterator over unchanged keys and values.
pub fn unchanged(&self) -> impl Iterator<Item = &'daft T> + '_ {
// TODO: Implement
todo!()
}

/// Returns true if the item corresponding to the key is unchanged.
pub fn is_unchanged<'a, Q>(&'a self, key: &Q) -> bool
where
Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
{
// TODO: Implement
todo!()
}

/// Returns the value associated with the key if it is unchanged,
/// otherwise `None`.
pub fn get_unchanged<'a, Q>(&'a self, key: &Q) -> Option<&'daft T>
where
Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
{
// TODO: Implement
todo!()
}

/// Returns an iterator over modified keys and values.
pub fn modified(&self) -> impl Iterator<Item = IdLeaf<&'daft T>> + '_ {
// TODO: Implement
todo!()
}

/// Returns true if the value corresponding to the key is
/// modified.
pub fn is_modified<'a, Q>(&'a self, key: &Q) -> bool
where
Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
{
// TODO: Implement
todo!()
}

/// Returns the [`IdLeaf`] associated with the key if it is modified,
/// otherwise `None`.
pub fn get_modified<'a, Q>(&'a self, key: &Q) -> Option<IdLeaf<&'daft T>>
where
Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
{
// TODO: Implement
todo!()
}

/// Returns an iterator over modified keys and values, performing
/// a diff on the values.
///
/// This is useful when `T::Diff` is a complex type, not just a
/// [`daft::Leaf`].
pub fn modified_diff(&self) -> impl Iterator<Item = T::Diff<'daft>> + '_
where
T: Diffable,
{
// TODO: Implement
todo!()
}
}

impl<T: IdHashItem> IdHashItem for IdLeaf<T> {
type Key<'a>
= T::Key<'a>
where
T: 'a;

fn key(&self) -> Self::Key<'_> {
let before_key = self.before().key();
if before_key != self.after().key() {
panic!("key is different between before and after");
}
self.before().key()
}

#[inline]
fn upcast_key<'short, 'long: 'short>(
long: Self::Key<'long>,
) -> Self::Key<'short> {
T::upcast_key(long)
}
}
Loading
Loading