-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request 'Local context building blocks' (#64) from local-c…
…x into main Reviewed-on: https://codeberg.org/DM-Earth/rimecraft/pulls/64 Reviewed-by: C191239 <[email protected]>
- Loading branch information
Showing
11 changed files
with
669 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "rimecraft-local-cx" | ||
version = "0.1.0" | ||
edition = "2021" | ||
authors = ["JieningYu <[email protected]>"] | ||
description = "Rimecraft local context traits" | ||
repository = "https://github.com/rimecraft-rs/rimecraft/" | ||
license = "AGPL-3.0-or-later" | ||
categories = [] | ||
|
||
[badges] | ||
maintenance = { status = "passively-maintained" } | ||
|
||
[dependencies] | ||
global-cx = { path = "../global-cx", package = "rimecraft-global-cx" } | ||
serde = { version = "1.0", default-features = false, optional = true } | ||
erased-serde = { version = "0.4", optional = true } | ||
edcode2 = { path = "../../util/edcode2", package = "rimecraft-edcode2", optional = true } | ||
typeid = { version = "1.0", optional = true } | ||
ahash = { version = "0.8", optional = true } | ||
|
||
[features] | ||
serde = ["dep:serde"] | ||
erased-serde = ["dep:erased-serde"] | ||
edcode = ["dep:edcode2"] | ||
dyn-cx = ["dep:typeid", "dep:ahash"] | ||
|
||
[lints] | ||
workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
//! Dynamic context providers. | ||
|
||
#![cfg(feature = "dyn-cx")] | ||
|
||
use std::{any::TypeId, borrow::Cow, fmt::Debug}; | ||
|
||
use ahash::AHashMap; | ||
|
||
use crate::{BaseLocalContext, LocalContext}; | ||
|
||
/// Function table for getting contexts. | ||
#[derive(Debug)] | ||
pub struct ContextTable<LocalCx> { | ||
map: AHashMap<TypeId, fn(LocalCx, &mut (dyn FnMut(*const ()) + '_))>, | ||
} | ||
|
||
impl<Cx> ContextTable<Cx> { | ||
/// Creates a new context table. | ||
#[inline] | ||
pub fn new() -> Self { | ||
Self { | ||
map: AHashMap::new(), | ||
} | ||
} | ||
|
||
/// Enables dynamic fetching of a type. | ||
pub fn enable<T>(&mut self) | ||
where | ||
Cx: LocalContext<T>, | ||
{ | ||
let ty = typeid::of::<T>(); | ||
self.map.insert(ty, |cx, f| { | ||
let val = <Cx as LocalContext<T>>::acquire(cx); | ||
f(std::ptr::from_ref(&val).cast::<()>()) | ||
}); | ||
} | ||
} | ||
|
||
impl<Cx> Default for ContextTable<Cx> { | ||
#[inline] | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
impl<Cx> Clone for ContextTable<Cx> { | ||
#[inline] | ||
fn clone(&self) -> Self { | ||
Self { | ||
map: self.map.clone(), | ||
} | ||
} | ||
} | ||
|
||
/// A dynamic context provider. | ||
#[derive(Debug)] | ||
pub struct DynamicContext<'a, LocalCx> { | ||
cx: LocalCx, | ||
table: Cow<'a, ContextTable<LocalCx>>, | ||
} | ||
|
||
impl<'a, Cx> DynamicContext<'a, Cx> { | ||
/// Creates a new dynamic context with owned context table. | ||
#[inline] | ||
pub fn new(cx: Cx, table: ContextTable<Cx>) -> Self { | ||
Self { | ||
cx, | ||
table: Cow::Owned(table), | ||
} | ||
} | ||
|
||
/// Creates a new dynamic context with borrowed context table. | ||
#[inline] | ||
pub fn from_borrowed_table(cx: Cx, table: &'a ContextTable<Cx>) -> Self { | ||
Self { | ||
cx, | ||
table: Cow::Borrowed(table), | ||
} | ||
} | ||
} | ||
|
||
impl<Cx> DynamicContext<'_, Cx> | ||
where | ||
Cx: BaseLocalContext, | ||
{ | ||
/// Turns this context into an [`UnsafeDynamicContext`]. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The returned type is not safe enough to exist. | ||
#[inline(always)] | ||
pub unsafe fn as_unsafe_cx(&self) -> UnsafeDynamicContext<'_> { | ||
UnsafeDynamicContext(self) | ||
} | ||
} | ||
|
||
impl<Cx> BaseLocalContext for &DynamicContext<'_, Cx> {} | ||
|
||
impl<Cx, T> LocalContext<T> for &DynamicContext<'_, Cx> | ||
where | ||
Cx: LocalContext<T>, | ||
{ | ||
#[inline] | ||
fn acquire(self) -> T { | ||
self.cx.acquire() | ||
} | ||
} | ||
|
||
trait ErasedDynCx { | ||
fn erased_acquire(&self, ty: TypeId, f: &mut (dyn FnMut(*const ()) + '_)); | ||
} | ||
|
||
impl<Cx> ErasedDynCx for DynamicContext<'_, Cx> | ||
where | ||
Cx: BaseLocalContext, | ||
{ | ||
#[inline] | ||
fn erased_acquire(&self, ty: TypeId, f: &mut (dyn FnMut(*const ()) + '_)) { | ||
if let Some(g) = self.table.map.get(&ty) { | ||
g(self.cx, f) | ||
} | ||
} | ||
} | ||
|
||
/// A dynamic context provider that is **unsafe to exist**. | ||
#[repr(transparent)] | ||
#[derive(Clone, Copy)] | ||
pub struct UnsafeDynamicContext<'a>(&'a (dyn ErasedDynCx + 'a)); | ||
|
||
impl BaseLocalContext for UnsafeDynamicContext<'_> {} | ||
|
||
impl<T> LocalContext<T> for UnsafeDynamicContext<'_> | ||
where | ||
T: Copy, | ||
{ | ||
fn acquire(self) -> T { | ||
let mut val = None; | ||
self.0.erased_acquire(typeid::of::<T>(), &mut |obj| { | ||
val = Some(unsafe { *obj.cast::<T>() }) | ||
}); | ||
val.unwrap_or_else(|| { | ||
panic!( | ||
"type {} not found for dynamic context", | ||
std::any::type_name::<T>() | ||
) | ||
}) | ||
} | ||
} | ||
|
||
impl Debug for UnsafeDynamicContext<'_> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_tuple("UnsafeDynamicContext").finish() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#![cfg(feature = "edcode")] | ||
|
||
use edcode2::{Buf, BufMut}; | ||
|
||
use crate::WithLocalCx; | ||
|
||
impl<T, Cx> Buf for WithLocalCx<T, Cx> | ||
where | ||
T: Buf, | ||
{ | ||
#[inline(always)] | ||
fn remaining(&self) -> usize { | ||
self.inner.remaining() | ||
} | ||
|
||
#[inline(always)] | ||
fn chunk(&self) -> &[u8] { | ||
self.inner.chunk() | ||
} | ||
|
||
#[inline(always)] | ||
fn advance(&mut self, cnt: usize) { | ||
self.inner.advance(cnt) | ||
} | ||
|
||
#[inline(always)] | ||
fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize { | ||
self.inner.chunks_vectored(dst) | ||
} | ||
} | ||
|
||
unsafe impl<T, Cx> BufMut for WithLocalCx<T, Cx> | ||
where | ||
T: BufMut, | ||
{ | ||
#[inline(always)] | ||
fn remaining_mut(&self) -> usize { | ||
self.inner.remaining_mut() | ||
} | ||
|
||
#[inline(always)] | ||
unsafe fn advance_mut(&mut self, cnt: usize) { | ||
self.inner.advance_mut(cnt) | ||
} | ||
|
||
#[inline(always)] | ||
fn chunk_mut(&mut self) -> &mut edcode2::UninitSlice { | ||
self.inner.chunk_mut() | ||
} | ||
|
||
#[inline(always)] | ||
fn put<T1: Buf>(&mut self, src: T1) | ||
where | ||
Self: Sized, | ||
{ | ||
self.inner.put(src) | ||
} | ||
|
||
#[inline(always)] | ||
fn put_slice(&mut self, src: &[u8]) { | ||
self.inner.put_slice(src) | ||
} | ||
|
||
#[inline(always)] | ||
fn put_bytes(&mut self, val: u8, cnt: usize) { | ||
self.inner.put_bytes(val, cnt) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
//! Local context traits. | ||
|
||
use std::fmt::Debug; | ||
|
||
use global_cx::GlobalContext; | ||
|
||
pub mod dyn_cx; | ||
|
||
mod edcode; | ||
pub mod serde; | ||
|
||
/// A base local context. | ||
pub trait BaseLocalContext: Sized + Copy {} | ||
|
||
/// A local context provides data to the global context. | ||
pub trait LocalContext<T>: BaseLocalContext { | ||
/// Acquire the data from the local context. | ||
fn acquire(self) -> T; | ||
} | ||
|
||
/// Global context types that provides implicit local context type. | ||
pub trait ProvideLocalCxTy: GlobalContext { | ||
/// The local context type. | ||
type Context<'cx>: BaseLocalContext; | ||
} | ||
|
||
/// A type that carries a local context. | ||
/// | ||
/// This type is used to carry a local context along with the data. | ||
pub struct WithLocalCx<T, LocalCx> { | ||
/// The data. | ||
pub inner: T, | ||
/// The local context. | ||
pub local_cx: LocalCx, | ||
} | ||
|
||
impl<T, Cx> Debug for WithLocalCx<T, Cx> | ||
where | ||
T: Debug, | ||
{ | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{:?}", &self.inner) | ||
} | ||
} | ||
|
||
/// Extension trait for local context. | ||
pub trait LocalContextExt { | ||
/// Create a `WithLocalCx` with the given inner data. | ||
#[inline] | ||
fn with<T>(self, inner: T) -> WithLocalCx<T, Self> | ||
where | ||
Self: Sized, | ||
{ | ||
WithLocalCx { | ||
inner, | ||
local_cx: self, | ||
} | ||
} | ||
} | ||
|
||
impl<Cx> LocalContextExt for Cx where Cx: BaseLocalContext {} | ||
|
||
mod tests; |
Oops, something went wrong.