use std::ptr::{null_mut, NonNull};
use std::{fmt, slice};
pub mod sys {
#[allow(non_camel_case_types)]
#[allow(dead_code)]
mod c {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub use c::custom_labels_label_t as Label;
pub use c::custom_labels_labelset_t as Labelset;
pub use c::custom_labels_string_t as String;
impl<'a> From<&'a [u8]> for self::String {
fn from(value: &'a [u8]) -> Self {
Self {
len: value.len(),
buf: value.as_ptr(),
}
}
}
impl self::String {
pub fn to_owned(&self) -> OwnedString {
unsafe {
let buf = libc::malloc(self.len);
if buf.is_null() {
panic!("Out of memory");
}
libc::memcpy(buf, self.buf as *const _, self.len);
OwnedString(Self {
len: self.len,
buf: buf as *mut _,
})
}
}
}
pub struct OwnedString(self::String);
impl OwnedString {
pub fn new() -> Self {
OwnedString(self::String {
len: 0,
buf: std::ptr::null(),
})
}
}
impl std::ops::Deref for OwnedString {
type Target = self::String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for OwnedString {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Drop for OwnedString {
fn drop(&mut self) {
unsafe {
libc::free(self.0.buf as *mut _);
}
}
}
pub use c::custom_labels_clone as clone;
pub use c::custom_labels_current as current;
pub use c::custom_labels_debug_string as debug_string;
pub use c::custom_labels_delete as delete;
pub use c::custom_labels_free as free;
pub use c::custom_labels_get as get;
pub use c::custom_labels_new as new;
pub use c::custom_labels_replace as replace;
pub use c::custom_labels_run_with as run_with;
pub use c::custom_labels_set as set;
pub mod careful {
pub use super::c::custom_labels_careful_delete as delete;
pub use super::c::custom_labels_careful_run_with as run_with;
pub use super::c::custom_labels_careful_set as set;
}
}
pub mod build {
pub fn emit_build_instructions() {
let dlist_path = format!("{}/dlist", std::env::var("OUT_DIR").unwrap());
std::fs::write(&dlist_path, include_str!("../dlist")).unwrap();
println!("cargo:rustc-link-arg=-Wl,--dynamic-list={}", dlist_path);
}
}
pub struct Labelset {
raw: NonNull<sys::Labelset>,
}
unsafe impl Send for Labelset {}
impl Labelset {
pub fn new() -> Self {
Self::with_capacity(0)
}
pub fn with_capacity(capacity: usize) -> Self {
let raw = unsafe { sys::new(capacity) };
let raw = NonNull::new(raw).expect("failed to allocate labelset");
Self { raw }
}
pub fn clone_from_current() -> Self {
Self::try_clone_from_current().unwrap_or_default()
}
pub fn try_clone_from_current() -> Option<Self> {
let raw = unsafe { sys::current() };
if raw.is_null() {
None
} else {
let raw = unsafe { sys::clone(raw) };
let raw = NonNull::new(raw).expect("failed to clone labelset");
Some(Self { raw })
}
}
pub fn enter<F, Ret>(&mut self, f: F) -> Ret
where
F: FnOnce() -> Ret,
{
struct Guard {
old: *mut sys::Labelset,
}
impl Drop for Guard {
fn drop(&mut self) {
unsafe { sys::replace(self.old) };
}
}
let old = unsafe { sys::replace(self.raw.as_ptr()) };
let _guard = Guard { old };
f()
}
pub fn set<K, V>(&mut self, key: K, value: V)
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
let errno = unsafe {
sys::set(
self.raw.as_ptr(),
key.as_ref().into(),
value.as_ref().into(),
null_mut(),
)
};
if errno != 0 {
panic!("out of memory");
}
}
pub fn delete<K>(&mut self, key: K)
where
K: AsRef<[u8]>,
{
unsafe { sys::delete(self.raw.as_ptr(), key.as_ref().into()) }
}
pub fn get<K>(&self, key: K) -> Option<&[u8]>
where
K: AsRef<[u8]>,
{
unsafe {
sys::get(self.raw.as_ptr(), key.as_ref().into())
.as_ref()
.map(|lbl| slice::from_raw_parts(lbl.value.buf, lbl.value.len))
}
}
}
impl Default for Labelset {
fn default() -> Self {
Self::new()
}
}
impl Drop for Labelset {
fn drop(&mut self) {
unsafe { sys::free(self.raw.as_ptr()) }
}
}
impl Clone for Labelset {
fn clone(&self) -> Self {
let raw = unsafe { sys::clone(self.raw.as_ptr()) };
let raw = NonNull::new(raw).expect("failed to clone labelset");
Self { raw }
}
}
impl<K, V> Extend<(K, V)> for Labelset
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
for (k, v) in iter {
self.set(k, v);
}
}
}
impl fmt::Debug for Labelset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_labelset(f, self.raw.as_ptr())
}
}
fn debug_labelset(f: &mut fmt::Formatter<'_>, labelset: *const sys::Labelset) -> fmt::Result {
let mut cstr = sys::OwnedString::new();
let errno = unsafe { sys::debug_string(labelset, &mut *cstr) };
if errno != 0 {
panic!("out of memory");
}
let bytes = unsafe { slice::from_raw_parts(cstr.buf, cstr.len) };
let str = String::from_utf8_lossy(bytes);
f.write_str(&str)
}
pub const CURRENT_LABELSET: CurrentLabelset = CurrentLabelset { _priv: () };
pub struct CurrentLabelset {
_priv: (),
}
impl CurrentLabelset {
pub fn set<K, V>(&self, key: K, value: V)
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
if unsafe { sys::current() }.is_null() {
panic!("no current label set");
}
let errno = unsafe {
sys::set(
sys::current(),
key.as_ref().into(),
value.as_ref().into(),
null_mut(),
)
};
if errno != 0 {
panic!("out of memory");
}
}
pub fn delete<K>(&self, key: K)
where
K: AsRef<[u8]>,
{
unsafe { sys::delete(sys::current(), key.as_ref().into()) }
}
pub fn get<K>(&self, key: K) -> Option<Vec<u8>>
where
K: AsRef<[u8]>,
{
unsafe {
sys::get(sys::current(), key.as_ref().into())
.as_ref()
.map(|lbl| {
let v = slice::from_raw_parts(lbl.value.buf, lbl.value.len);
v.to_vec()
})
}
}
}
impl fmt::Debug for CurrentLabelset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let current = unsafe { sys::current() };
if current.is_null() {
panic!("no current labelset");
}
debug_labelset(f, current)
}
}
pub fn with_label<K, V, F, Ret>(k: K, v: V, f: F) -> Ret
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
F: FnOnce() -> Ret,
{
unsafe {
if sys::current().is_null() {
let l = sys::new(0);
sys::replace(l);
}
}
struct Guard<'a> {
k: &'a [u8],
old_v: Option<sys::OwnedString>,
}
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
if let Some(old_v) = std::mem::take(&mut self.old_v) {
let errno = unsafe { sys::set(sys::current(), self.k.into(), *old_v, null_mut()) };
if errno != 0 {
panic!("corruption in custom labels library: errno {errno}");
}
} else {
unsafe { sys::delete(sys::current(), self.k.into()) };
}
}
}
let old_v = unsafe { sys::get(sys::current(), k.as_ref().into()).as_ref() }
.map(|lbl| lbl.value.to_owned());
let _g = Guard {
k: k.as_ref(),
old_v,
};
let errno = unsafe {
sys::set(
sys::current(),
k.as_ref().into(),
v.as_ref().into(),
null_mut(),
)
};
if errno != 0 {
panic!("corruption in custom labels library: errno {errno}")
}
f()
}
pub fn with_labels<I, K, V, F, Ret>(i: I, f: F) -> Ret
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<[u8]>,
V: AsRef<[u8]>,
F: FnOnce() -> Ret,
{
let mut i = i.into_iter();
if let Some((k, v)) = i.next() {
with_label(k, v, || with_labels(i, f))
} else {
f()
}
}
pub mod asynchronous {
use pin_project_lite::pin_project;
use std::future::Future;
use std::iter;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::Labelset;
pin_project! {
pub struct Labeled<Fut> {
#[pin]
inner: Fut,
labelset: Labelset,
}
}
impl<Fut, Ret> Future for Labeled<Fut>
where
Fut: Future<Output = Ret>,
{
type Output = Ret;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let p = self.project();
p.labelset.enter(|| p.inner.poll(cx))
}
}
pub trait Label: Sized {
fn with_current_labels(self) -> Labeled<Self>;
fn with_label<K, V>(self, k: K, v: V) -> Labeled<Self>
where
K: AsRef<[u8]>,
V: AsRef<[u8]>;
fn with_labels<I, K, V>(self, i: I) -> Labeled<Self>
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<[u8]>,
V: AsRef<[u8]>;
fn with_labelset(self, labelset: Labelset) -> Labeled<Self>;
}
impl<Fut: Future> Label for Fut {
fn with_current_labels(self) -> Labeled<Self> {
self.with_labels(iter::empty::<(&[u8], &[u8])>())
}
fn with_label<K, V>(self, k: K, v: V) -> Labeled<Self>
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
self.with_labels(iter::once((k, v)))
}
fn with_labels<I, K, V>(self, iter: I) -> Labeled<Self>
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
let mut labelset = Labelset::clone_from_current();
labelset.extend(iter);
Labeled {
inner: self,
labelset,
}
}
fn with_labelset(self, labelset: Labelset) -> Labeled<Self> {
Labeled {
inner: self,
labelset,
}
}
}
}