Skip to content

Commit

Permalink
Merge pull request #18 from YosysHQ/dsf
Browse files Browse the repository at this point in the history
Add dsf crates with union_find and tracked_union_find
  • Loading branch information
aiju authored Oct 29, 2024
2 parents 8a42e72 + 5446ca8 commit 1a73a50
Show file tree
Hide file tree
Showing 19 changed files with 1,521 additions and 23 deletions.
51 changes: 49 additions & 2 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 @@ -17,6 +17,7 @@ members = [
"imctk",
"lit",
"stable_set",
"union_find",

# comment to force multi-line layout
]
Expand Down
5 changes: 5 additions & 0 deletions derive/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub fn derive_id(input: DeriveInput, internal_generic_id: bool) -> syn::Result<T

let Id = quote![#imctk_ids::Id];
let ConstIdFromIdIndex = quote![#imctk_ids::ConstIdFromIdIndex];
let NoUninit = quote![#imctk_ids::NoUninit];

let Clone = quote![::core::prelude::rust_2021::Clone];
let Copy = quote![::core::prelude::rust_2021::Copy];
Expand Down Expand Up @@ -113,6 +114,10 @@ pub fn derive_id(input: DeriveInput, internal_generic_id: bool) -> syn::Result<T
// SAFETY: we ensure that the newtype wrapped type is `Id` and thus `Sync`
unsafe impl #impl_generics #Sync for #target_type #where_clause {
}

// SAFETY: we ensure that the newtype wrapped type is `Id` and thus `NoUninit`
unsafe impl #impl_generics #NoUninit for #target_type #where_clause {
}
}
};

Expand Down
1 change: 1 addition & 0 deletions ids/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ imctk-derive = { path = "../derive" }
imctk-transparent = { path = "../transparent" }
table_seq = { path = "../table_seq" }
zwohash = "0.1.2"
bytemuck = { version = "1.19.0", features = ["derive"] }

[dev-dependencies]
rand = { version = "0.8.5", features = ["small_rng"] }
9 changes: 8 additions & 1 deletion ids/src/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::NoUninit;
use core::{fmt::Debug, hash::Hash};

