diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/wit-bindgen-rt/src | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/wit-bindgen-rt/src')
| -rw-r--r-- | vendor/wit-bindgen-rt/src/async_support.rs | 515 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/async_support/future_support.rs | 362 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/async_support/stream_support.rs | 431 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/cabi_realloc.c | 10 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/cabi_realloc.o | bin | 0 -> 261 bytes | |||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/cabi_realloc.rs | 11 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/lib.rs | 118 | ||||
| -rw-r--r-- | vendor/wit-bindgen-rt/src/libwit_bindgen_cabi_realloc.a | bin | 0 -> 412 bytes |
8 files changed, 1447 insertions, 0 deletions
diff --git a/vendor/wit-bindgen-rt/src/async_support.rs b/vendor/wit-bindgen-rt/src/async_support.rs new file mode 100644 index 00000000..b60cabaf --- /dev/null +++ b/vendor/wit-bindgen-rt/src/async_support.rs @@ -0,0 +1,515 @@ +#![deny(missing_docs)] +#![allow(static_mut_refs)] + +extern crate std; +use std::alloc::{self, Layout}; +use std::any::Any; +use std::boxed::Box; +use std::collections::{hash_map, HashMap}; +use std::fmt::{self, Debug, Display}; +use std::future::Future; +use std::pin::Pin; +use std::ptr; +use std::string::String; +use std::sync::Arc; +use std::task::{Context, Poll, Wake, Waker}; +use std::vec::Vec; + +use futures::channel::oneshot; +use futures::future::FutureExt; +use futures::stream::{FuturesUnordered, StreamExt}; +use once_cell::sync::Lazy; + +mod future_support; +mod stream_support; + +pub use { + future_support::{FutureReader, FutureVtable, FutureWriter}, + stream_support::{StreamReader, StreamVtable, StreamWriter}, +}; + +pub use futures; + +type BoxFuture = Pin<Box<dyn Future<Output = ()> + 'static>>; + +/// Represents a task created by either a call to an async-lifted export or a +/// future run using `block_on` or `poll_future`. +struct FutureState { + /// Number of in-progress async-lowered import calls and/or stream/future reads/writes. + todo: usize, + /// Remaining work to do (if any) before this task can be considered "done". + /// + /// Note that we won't tell the host the task is done until this is drained + /// and `todo` is zero. + tasks: Option<FuturesUnordered<BoxFuture>>, +} + +/// Represents the state of a stream or future. +#[doc(hidden)] +pub enum Handle { + LocalOpen, + LocalReady(Box<dyn Any>, Waker), + LocalWaiting(oneshot::Sender<Box<dyn Any>>), + LocalClosed, + Read, + Write, +} + +/// The current task being polled (or null if none). +static mut CURRENT: *mut FutureState = ptr::null_mut(); + +/// Map of any in-progress calls to async-lowered imports, keyed by the +/// identifiers issued by the host. +static mut CALLS: Lazy<HashMap<i32, oneshot::Sender<u32>>> = Lazy::new(HashMap::new); + +/// Any newly-deferred work queued by calls to the `spawn` function while +/// polling the current task. +static mut SPAWNED: Vec<BoxFuture> = Vec::new(); + +/// The states of all currently-open streams and futures. +static mut HANDLES: Lazy<HashMap<u32, Handle>> = Lazy::new(HashMap::new); + +#[doc(hidden)] +pub fn with_entry<T>(handle: u32, fun: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { + fun(unsafe { HANDLES.entry(handle) }) +} + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc<Self>) {} + } + + static WAKER: Lazy<Arc<DummyWaker>> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +/// Poll the specified task until it either completes or can't make immediate +/// progress. +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + if let Some(futures) = (*state).tasks.as_mut() { + CURRENT = state; + let poll = futures.poll_next_unpin(&mut Context::from_waker(&dummy_waker())); + CURRENT = ptr::null_mut(); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => { + (*state).tasks = None; + break Poll::Ready(()); + } + Poll::Pending => break Poll::Pending, + } + } else { + futures.extend(SPAWNED.drain(..)); + } + } else { + break Poll::Ready(()); + } + } +} + +/// Poll the future generated by a call to an async-lifted export once, calling +/// the specified closure (presumably backed by a call to `task.return`) when it +/// generates a value. +/// +/// This will return a non-null pointer representing the task if it hasn't +/// completed immediately; otherwise it returns null. +#[doc(hidden)] +pub fn first_poll<T: 'static>( + future: impl Future<Output = T> + 'static, + fun: impl FnOnce(&T) + 'static, +) -> *mut u8 { + let state = Box::into_raw(Box::new(FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(|v| fun(&v))) as BoxFuture] + .into_iter() + .collect(), + ), + })); + match unsafe { poll(state) } { + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => state as _, + } +} + +/// Await the completion of a call to an async-lowered import. +#[doc(hidden)] +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, +) { + const STATUS_STARTING: u32 = 0; + const STATUS_STARTED: u32 = 1; + const STATUS_RETURNED: u32 = 2; + const STATUS_DONE: u32 = 3; + + let result = import(params, results) as u32; + let status = result >> 30; + let call = (result & !(0b11 << 30)) as i32; + + if status != STATUS_DONE { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + } + + match status { + STATUS_STARTING => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } + STATUS_STARTED => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + } + STATUS_RETURNED | STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + _ => unreachable!(), + } +} + +/// stream/future read/write results defined by the Component Model ABI. +mod results { + pub const BLOCKED: u32 = 0xffff_ffff; + pub const CLOSED: u32 = 0x8000_0000; + pub const CANCELED: u32 = 0; +} + +/// Await the completion of a future read or write. +#[doc(hidden)] +pub async unsafe fn await_future_result( + import: unsafe extern "C" fn(u32, *mut u8) -> u32, + future: u32, + address: *mut u8, +) -> bool { + let result = import(future, address); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(future as _, tx); + let v = rx.await.unwrap(); + v == 1 + } + results::CLOSED | results::CANCELED => false, + 1 => true, + _ => unreachable!(), + } +} + +/// Await the completion of a stream read or write. +#[doc(hidden)] +pub async unsafe fn await_stream_result( + import: unsafe extern "C" fn(u32, *mut u8, u32) -> u32, + stream: u32, + address: *mut u8, + count: u32, +) -> Option<usize> { + let result = import(stream, address, count); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(stream as _, tx); + let v = rx.await.unwrap(); + if let results::CLOSED | results::CANCELED = v { + None + } else { + Some(usize::try_from(v).unwrap()) + } + } + results::CLOSED | results::CANCELED => None, + v => Some(usize::try_from(v).unwrap()), + } +} + +/// Call the `subtask.drop` canonical built-in function. +fn subtask_drop(subtask: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = subtask; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(_: u32); + } + unsafe { + subtask_drop(subtask); + } + } +} + +/// Handle a progress notification from the host regarding either a call to an +/// async-lowered import or a stream/future read/write operation. +#[doc(hidden)] +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const _EVENT_CALL_STARTING: i32 = 0; + const EVENT_CALL_STARTED: i32 = 1; + const EVENT_CALL_RETURNED: i32 = 2; + const EVENT_CALL_DONE: i32 = 3; + const _EVENT_YIELDED: i32 = 4; + const EVENT_STREAM_READ: i32 = 5; + const EVENT_STREAM_WRITE: i32 = 6; + const EVENT_FUTURE_READ: i32 = 7; + const EVENT_FUTURE_WRITE: i32 = 8; + + match event0 { + EVENT_CALL_STARTED => 0, + EVENT_CALL_RETURNED | EVENT_CALL_DONE | EVENT_STREAM_READ | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ | EVENT_FUTURE_WRITE => { + if let Some(call) = CALLS.remove(&event1) { + _ = call.send(event2 as _); + } + + let state = ctx as *mut FutureState; + let done = poll(state).is_ready(); + + if event0 == EVENT_CALL_DONE { + subtask_drop(event1 as u32); + } + + if matches!( + event0, + EVENT_CALL_DONE + | EVENT_STREAM_READ + | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ + | EVENT_FUTURE_WRITE + ) { + (*state).todo -= 1; + } + + if done && (*state).todo == 0 { + drop(Box::from_raw(state)); + 1 + } else { + 0 + } + } + _ => unreachable!(), + } +} + +/// Represents the Component Model `error-context` type. +pub struct ErrorContext { + handle: u32, +} + +impl ErrorContext { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } + + /// Extract the debug message from a given [`ErrorContext`] + pub fn debug_message(&self) -> String { + #[cfg(not(target_arch = "wasm32"))] + { + _ = self; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]"] + fn error_context_debug_message(_: u32, _: *mut u8); + } + + unsafe { + let mut ret = [0u32; 2]; + error_context_debug_message(self.handle, ret.as_mut_ptr() as *mut _); + let len = usize::try_from(ret[1]).unwrap(); + String::from_raw_parts(usize::try_from(ret[0]).unwrap() as *mut _, len, len) + } + } + } +} + +impl Debug for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ErrorContext").finish() + } +} + +impl Display for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for ErrorContext {} + +impl Drop for ErrorContext { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-context-drop]"] + fn error_drop(_: u32); + } + if self.handle != 0 { + unsafe { error_drop(self.handle) } + } + } + } +} + +/// Defer the specified future to be run after the current async-lifted export +/// task has returned a value. +/// +/// The task will remain in a running state until all spawned futures have +/// completed. +pub fn spawn(future: impl Future<Output = ()> + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} + +fn task_wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = state; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +/// Run the specified future to completion, returning the result. +/// +/// This uses `task.wait` to poll for progress on any in-progress calls to +/// async-lowered imports as necessary. +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on<T: 'static>(future: impl Future<Output = T> + 'static) -> T { + let (tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => task_wait(state), + } + } +} + +/// Call the `task.yield` canonical built-in function. +/// +/// This yields control to the host temporarily, allowing other tasks to make +/// progress. It's a good idea to call this inside a busy loop which does not +/// otherwise ever yield control the the host. +pub fn task_yield() { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-yield]"] + fn yield_(); + } + unsafe { + yield_(); + } + } +} + +/// Call the `task.backpressure` canonical built-in function. +/// +/// When `enabled` is `true`, this tells the host to defer any new calls to this +/// component instance until further notice (i.e. until `task.backpressure` is +/// called again with `enabled` set to `false`). +pub fn task_backpressure(enabled: bool) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = enabled; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-backpressure]"] + fn backpressure(_: i32); + } + unsafe { + backpressure(if enabled { 1 } else { 0 }); + } + } +} + +/// Call the `error-context.new` canonical built-in function. +pub fn error_context_new(debug_message: &str) -> ErrorContext { + #[cfg(not(target_arch = "wasm32"))] + { + _ = debug_message; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-context-new;encoding=utf8]"] + fn context_new(_: *const u8, _: usize) -> i32; + } + + unsafe { + let handle = context_new(debug_message.as_ptr(), debug_message.len()); + // SAFETY: Handles (including error context handles are guaranteed to + // fit inside u32 by the Component Model ABI + ErrorContext::from_handle(u32::try_from(handle).unwrap()) + } + } +} diff --git a/vendor/wit-bindgen-rt/src/async_support/future_support.rs b/vendor/wit-bindgen-rt/src/async_support/future_support.rs new file mode 100644 index 00000000..8477ec48 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/async_support/future_support.rs @@ -0,0 +1,362 @@ +extern crate std; + +use { + super::Handle, + futures::{ + channel::oneshot, + future::{self, FutureExt}, + }, + std::{ + boxed::Box, + collections::hash_map::Entry, + fmt, + future::{Future, IntoFuture}, + pin::Pin, + sync::atomic::{AtomicU32, Ordering::Relaxed}, + task::{Context, Poll}, + }, +}; + +#[doc(hidden)] +pub struct FutureVtable<T> { + pub write: fn(future: u32, value: T) -> Pin<Box<dyn Future<Output = bool>>>, + pub read: fn(future: u32) -> Pin<Box<dyn Future<Output = Option<T>>>>, + pub cancel_write: fn(future: u32), + pub cancel_read: fn(future: u32), + pub close_writable: fn(future: u32), + pub close_readable: fn(future: u32), +} + +/// Represents the writable end of a Component Model `future`. +pub struct FutureWriter<T: 'static> { + handle: u32, + vtable: &'static FutureVtable<T>, +} + +impl<T> fmt::Debug for FutureWriter<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriter") + .field("handle", &self.handle) + .finish() + } +} + +/// Represents a write operation which may be canceled prior to completion. +pub struct CancelableWrite<T: 'static> { + writer: Option<FutureWriter<T>>, + future: Pin<Box<dyn Future<Output = ()>>>, +} + +impl<T> Future for CancelableWrite<T> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + let me = self.get_mut(); + match me.future.poll_unpin(cx) { + Poll::Ready(()) => { + me.writer = None; + Poll::Ready(()) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl<T> CancelableWrite<T> { + /// Cancel this write if it hasn't already completed, returning the original `FutureWriter`. + /// + /// This method will panic if the write has already completed. + pub fn cancel(mut self) -> FutureWriter<T> { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter<T> { + let writer = self.writer.take().unwrap(); + super::with_entry(writer.handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen + | Handle::LocalWaiting(_) + | Handle::Read + | Handle::LocalClosed => unreachable!(), + Handle::LocalReady(..) => { + entry.insert(Handle::LocalOpen); + } + Handle::Write => (writer.vtable.cancel_write)(writer.handle), + }, + }); + writer + } +} + +impl<T> Drop for CancelableWrite<T> { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } +} + +impl<T> FutureWriter<T> { + #[doc(hidden)] + pub fn new(handle: u32, vtable: &'static FutureVtable<T>) -> Self { + Self { handle, vtable } + } + + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite<T> { + let handle = self.handle; + let vtable = self.vtable; + CancelableWrite { + writer: Some(self), + future: super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + let mut v = Some(v); + Box::pin(future::poll_fn(move |cx| { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + entry.insert(Handle::LocalReady( + Box::new(v.take().unwrap()), + cx.waker().clone(), + )); + Poll::Pending + } + Handle::LocalReady(..) => Poll::Pending, + Handle::LocalClosed => Poll::Ready(()), + Handle::LocalWaiting(_) | Handle::Read | Handle::Write => { + unreachable!() + } + }, + }) + })) as Pin<Box<dyn Future<Output = _>>> + } + Handle::LocalWaiting(_) => { + let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalClosed) else { + unreachable!() + }; + _ = tx.send(Box::new(v)); + Box::pin(future::ready(())) + } + Handle::LocalClosed => Box::pin(future::ready(())), + Handle::Read | Handle::LocalReady(..) => unreachable!(), + Handle::Write => Box::pin((vtable.write)(handle, v).map(drop)), + }, + }), + } + } +} + +impl<T> Drop for FutureWriter<T> { + fn drop(&mut self) { + super::with_entry(self.handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get_mut() { + Handle::LocalOpen | Handle::LocalWaiting(_) | Handle::LocalReady(..) => { + entry.insert(Handle::LocalClosed); + } + Handle::Read => unreachable!(), + Handle::Write | Handle::LocalClosed => { + entry.remove(); + (self.vtable.close_writable)(self.handle); + } + }, + }); + } +} + +/// Represents a read operation which may be canceled prior to completion. +pub struct CancelableRead<T: 'static> { + reader: Option<FutureReader<T>>, + future: Pin<Box<dyn Future<Output = Option<T>>>>, +} + +impl<T> Future for CancelableRead<T> { + type Output = Option<T>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<T>> { + let me = self.get_mut(); + match me.future.poll_unpin(cx) { + Poll::Ready(v) => { + me.reader = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl<T> CancelableRead<T> { + /// Cancel this read if it hasn't already completed, returning the original `FutureReader`. + /// + /// This method will panic if the read has already completed. + pub fn cancel(mut self) -> FutureReader<T> { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader<T> { + let reader = self.reader.take().unwrap(); + let handle = reader.handle.load(Relaxed); + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen + | Handle::LocalReady(..) + | Handle::Write + | Handle::LocalClosed => unreachable!(), + Handle::LocalWaiting(_) => { + entry.insert(Handle::LocalOpen); + } + Handle::Read => (reader.vtable.cancel_read)(handle), + }, + }); + reader + } +} + +impl<T> Drop for CancelableRead<T> { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } +} + +/// Represents the readable end of a Component Model `future`. +pub struct FutureReader<T: 'static> { + handle: AtomicU32, + vtable: &'static FutureVtable<T>, +} + +impl<T> fmt::Debug for FutureReader<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } +} + +impl<T> FutureReader<T> { + #[doc(hidden)] + pub fn new(handle: u32, vtable: &'static FutureVtable<T>) -> Self { + Self { + handle: AtomicU32::new(handle), + vtable, + } + } + + #[doc(hidden)] + pub fn from_handle_and_vtable(handle: u32, vtable: &'static FutureVtable<T>) -> Self { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(entry) => { + entry.insert(Handle::Read); + } + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write => { + entry.insert(Handle::LocalOpen); + } + Handle::Read + | Handle::LocalOpen + | Handle::LocalReady(..) + | Handle::LocalWaiting(_) + | Handle::LocalClosed => { + unreachable!() + } + }, + }); + + Self { + handle: AtomicU32::new(handle), + vtable, + } + } + + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + let handle = self.handle.swap(u32::MAX, Relaxed); + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + entry.insert(Handle::Write); + } + Handle::Read | Handle::LocalClosed => { + entry.remove(); + } + Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => unreachable!(), + }, + }); + + handle + } +} + +impl<T> IntoFuture for FutureReader<T> { + type Output = Option<T>; + type IntoFuture = CancelableRead<T>; + + /// Convert this object into a `Future` which will resolve when a value is + /// written to the writable end of this `future` (yielding a `Some` result) + /// or when the writable end is dropped (yielding a `None` result). + fn into_future(self) -> Self::IntoFuture { + let handle = self.handle.load(Relaxed); + let vtable = self.vtable; + CancelableRead { + reader: Some(self), + future: super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write | Handle::LocalWaiting(_) => unreachable!(), + Handle::Read => Box::pin(async move { (vtable.read)(handle).await }) + as Pin<Box<dyn Future<Output = _>>>, + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + Box::pin(async move { rx.await.ok().map(|v| *v.downcast().unwrap()) }) + } + Handle::LocalClosed => Box::pin(future::ready(None)), + Handle::LocalReady(..) => { + let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalClosed) else { + unreachable!() + }; + waker.wake(); + Box::pin(future::ready(Some(*v.downcast().unwrap()))) + } + }, + }), + } + } +} + +impl<T> Drop for FutureReader<T> { + fn drop(&mut self) { + match self.handle.load(Relaxed) { + u32::MAX => {} + handle => { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get_mut() { + Handle::LocalReady(..) => { + let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed) + else { + unreachable!() + }; + waker.wake(); + } + Handle::LocalOpen | Handle::LocalWaiting(_) => { + entry.insert(Handle::LocalClosed); + } + Handle::Read | Handle::LocalClosed => { + entry.remove(); + (self.vtable.close_readable)(handle); + } + Handle::Write => unreachable!(), + }, + }); + } + } + } +} diff --git a/vendor/wit-bindgen-rt/src/async_support/stream_support.rs b/vendor/wit-bindgen-rt/src/async_support/stream_support.rs new file mode 100644 index 00000000..d80b96d6 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/async_support/stream_support.rs @@ -0,0 +1,431 @@ +extern crate std; + +use { + super::Handle, + futures::{ + channel::oneshot, + future::{self, FutureExt}, + sink::Sink, + stream::Stream, + }, + std::{ + boxed::Box, + collections::hash_map::Entry, + convert::Infallible, + fmt, + future::Future, + iter, + mem::{self, MaybeUninit}, + pin::Pin, + sync::atomic::{AtomicU32, Ordering::Relaxed}, + task::{Context, Poll}, + vec::Vec, + }, +}; + +fn ceiling(x: usize, y: usize) -> usize { + (x / y) + if x % y == 0 { 0 } else { 1 } +} + +#[doc(hidden)] +pub struct StreamVtable<T> { + pub write: fn(future: u32, values: &[T]) -> Pin<Box<dyn Future<Output = usize> + '_>>, + pub read: fn( + future: u32, + values: &mut [MaybeUninit<T>], + ) -> Pin<Box<dyn Future<Output = Option<usize>> + '_>>, + pub cancel_write: fn(future: u32), + pub cancel_read: fn(future: u32), + pub close_writable: fn(future: u32), + pub close_readable: fn(future: u32), +} + +struct CancelWriteOnDrop<T: 'static> { + handle: Option<u32>, + vtable: &'static StreamVtable<T>, +} + +impl<T> Drop for CancelWriteOnDrop<T> { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen + | Handle::LocalWaiting(_) + | Handle::Read + | Handle::LocalClosed => unreachable!(), + Handle::LocalReady(..) => { + entry.insert(Handle::LocalOpen); + } + Handle::Write => (self.vtable.cancel_write)(handle), + }, + }); + } + } +} + +/// Represents the writable end of a Component Model `stream`. +pub struct StreamWriter<T: 'static> { + handle: u32, + future: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>, + vtable: &'static StreamVtable<T>, +} + +impl<T> StreamWriter<T> { + #[doc(hidden)] + pub fn new(handle: u32, vtable: &'static StreamVtable<T>) -> Self { + Self { + handle, + future: None, + vtable, + } + } + + /// Cancel the current pending write operation. + /// + /// This will panic if no such operation is pending. + pub fn cancel(&mut self) { + assert!(self.future.is_some()); + self.future = None; + } +} + +impl<T> fmt::Debug for StreamWriter<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } +} + +impl<T> Sink<Vec<T>> for StreamWriter<T> { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { + let me = self.get_mut(); + + if let Some(future) = &mut me.future { + match future.as_mut().poll(cx) { + Poll::Ready(_) => { + me.future = None; + Poll::Ready(Ok(())) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Ready(Ok(())) + } + } + + fn start_send(self: Pin<&mut Self>, item: Vec<T>) -> Result<(), Self::Error> { + assert!(self.future.is_none()); + super::with_entry(self.handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + let handle = self.handle; + let mut item = Some(item); + let mut cancel_on_drop = Some(CancelWriteOnDrop::<T> { + handle: Some(handle), + vtable: self.vtable, + }); + self.get_mut().future = Some(Box::pin(future::poll_fn(move |cx| { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + if let Some(item) = item.take() { + entry.insert(Handle::LocalReady( + Box::new(item), + cx.waker().clone(), + )); + Poll::Pending + } else { + cancel_on_drop.take().unwrap().handle = None; + Poll::Ready(()) + } + } + Handle::LocalReady(..) => Poll::Pending, + Handle::LocalClosed => { + cancel_on_drop.take().unwrap().handle = None; + Poll::Ready(()) + } + Handle::LocalWaiting(_) | Handle::Read | Handle::Write => { + unreachable!() + } + }, + }) + }))); + } + Handle::LocalWaiting(_) => { + let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalOpen) else { + unreachable!() + }; + _ = tx.send(Box::new(item)); + } + Handle::LocalClosed => (), + Handle::Read | Handle::LocalReady(..) => unreachable!(), + Handle::Write => { + let handle = self.handle; + let vtable = self.vtable; + let mut cancel_on_drop = CancelWriteOnDrop::<T> { + handle: Some(handle), + vtable, + }; + self.get_mut().future = Some(Box::pin(async move { + (vtable.write)(handle, &item).await; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + })); + } + }, + }); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { + self.poll_ready(cx) + } +} + +impl<T> Drop for StreamWriter<T> { + fn drop(&mut self) { + self.future = None; + + super::with_entry(self.handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get_mut() { + Handle::LocalOpen | Handle::LocalWaiting(_) | Handle::LocalReady(..) => { + entry.insert(Handle::LocalClosed); + } + Handle::Read => unreachable!(), + Handle::Write | Handle::LocalClosed => { + entry.remove(); + (self.vtable.close_writable)(self.handle); + } + }, + }); + } +} + +struct CancelReadOnDrop<T: 'static> { + handle: Option<u32>, + vtable: &'static StreamVtable<T>, +} + +impl<T> Drop for CancelReadOnDrop<T> { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen + | Handle::LocalReady(..) + | Handle::Write + | Handle::LocalClosed => unreachable!(), + Handle::LocalWaiting(_) => { + entry.insert(Handle::LocalOpen); + } + Handle::Read => (self.vtable.cancel_read)(handle), + }, + }); + } + } +} + +/// Represents the readable end of a Component Model `stream`. +pub struct StreamReader<T: 'static> { + handle: AtomicU32, + future: Option<Pin<Box<dyn Future<Output = Option<Vec<T>>> + 'static>>>, + vtable: &'static StreamVtable<T>, +} + +impl<T> StreamReader<T> { + /// Cancel the current pending read operation. + /// + /// This will panic if no such operation is pending. + pub fn cancel(&mut self) { + assert!(self.future.is_some()); + self.future = None; + } +} + +impl<T> fmt::Debug for StreamReader<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } +} + +impl<T> StreamReader<T> { + #[doc(hidden)] + pub fn new(handle: u32, vtable: &'static StreamVtable<T>) -> Self { + Self { + handle: AtomicU32::new(handle), + future: None, + vtable, + } + } + + #[doc(hidden)] + pub fn from_handle_and_vtable(handle: u32, vtable: &'static StreamVtable<T>) -> Self { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(entry) => { + entry.insert(Handle::Read); + } + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write => { + entry.insert(Handle::LocalOpen); + } + Handle::Read + | Handle::LocalOpen + | Handle::LocalReady(..) + | Handle::LocalWaiting(_) + | Handle::LocalClosed => { + unreachable!() + } + }, + }); + + Self { + handle: AtomicU32::new(handle), + future: None, + vtable, + } + } + + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + let handle = self.handle.swap(u32::MAX, Relaxed); + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + entry.insert(Handle::Write); + } + Handle::Read | Handle::LocalClosed => { + entry.remove(); + } + Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => unreachable!(), + }, + }); + + handle + } +} + +impl<T> Stream for StreamReader<T> { + type Item = Vec<T>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> { + let me = self.get_mut(); + + if me.future.is_none() { + me.future = Some(super::with_entry( + me.handle.load(Relaxed), + |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write | Handle::LocalWaiting(_) => unreachable!(), + Handle::Read => { + let handle = me.handle.load(Relaxed); + let vtable = me.vtable; + let mut cancel_on_drop = CancelReadOnDrop::<T> { + handle: Some(handle), + vtable, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::<T>().max(1))) + .collect::<Vec<_>>(); + + let result = + if let Some(count) = (vtable.read)(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) as Pin<Box<dyn Future<Output = _>>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop::<T> { + handle: Some(me.handle.load(Relaxed)), + vtable: me.vtable, + }; + Box::pin(async move { + let result = + rx.map(|v| v.ok().map(|v| *v.downcast().unwrap())).await; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) + } + Handle::LocalClosed => Box::pin(future::ready(None)), + Handle::LocalReady(..) => { + let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen) + else { + unreachable!() + }; + waker.wake(); + Box::pin(future::ready(Some(*v.downcast().unwrap()))) + } + }, + }, + )); + } + + match me.future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl<T> Drop for StreamReader<T> { + fn drop(&mut self) { + self.future = None; + + match self.handle.load(Relaxed) { + u32::MAX => {} + handle => { + super::with_entry(handle, |entry| match entry { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get_mut() { + Handle::LocalReady(..) => { + let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed) + else { + unreachable!() + }; + waker.wake(); + } + Handle::LocalOpen | Handle::LocalWaiting(_) => { + entry.insert(Handle::LocalClosed); + } + Handle::Read | Handle::LocalClosed => { + entry.remove(); + (self.vtable.close_readable)(handle); + } + Handle::Write => unreachable!(), + }, + }); + } + } + } +} diff --git a/vendor/wit-bindgen-rt/src/cabi_realloc.c b/vendor/wit-bindgen-rt/src/cabi_realloc.c new file mode 100644 index 00000000..46e59ec8 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/cabi_realloc.c @@ -0,0 +1,10 @@ +// This file is generated by ./ci/rebuild-libcabi-realloc.sh + +#include <stdint.h> + +extern void *cabi_realloc_wit_bindgen_0_39_0(void *ptr, size_t old_size, size_t align, size_t new_size); + +__attribute__((__weak__, __export_name__("cabi_realloc"))) +void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { + return cabi_realloc_wit_bindgen_0_39_0(ptr, old_size, align, new_size); +} diff --git a/vendor/wit-bindgen-rt/src/cabi_realloc.o b/vendor/wit-bindgen-rt/src/cabi_realloc.o Binary files differnew file mode 100644 index 00000000..ea5f3988 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/cabi_realloc.o diff --git a/vendor/wit-bindgen-rt/src/cabi_realloc.rs b/vendor/wit-bindgen-rt/src/cabi_realloc.rs new file mode 100644 index 00000000..5a2fd72f --- /dev/null +++ b/vendor/wit-bindgen-rt/src/cabi_realloc.rs @@ -0,0 +1,11 @@ +// This file is generated by ./ci/rebuild-libcabi-realloc.sh + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_39_0( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + crate::cabi_realloc(old_ptr, old_len, align, new_len) +} diff --git a/vendor/wit-bindgen-rt/src/lib.rs b/vendor/wit-bindgen-rt/src/lib.rs new file mode 100644 index 00000000..0f285836 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/lib.rs @@ -0,0 +1,118 @@ +#![no_std] + +extern crate alloc; + +// Re-export `bitflags` so that we can reference it from macros. +#[cfg(feature = "bitflags")] +#[doc(hidden)] +pub use bitflags; + +/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +#[cfg(not(target_env = "p2"))] +mod cabi_realloc; + +/// This function is called from generated bindings and will be deleted by +/// the linker. The purpose of this function is to force a reference to the +/// symbol `cabi_realloc` to make its way through to the final linker +/// command line. That way `wasm-ld` will pick it up, see it needs to be +/// exported, and then export it. +/// +/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +pub fn maybe_link_cabi_realloc() { + #[cfg(all(target_family = "wasm", not(target_env = "p2")))] + { + extern "C" { + fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, + ) -> *mut u8; + } + // Force the `cabi_realloc` symbol to be referenced from here. This + // is done with a `#[used]` Rust `static` to ensure that this + // reference makes it all the way to the linker before it's + // considered for garbage collection. When the linker sees it it'll + // remove this `static` here (due to it not actually being needed) + // but the linker will have at that point seen the `cabi_realloc` + // symbol and it should get exported. + #[used] + static _NAME_DOES_NOT_MATTER: unsafe extern "C" fn( + *mut u8, + usize, + usize, + usize, + ) -> *mut u8 = cabi_realloc; + } +} + +/// NB: this function is called by a generated function in the +/// `cabi_realloc` module above. It's otherwise never explicitly called. +/// +/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +#[cfg(not(target_env = "p2"))] +pub unsafe fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + use self::alloc::alloc::{self, Layout}; + + let layout; + let ptr = if old_len == 0 { + if new_len == 0 { + return align as *mut u8; + } + layout = Layout::from_size_align_unchecked(new_len, align); + alloc::alloc(layout) + } else { + debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); + layout = Layout::from_size_align_unchecked(old_len, align); + alloc::realloc(old_ptr, layout, new_len) + }; + if ptr.is_null() { + // Print a nice message in debug mode, but in release mode don't + // pull in so many dependencies related to printing so just emit an + // `unreachable` instruction. + if cfg!(debug_assertions) { + alloc::handle_alloc_error(layout); + } else { + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + #[cfg(not(target_arch = "wasm32"))] + unreachable!(); + } + } + return ptr; +} + +/// Provide a hook for generated export functions to run static constructors at +/// most once. +/// +/// wit-bindgen-rust generates a call to this function at the start of all +/// component export functions. Importantly, it is not called as part of +/// `cabi_realloc`, which is a *core* export func, but should not execute ctors. +#[cfg(target_arch = "wasm32")] +pub fn run_ctors_once() { + static mut RUN: bool = false; + unsafe { + if !RUN { + // This function is synthesized by `wasm-ld` to run all static + // constructors. wasm-ld will either provide an implementation + // of this symbol, or synthesize a wrapper around each + // exported function to (unconditionally) run ctors. By using + // this function, the linked module is opting into "manually" + // running ctors. + extern "C" { + fn __wasm_call_ctors(); + } + __wasm_call_ctors(); + RUN = true; + } + } +} + +/// Support for using the Component Model Async ABI +#[cfg(feature = "async")] +pub mod async_support; diff --git a/vendor/wit-bindgen-rt/src/libwit_bindgen_cabi_realloc.a b/vendor/wit-bindgen-rt/src/libwit_bindgen_cabi_realloc.a Binary files differnew file mode 100644 index 00000000..709cea46 --- /dev/null +++ b/vendor/wit-bindgen-rt/src/libwit_bindgen_cabi_realloc.a |
