Skip to content

Commit

Permalink
feat: add DependencySnapshot (#44)
Browse files Browse the repository at this point in the history
* feat: add DependencySnapshot

* chore: add more interning methods

* fix: tests

* fix: missing snapshot
  • Loading branch information
baszalmstra authored Jun 10, 2024
1 parent 1777fd0 commit 7b9e911
Show file tree
Hide file tree
Showing 9 changed files with 703 additions and 10 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[workspace]
members = ["tools/solve-snapshot"]

[package]
name = "resolvo"
version = "0.6.0"
Expand Down Expand Up @@ -33,3 +36,4 @@ proptest = "1.2"
tracing-test = { version = "0.2.4", features = ["no-env-filter"] }
tokio = { version = "1.35.1", features = ["time", "rt"] }
resolvo = { path = ".", features = ["tokio"] }
serde_json = "1.0"
8 changes: 8 additions & 0 deletions src/internal/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::{internal::arena::ArenaId, Interner};
/// The id associated to a package name
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct NameId(u32);

impl ArenaId for NameId {
Expand All @@ -20,6 +22,8 @@ impl ArenaId for NameId {
/// The id associated with a generic string
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct StringId(u32);

impl ArenaId for StringId {
Expand All @@ -35,6 +39,8 @@ impl ArenaId for StringId {
/// The id associated with a VersionSet.
#[repr(transparent)]
#[derive(Clone, Default, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct VersionSetId(u32);

impl ArenaId for VersionSetId {
Expand All @@ -50,6 +56,8 @@ impl ArenaId for VersionSetId {
/// The id associated to a solvable
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct SolvableId(u32);

/// Internally used id for solvables that can also represent root and null.
Expand Down
60 changes: 51 additions & 9 deletions src/internal/mapping.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use std::{cmp, iter::FusedIterator, marker::PhantomData};

use crate::internal::arena::ArenaId;
use std::cmp;
use std::iter::FusedIterator;
use std::marker::PhantomData;

const VALUES_PER_CHUNK: usize = 128;

/// A `Mapping<TValue>` holds a collection of `TValue`s that can be addressed by `TId`s. You can
/// think of it as a HashMap<TId, TValue>, optimized for the case in which we know the `TId`s are
/// contiguous.
/// A `Mapping<TValue>` holds a collection of `TValue`s that can be addressed by
/// `TId`s. You can think of it as a HashMap<TId, TValue>, optimized for the
/// case in which we know the `TId`s are contiguous.
#[derive(Clone)]
pub struct Mapping<TId, TValue> {
chunks: Vec<[Option<TValue>; VALUES_PER_CHUNK]>,
len: usize,
max: usize,
_phantom: PhantomData<TId>,
}

Expand All @@ -35,6 +36,7 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
Self {
chunks,
len: 0,
max: 0,
_phantom: Default::default(),
}
}
Expand All @@ -49,7 +51,8 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {

/// Insert into the mapping with the specific value
pub fn insert(&mut self, id: TId, value: TValue) {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
let idx = id.to_usize();
let (chunk, offset) = Self::chunk_and_offset(idx);

// Resize to fit if needed
if chunk >= self.chunks.len() {
Expand All @@ -58,6 +61,7 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
}
self.chunks[chunk][offset] = Some(value);
self.len += 1;
self.max = self.max.max(idx);
}

/// Get a specific value in the mapping with bound checks
Expand Down Expand Up @@ -95,7 +99,9 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
/// Get a specific value in the mapping without bound checks
///
/// # Safety
/// The caller must uphold most of the safety requirements for `get_unchecked`. i.e. the id having been inserted into the Mapping before.
/// The caller must uphold most of the safety requirements for
/// `get_unchecked`. i.e. the id having been inserted into the Mapping
/// before.
pub unsafe fn get_unchecked(&self, id: TId) -> &TValue {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
self.chunks
Expand All @@ -108,7 +114,9 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
/// Get a specific value in the mapping without bound checks
///
/// # Safety
/// The caller must uphold most of the safety requirements for `get_unchecked_mut`. i.e. the id having been inserted into the Mapping before.
/// The caller must uphold most of the safety requirements for
/// `get_unchecked_mut`. i.e. the id having been inserted into the Mapping
/// before.
pub unsafe fn get_unchecked_mut(&mut self, id: TId) -> &mut TValue {
let (chunk, offset) = Self::chunk_and_offset(id.to_usize());
self.chunks
Expand All @@ -128,6 +136,11 @@ impl<TId: ArenaId, TValue> Mapping<TId, TValue> {
self.len == 0
}

/// Returns the maximum id that has been inserted
pub(crate) fn max(&self) -> usize {
self.max
}

/// Defines the number of slots that can be used
/// theses slots are not initialized
pub fn slots(&self) -> usize {
Expand Down Expand Up @@ -177,6 +190,35 @@ impl<'a, TId: ArenaId, TValue> Iterator for MappingIter<'a, TId, TValue> {

impl<'a, TId: ArenaId, TValue> FusedIterator for MappingIter<'a, TId, TValue> {}

#[cfg(feature = "serde")]
impl<K: ArenaId, V: serde::Serialize> serde::Serialize for Mapping<K, V> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.chunks
.iter()
.flatten()
.take(self.max())
.collect::<Vec<_>>()
.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de, K: ArenaId, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Mapping<K, V> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let values = Vec::<Option<V>>::deserialize(deserializer)?;
let mut mapping = Mapping::with_capacity(values.len());
for (i, value) in values.into_iter().enumerate() {
if let Some(value) = value {
mapping.insert(K::from_usize(i), value);
}
}
Ok(mapping)
}
}

#[cfg(test)]
mod tests {
use crate::internal::arena::ArenaId;
Expand Down
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
pub(crate) mod internal;
pub mod problem;
pub mod runtime;
pub mod snapshot;
mod solver;
pub mod utils;

Expand Down Expand Up @@ -171,6 +172,8 @@ pub struct Candidates {

/// Holds information about the dependencies of a package.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Dependencies {
/// The dependencies are known.
Known(KnownDependencies),
Expand All @@ -184,9 +187,14 @@ pub enum Dependencies {

/// Holds information about the dependencies of a package when they are known.
#[derive(Default, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KnownDependencies {
/// Defines which packages should be installed alongside the depending
/// package and the constraints applied to the package.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub requirements: Vec<VersionSetId>,

/// Defines additional constraints on packages that may or may not be part
Expand All @@ -196,5 +204,9 @@ pub struct KnownDependencies {
/// package also added to the solution.
///
/// This is often useful to use for optional dependencies.
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub constrains: Vec<VersionSetId>,
}
Loading

0 comments on commit 7b9e911

Please sign in to comment.