mod id_types;
Expand Down Expand Up @@ -43,7 +44,7 @@ pub trait ConstIdFromIdIndex<const INDEX: usize> {
///
/// Users of this trait may depend on implementing types following these requirements for upholding
/// their own safety invariants.
pub unsafe trait Id: Copy + Ord + Hash + Send + Sync + Debug {
pub unsafe trait Id: Copy + Ord + Hash + Send + Sync + Debug + NoUninit {
/// An [`Id`] type that has the same representation and index range as this type.
///
/// This is provided to enable writing generic code that during monomorphization is only
Expand Down Expand Up @@ -161,6 +162,12 @@ pub unsafe trait Id: Copy + Ord + Hash + Send + Sync + Debug {
#[repr(transparent)]
pub struct GenericId<const MAX_ID_INDEX: usize, Repr: Id = usize>(Repr);

// SAFETY: #[repr(transparent)] and the only field is explicitly required to be NoUninit
unsafe impl<const MAX_ID_INDEX: usize, Repr> NoUninit for GenericId<MAX_ID_INDEX, Repr> where
Repr: Id + NoUninit
{
}

impl<const MAX_ID_INDEX: usize, Repr: Id> Debug for GenericId<MAX_ID_INDEX, Repr> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
28 changes: 14 additions & 14 deletions ids/src/id/id_types.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod id8 {
use imctk_transparent::SubtypeCast;

use crate::id::{u8_range_types::NonMaxHighNibbleU8, ConstIdFromIdIndex, GenericId, Id};
use crate::id::{u8_range_types::NonMaxHighNibbleU8, ConstIdFromIdIndex, GenericId, Id, NoUninit};
use core::{fmt, fmt::Debug, hash::Hash};

/// [`Id`] type representing indices in the range `0..0xf0`.
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(transparent)]
pub struct Id8(NonMaxHighNibbleU8);

Expand Down Expand Up @@ -177,13 +177,13 @@ mod id8 {
mod id16 {
use imctk_transparent::SubtypeCast;

use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id};
use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id, NoUninit};
use core::{fmt, fmt::Debug, hash::Hash};

/// [`Id`] type representing indices in the range `0..0xff00`.
#[cfg(target_endian = "little")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(2))]
pub struct Id16 {
lsb: u8,
Expand All @@ -192,7 +192,7 @@ mod id16 {

#[cfg(target_endian = "big")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(2))]
pub struct Id16 {
msb: NonMaxU8,
Expand Down Expand Up @@ -366,13 +366,13 @@ mod id16 {
mod id32 {
use imctk_transparent::SubtypeCast;

use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id};
use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id, NoUninit};
use core::{fmt, fmt::Debug, hash::Hash};

/// [`Id`] type representing indices in the range `0..0xff00_0000`.
#[cfg(target_endian = "little")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(4))]
pub struct Id32 {
lsbs: [u8; 3],
Expand All @@ -381,7 +381,7 @@ mod id32 {

#[cfg(target_endian = "big")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(4))]
pub struct Id32 {
msb: NonMaxU8,
Expand Down Expand Up @@ -562,13 +562,13 @@ mod id32 {
mod id64 {
use imctk_transparent::SubtypeCast;

use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id};
use crate::id::{u8_range_types::NonMaxU8, ConstIdFromIdIndex, GenericId, Id, NoUninit};
use core::{fmt, fmt::Debug, hash::Hash};

/// [`Id`] type representing indices in the range `0..0xff00_0000_0000_0000`.
#[cfg(target_endian = "little")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(8))]
pub struct Id64 {
lsbs: [u8; 7],
Expand All @@ -577,7 +577,7 @@ mod id64 {

#[cfg(target_endian = "big")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(C, align(8))]
pub struct Id64 {
msb: NonMaxU8,
Expand Down Expand Up @@ -758,15 +758,15 @@ mod id64 {
mod id_size {
use imctk_transparent::SubtypeCast;

use crate::id::{u8_range_types::NonMaxMsbU8, ConstIdFromIdIndex, GenericId, Id};
use crate::id::{u8_range_types::NonMaxMsbU8, ConstIdFromIdIndex, GenericId, Id, NoUninit};
use core::{fmt, fmt::Debug, hash::Hash};

const LSBS: usize = (usize::BITS as usize / 8) - 1;

/// [`Id`] type representing indices in the range `0..=isize::MAX as usize`.
#[cfg(target_endian = "little")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
#[cfg_attr(target_pointer_width = "32", repr(C, align(4)))]
#[cfg_attr(target_pointer_width = "64", repr(C, align(8)))]
Expand All @@ -777,7 +777,7 @@ mod id_size {

#[cfg(target_endian = "big")]
#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
#[cfg_attr(target_pointer_width = "32", repr(C, align(4)))]
#[cfg_attr(target_pointer_width = "64", repr(C, align(8)))]
Expand Down
8 changes: 5 additions & 3 deletions ids/src/id/u8_range_types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::NoUninit;

#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(u8)]
pub enum NonMaxU8 {
Val00 = 0x00,
Expand Down Expand Up @@ -260,7 +262,7 @@ pub enum NonMaxU8 {
}

#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(u8)]
pub enum NonMaxHighNibbleU8 {
Val00 = 0x00,
Expand Down Expand Up @@ -506,7 +508,7 @@ pub enum NonMaxHighNibbleU8 {
}

#[allow(dead_code)] // Only constructed via transmutation and/or pointer casts
#[derive(Clone, Copy)]
#[derive(Clone, Copy, NoUninit)]
#[repr(u8)]
pub enum NonMaxMsbU8 {
Val00 = 0x00,
Expand Down
54 changes: 54 additions & 0 deletions ids/src/id_alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! An allocator for IDs that allows concurrent access from multiple threads.
use std::sync::atomic::Ordering::Relaxed;
use std::{marker::PhantomData, sync::atomic::AtomicUsize};

use crate::{Id, IdRange};

/// An allocator for IDs that allows concurrent access from multiple threads.
pub struct IdAlloc<T> {
counter: AtomicUsize,
_phantom: PhantomData<T>,
}

impl<T: Id> Default for IdAlloc<T> {
fn default() -> Self {
Self::new()
}
}

/// `IdAllocError` indicates that there are not enough IDs remaining.
#[derive(Clone, Copy, Debug)]
pub struct IdAllocError;

impl<T: Id> IdAlloc<T> {
/// Constructs a new ID allocator.
pub const fn new() -> Self {
Self {
counter: AtomicUsize::new(0),
_phantom: PhantomData,
}
}
fn alloc_indices(&self, n: usize) -> Result<usize, IdAllocError> {
self.counter
.fetch_update(Relaxed, Relaxed, |current_id| {
current_id
.checked_add(n)
.filter(|&index| index <= T::MAX_ID_INDEX.saturating_add(1))
})
.map_err(|_| IdAllocError)
}
/// Allocates a single ID.
pub fn alloc(&self) -> Result<T, IdAllocError> {
self.alloc_indices(1).map(|index| {
// SAFETY: the precondition was checked by `alloc_indices`
unsafe { T::from_id_index_unchecked(index) }
})
}
/// Allocates a contiguous range of the specified size.
pub fn alloc_range(&self, n: usize) -> Result<IdRange<T>, IdAllocError> {
self.alloc_indices(n).map(|start| {
// SAFETY: the precondition was checked by `alloc_indices`
unsafe { IdRange::from_index_range_unchecked(start..start + n) }
})
}
}
2 changes: 1 addition & 1 deletion ids/src/id_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ impl<K: Id, V: Clone> Clone for IdVec<K, V> {
}
}

impl<K: Id, V> Default for IdVec<K, V> {
impl<K, V> Default for IdVec<K, V> {
#[inline(always)]
fn default() -> Self {
Self {
Expand Down
7 changes: 7 additions & 0 deletions ids/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod id;
mod id_range;
pub mod id_vec;
pub mod indexed_id_vec;
pub mod id_alloc;

pub mod id_set_seq;

Expand All @@ -31,3 +32,9 @@ pub use imctk_derive::Id;
pub use id::{ConstIdFromIdIndex, GenericId, Id, Id16, Id32, Id64, Id8, IdSize};

pub use id_range::IdRange;

pub use id_alloc::IdAlloc;

// re-export this so that others can use it without depending on bytemuck explicitly
// in particular needed for #[derive(Id)]
pub use bytemuck::NoUninit;
2 changes: 1 addition & 1 deletion ids/tests/test_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ fn conversion_usize() {

#[derive(Id)]
#[repr(transparent)]
pub struct NewtypePhantom<T>(usize, PhantomData<T>);
pub struct NewtypePhantom<T: 'static>(usize, PhantomData<T>);

impl<T> std::fmt::Debug for NewtypePhantom<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
2 changes: 1 addition & 1 deletion lit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ imctk-transparent = { version = "0.1.0", path = "../transparent" }
imctk-util = { version = "0.1.0", path = "../util" }
log = "0.4.21"
table_seq = { version = "0.1.0", path = "../table_seq" }
zwohash = "0.1.2"
zwohash = "0.1.2"
Loading

0 comments on commit 1a73a50

Please sign in to comment.