use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use crate::context::spinlock::SpinLock;
use crate::{ffi, Context, Secp256k1};
mod self_contained_context {
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use crate::ffi::types::{c_void, AlignedType};
use crate::{ffi, AllPreallocated, Context as _};
const MAX_PREALLOC_SIZE: usize = 16;
pub struct SelfContainedContext(
[MaybeUninit<AlignedType>; MAX_PREALLOC_SIZE],
Option<NonNull<ffi::Context>>,
);
unsafe impl Send for SelfContainedContext {}
impl SelfContainedContext {
pub const fn new_uninitialized() -> Self {
Self([MaybeUninit::uninit(); MAX_PREALLOC_SIZE], None)
}
fn buf(&mut self) -> NonNull<c_void> {
NonNull::new(self.0.as_mut_ptr() as *mut c_void).unwrap()
}
pub fn clone_into(&mut self, other: &mut SelfContainedContext) {
unsafe {
let other = other.raw_ctx().as_ptr();
assert!(
ffi::secp256k1_context_preallocated_clone_size(other)
<= core::mem::size_of::<[AlignedType; MAX_PREALLOC_SIZE]>(),
"prealloc size exceeds our guessed compile-time upper bound",
);
ffi::secp256k1_context_preallocated_clone(other, self.buf());
}
}
pub fn raw_ctx(&mut self) -> NonNull<ffi::Context> {
let buf = self.buf();
*self.1.get_or_insert_with(|| {
unsafe {
assert!(
ffi::secp256k1_context_preallocated_size(AllPreallocated::FLAGS)
<= core::mem::size_of::<[AlignedType; MAX_PREALLOC_SIZE]>(),
"prealloc size exceeds our guessed compile-time upper bound",
);
ffi::secp256k1_context_preallocated_create(buf, AllPreallocated::FLAGS)
}
})
}
}
}
pub(super) use self_contained_context::SelfContainedContext;
static SECP256K1: SpinLock<SelfContainedContext> = SpinLock::<SelfContainedContext>::new();
pub fn with_global_context<T, Ctx: Context, F: FnOnce(&Secp256k1<Ctx>) -> T>(
f: F,
rerandomize_seed: Option<&[u8; 32]>,
) -> T {
with_raw_global_context(
|ctx| {
let secp = ManuallyDrop::new(Secp256k1 { ctx, phantom: PhantomData });
f(&*secp)
},
rerandomize_seed,
)
}
pub fn with_raw_global_context<T, F: FnOnce(NonNull<ffi::Context>) -> T>(
f: F,
rerandomize_seed: Option<&[u8; 32]>,
) -> T {
let mut ctx = SelfContainedContext::new_uninitialized();
let mut have_global_ctx = false;
if let Some(mut guard) = SECP256K1.try_lock() {
let global_ctx = &mut *guard;
ctx.clone_into(global_ctx);
have_global_ctx = true;
}
let ctx_ptr = ctx.raw_ctx();
let ret = f(ctx_ptr);
if have_global_ctx {
if let Some(seed) = rerandomize_seed {
unsafe {
assert_eq!(ffi::secp256k1_context_randomize(ctx_ptr, seed.as_ptr()), 1);
}
if let Some(ref mut guard) = SECP256K1.try_lock() {
guard.clone_into(&mut ctx);
}
}
}
ret
}
pub fn rerandomize_global_context(seed: &[u8; 32]) { with_raw_global_context(|_| {}, Some(seed)) }