Bug 1900400 -- Update UniFFI to 0.27.3. r=supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D212455
This commit is contained in:
Ben Dean-Kawamura 2024-06-10 16:15:28 +00:00
parent 7bd02424dc
commit 378bcfe12c
66 changed files with 669 additions and 4764 deletions

51
Cargo.lock generated
View File

@ -4275,12 +4275,6 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oneshot-uniffi"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c"
[[package]]
name = "ordered-float"
version = "3.4.0"
@ -5734,8 +5728,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
dependencies = [
"smawk",
"unicode-linebreak",
"unicode-width",
]
[[package]]
@ -6080,12 +6072,6 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-linebreak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
@ -6109,9 +6095,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uniffi"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5566fae48a5cb017005bf9cd622af5236b2a203a13fb548afde3506d3c68277"
checksum = "cb3a4c447c50fcda7bc5604a8588b7e1f37ffbfd8838a1516a290398efa7c6f0"
dependencies = [
"anyhow",
"uniffi_bindgen",
@ -6211,9 +6197,9 @@ dependencies = [
[[package]]
name = "uniffi_bindgen"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a77bb514bcd4bf27c9bd404d7c3f2a6a8131b957eba9c22cfeb7751c4278e09"
checksum = "0be2bc6bafd82c979b0faca77c7b26630d54017de9f5bd5a686ec6ef038ad5d9"
dependencies = [
"anyhow",
"askama",
@ -6235,9 +6221,9 @@ dependencies = [
[[package]]
name = "uniffi_build"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45cba427aeb7b3a8b54830c4c915079a7a3c62608dd03dddba1d867a8a023eb4"
checksum = "1c59b65d59685ff3a10569287c6419f76487b4052ac52d5a0df38b2253d7f440"
dependencies = [
"anyhow",
"camino",
@ -6246,9 +6232,9 @@ dependencies = [
[[package]]
name = "uniffi_checksum_derive"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae7e5a6c33b1dec3f255f57ec0b6af0f0b2bb3021868be1d5eec7a38e2905ebc"
checksum = "d5c400339a9d1d17be34257d0b407e91d64af335e5b4fa49f4bf28467fc8d635"
dependencies = [
"quote",
"syn",
@ -6256,25 +6242,24 @@ dependencies = [
[[package]]
name = "uniffi_core"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea3eb5474d50fc149b7e4d86b9c5bd4a61dcc167f0683902bf18ae7bbb3deef"
checksum = "a02e67ac9634b10da9e4aa63a29a7920b8f1395eafef1ea659b2dd76dda96906"
dependencies = [
"anyhow",
"bytes",
"camino",
"log",
"once_cell",
"oneshot-uniffi",
"paste",
"static_assertions",
]
[[package]]
name = "uniffi_macros"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18331d35003f46f0d04047fbe4227291815b83a937a8c32bc057f990962182c4"
checksum = "b6f08d5592c669b80a8af5066027098bebec4b4af17a9b8b299bac5f518ab89e"
dependencies = [
"bincode",
"camino",
@ -6290,9 +6275,9 @@ dependencies = [
[[package]]
name = "uniffi_meta"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7224422c4cfd181c7ca9fca2154abca4d21db962f926f270f996edd38b0c4b8"
checksum = "583bab49f2bdf5681f9732f8b67a7e555ad920dbb5427be21450217bf1818189"
dependencies = [
"anyhow",
"bytes",
@ -6302,9 +6287,9 @@ dependencies = [
[[package]]
name = "uniffi_testing"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ce878d0bdfc288b58797044eaaedf748526c56eef3575380bb4d4b19d69eee"
checksum = "13963044ca9bde9b709d2eee68bc11dafc7acea144ae0fdc0cf29ed4add44481"
dependencies = [
"anyhow",
"camino",
@ -6315,9 +6300,9 @@ dependencies = [
[[package]]
name = "uniffi_udl"
version = "0.27.1"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c43c9ed40a8d20a5c3eae2d23031092db6b96dc8e571beb449ba9757484cea0"
checksum = "b92f984bb0d9a06778f256aec963e1e9a80714014f7a90fb0e01008821fe5a97"
dependencies = [
"anyhow",
"textwrap",

View File

@ -54,8 +54,8 @@ resolver = "2"
[workspace.dependencies]
# Shared across multiple UniFFI consumers.
uniffi = "0.27.1"
uniffi_bindgen = "0.27.1"
uniffi = "0.27.3"
uniffi_bindgen = "0.27.3"
# Shared across multiple application-services consumers.
rusqlite = "0.31.0"

View File

@ -657,6 +657,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_bindgen]]
version = "0.27.1"
when = "2024-04-03"
@ -664,6 +670,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_bindgen]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_build]]
version = "0.27.1"
when = "2024-04-03"
@ -671,6 +683,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_build]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_checksum_derive]]
version = "0.27.1"
when = "2024-04-03"
@ -678,6 +696,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_checksum_derive]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_core]]
version = "0.27.1"
when = "2024-04-03"
@ -685,6 +709,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_core]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_macros]]
version = "0.27.1"
when = "2024-04-03"
@ -692,6 +722,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_macros]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_meta]]
version = "0.27.1"
when = "2024-04-03"
@ -699,6 +735,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_meta]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_testing]]
version = "0.27.1"
when = "2024-04-03"
@ -706,6 +748,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_testing]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.uniffi_udl]]
version = "0.27.1"
when = "2024-04-03"
@ -713,6 +761,12 @@ user-id = 111105
user-login = "mhammond"
user-name = "Mark Hammond"
[[publisher.uniffi_udl]]
version = "0.27.3"
when = "2024-06-03"
user-id = 127697
user-login = "bendk"
[[publisher.utf8_iter]]
version = "1.0.3"
when = "2022-09-09"

View File

@ -1 +0,0 @@
{"files":{"CHANGELOG.md":"4ad03d95d5532e8f2551e3e53877e6347c04c32f479c4edf517244ecd5921ac7","Cargo.lock":"5d85bcfda2ee559d243099fb26f3724ae239777d891e780a924804e30f6733ad","Cargo.toml":"07a73ff74274df3a7439fccc8acfe306fae0f51ad79a80edbc54b51a730314c0","README.md":"811ea1c958d5a65583d0223b7ab09bb282e7a51ed60f9a2cb90ef6d555325a68","benches/benches.rs":"67dcc916d0b7e28e396c28dac0499726366e1cb10e9991948d1c881a5abf5faa","check_mem_leaks.sh":"c1ab6ef27997c7f971352ab1c86a184004843c499bc24925da953aefcf1c624c","examples/recv_before_send.rs":"9a3cabcc2878990b61787d0048061b382555a8cd1a08b1ddec63a6e8a4a31e56","examples/recv_before_send_then_drop_sender.rs":"14706c6b4308a690662ceaa47f1699588bd833b3ec020eb9f42f220f3ffc7ae7","examples/recv_ref_before_send.rs":"43699f4720c46b5f138c260b866eb708ddf616e2b442ffa74a97373f4f48d4d0","examples/recv_ref_before_send_then_drop_sender.rs":"a190ed220cb4288d4965485365c9afaed30535cbfad5f8cb7389071b82d67cac","examples/recv_timeout_before_send.rs":"2262aa6531afce7816d43182ad9cbec2c04f3dc129064e11e89452278ce8b163","examples/recv_timeout_before_send_then_drop_sender.rs":"4cc8eade4c211f52f5b9be0f72a5906689b894490f4cb5255525e44106e7a4a8","examples/recv_with_dropped_sender.rs":"7906685053ce1c53ff6c26ce11d3221d4bf5ca3429d1d4d2c28de9237cb151c6","examples/send_before_recv.rs":"5555bd61ad52273b663007794128d8f012fc54272bd3225259b5546221bcd591","examples/send_then_drop_receiver.rs":"c3612de207309098404b057468687a2d2311d07f354b7e046398e35e93c4cdcf","examples/send_with_dropped_receiver.rs":"f5a7762b231a24a0db4397c5139437cba155d09b9dbb59872d662c7923080706","src/errors.rs":"a5aa56bc497dccdbdbe15b9070360f50835c762f11be4ee96e0d25b150168ac9","src/lib.rs":"4bef3602ff4f5d2b42ce963d722a48c9ff07275e75ef6bed7b523e8f45e459fe","src/loombox.rs":"fc85d1c2d3fda432be60f0c4d1d528e5998ec2b738a5b395a242285051b94d65","tests/assert_mem.rs":"b1e5190af01af22e55c7c1cd1ff2711807591f788e4eb8b6c6d89123e146105e","tests/async.rs":"6fd2826e589b94677d4eeed1080deda8bcc429aa05a20d843d1442a3a48ea757","tests/future.rs":"0e71f0293cd5a8c44210e8882aca20cfbf1e3771ecd4e4f6b59b924c0d01dd97","tests/helpers/mod.rs":"19161ed33e0ba8862746f04678d0606dee90205896083f85d8c1dcd4d211ccb0","tests/helpers/waker.rs":"77494d49f62d0d320df3830643c306e06e6e20751d210cf6fa58b238bd96c3f9","tests/loom.rs":"ea350fa424a95581e1871bc0037badecc5a090f28fd10532917abbaf561218ab","tests/raw.rs":"5564615fea811b0061d8ad801356e60e0018ec4e3fb99cc739287ed5b96cb7cf","tests/sync.rs":"1186fa6cdb5a180944fa7d793ccb8be412c4a4e88bb504daa70bc097ee081b06"},"package":"6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c"}

View File

@ -1,69 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
### Categories each change fall into
* **Added**: for new features.
* **Changed**: for changes in existing functionality.
* **Deprecated**: for soon-to-be removed features.
* **Removed**: for now removed features.
* **Fixed**: for any bug fixes.
* **Security**: in case of vulnerabilities.
## [Unreleased]
## [0.1.6] - 2023-09-14
### Added
* Add `into_raw` and `from_raw` methods on both `Sender` and `Receiver`. Allows passing `oneshot`
channels over FFI without an extra layer of heap allocation.
## [0.1.5] - 2022-09-01
### Fixed
- Handle the UNPARKING state correctly in all recv methods. `try_recv` will now not panic
if used on a `Receiver` that is being unparked from an async wait. The other `recv` methods
will still panic (as they should), but with a better error message.
## [0.1.4] - 2022-08-30
### Changed
- Upgrade to Rust edition 2021. Also increases the MSRV to Rust 1.60.
- Add null-pointer optimization to `Sender`, `Receiver` and `SendError`.
This reduces the call stack size of Sender::send and it makes
`Option<Sender>` and `Option<Receiver>` pointer sized (#18).
- Relax the memory ordering of all atomic operations from `SeqCst` to the most appropriate
lower ordering (#17 + #20).
### Fixed
- Fix undefined behavior due to multiple mutable references to the same channel instance (#18).
- Fix race condition that could happen during unparking of a receiving `Receiver` (#17 + #20).
## [0.1.3] - 2021-11-23
### Fixed
- Keep the *last* `Waker` in `Future::poll`, not the *first* one. Stops breaking the contract
on how futures should work.
## [0.1.2] - 2020-08-11
### Fixed
- Fix unreachable code panic that happened if the `Receiver` of an empty but open channel was
polled and then dropped.
## [0.1.1] - 2020-05-10
Initial implementation. Supports basically all the (for now) intended functionality.
Sender is as lock-free as I think it can get and the receiver can both do thread blocking
and be awaited asynchronously. The receiver also has a wait-free `try_recv` method.
The crate has two features. They are activated by default, but the user can opt out of async
support as well as usage of libstd (making the crate `no_std` but still requiring liballoc)
## [0.1.0] - 2019-05-30
Name reserved on crate.io by someone other than the author of this crate.

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.60.0"
name = "oneshot-uniffi"
version = "0.1.6"
authors = ["Linus Färnstrand <faern@faern.net>"]
description = """
Patched version of oneshot specifically for the UniFFI project.
This removes the `loom` target and dependency which helps with UniFFI's downstream consumers.
"""
readme = "README.md"
keywords = [
"oneshot",
"spsc",
"async",
"sync",
"channel",
]
categories = [
"asynchronous",
"concurrency",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/faern/oneshot"
[[bench]]
name = "benches"
harness = false
[dev-dependencies.async-std]
version = "1"
features = ["attributes"]
[dev-dependencies.criterion]
version = "0.3"
[dev-dependencies.tokio]
version = "1"
features = [
"rt",
"rt-multi-thread",
"macros",
"time",
]
[features]
async = []
default = [
"std",
"async",
]
std = []

View File

@ -1,94 +0,0 @@
# oneshot
Oneshot spsc (single producer, single consumer) channel. Meaning each channel instance
can only transport a single message. This has a few nice outcomes. One thing is that
the implementation can be very efficient, utilizing the knowledge that there will
only be one message. But more importantly, it allows the API to be expressed in such
a way that certain edge cases that you don't want to care about when only sending a
single message on a channel does not exist. For example: The sender can't be copied
or cloned, and the send method takes ownership and consumes the sender.
So you are guaranteed, at the type level, that there can only be one message sent.
The sender's send method is non-blocking, and potentially lock- and wait-free.
See documentation on [Sender::send] for situations where it might not be fully wait-free.
The receiver supports both lock- and wait-free `try_recv` as well as indefinite and time
limited thread blocking receive operations. The receiver also implements `Future` and
supports asynchronously awaiting the message.
## Examples
This example sets up a background worker that processes requests coming in on a standard
mpsc channel and replies on a oneshot channel provided with each request. The worker can
be interacted with both from sync and async contexts since the oneshot receiver
can receive both blocking and async.
```rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
type Request = String;
// Starts a background thread performing some computation on requests sent to it.
// Delivers the response back over a oneshot channel.
fn spawn_processing_thread() -> mpsc::Sender<(Request, oneshot::Sender<usize>)> {
let (request_sender, request_receiver) = mpsc::channel::<(Request, oneshot::Sender<usize>)>();
thread::spawn(move || {
for (request_data, response_sender) in request_receiver.iter() {
let compute_operation = || request_data.len();
let _ = response_sender.send(compute_operation()); // <- Send on the oneshot channel
}
});
request_sender
}
let processor = spawn_processing_thread();
// If compiled with `std` the library can receive messages with timeout on regular threads
#[cfg(feature = "std")] {
let (response_sender, response_receiver) = oneshot::channel();
let request = Request::from("data from sync thread");
processor.send((request, response_sender)).expect("Processor down");
match response_receiver.recv_timeout(Duration::from_secs(1)) { // <- Receive on the oneshot channel
Ok(result) => println!("Processor returned {}", result),
Err(oneshot::RecvTimeoutError::Timeout) => eprintln!("Processor was too slow"),
Err(oneshot::RecvTimeoutError::Disconnected) => panic!("Processor exited"),
}
}
// If compiled with the `async` feature, the `Receiver` can be awaited in an async context
#[cfg(feature = "async")] {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move {
let (response_sender, response_receiver) = oneshot::channel();
let request = Request::from("data from sync thread");
processor.send((request, response_sender)).expect("Processor down");
match response_receiver.await { // <- Receive on the oneshot channel asynchronously
Ok(result) => println!("Processor returned {}", result),
Err(_e) => panic!("Processor exited"),
}
});
}
```
## Sync vs async
The main motivation for writing this library was that there were no (known to me) channel
implementations allowing you to seamlessly send messages between a normal thread and an async
task, or the other way around. If message passing is the way you are communicating, of course
that should work smoothly between the sync and async parts of the program!
This library achieves that by having a fast and cheap send operation that can
be used in both sync threads and async tasks. The receiver has both thread blocking
receive methods for synchronous usage, and implements `Future` for asynchronous usage.
The receiving endpoint of this channel implements Rust's `Future` trait and can be waited on
in an asynchronous task. This implementation is completely executor/runtime agnostic. It should
be possible to use this library with any executor.
License: MIT OR Apache-2.0

View File

@ -1,122 +0,0 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::mem;
use std::time::{Duration, Instant};
criterion_group!(benches, bench);
criterion_main!(benches);
macro_rules! bench_send_and_recv {
($c:expr, $($type:ty => $value:expr);+) => {
// Sanity check that all $values are of $type.
$(let _: $type = $value;)*
{
let mut group = $c.benchmark_group("create_channel");
$(group.bench_function(stringify!($type), |b| {
b.iter(oneshot::channel::<$type>)
});)*
group.finish();
}
{
let mut group = $c.benchmark_group("create_and_send");
$(group.bench_function(stringify!($type), |b| {
b.iter(|| {
let (sender, _receiver) = oneshot::channel();
sender.send(black_box($value)).unwrap()
});
});)*
group.finish();
}
{
let mut group = $c.benchmark_group("create_and_send_on_closed");
$(group.bench_function(stringify!($type), |b| {
b.iter(|| {
let (sender, _) = oneshot::channel();
sender.send(black_box($value)).unwrap_err()
});
});)*
group.finish();
}
{
let mut group = $c.benchmark_group("create_send_and_recv");
$(group.bench_function(stringify!($type), |b| {
b.iter(|| {
let (sender, receiver) = oneshot::channel();
sender.send(black_box($value)).unwrap();
receiver.recv().unwrap()
});
});)*
group.finish();
}
{
let mut group = $c.benchmark_group("create_send_and_recv_ref");
$(group.bench_function(stringify!($type), |b| {
b.iter(|| {
let (sender, receiver) = oneshot::channel();
sender.send(black_box($value)).unwrap();
receiver.recv_ref().unwrap()
});
});)*
group.finish();
}
};
}
fn bench(c: &mut Criterion) {
bench_send_and_recv!(c,
() => ();
u8 => 7u8;
usize => 9876usize;
u128 => 1234567u128;
[u8; 64] => [0b10101010u8; 64];
[u8; 4096] => [0b10101010u8; 4096]
);
bench_try_recv(c);
bench_recv_deadline_now(c);
bench_recv_timeout_zero(c);
}
fn bench_try_recv(c: &mut Criterion) {
let (sender, receiver) = oneshot::channel::<u128>();
c.bench_function("try_recv_empty", |b| {
b.iter(|| receiver.try_recv().unwrap_err())
});
mem::drop(sender);
c.bench_function("try_recv_empty_closed", |b| {
b.iter(|| receiver.try_recv().unwrap_err())
});
}
fn bench_recv_deadline_now(c: &mut Criterion) {
let now = Instant::now();
{
let (_sender, receiver) = oneshot::channel::<u128>();
c.bench_function("recv_deadline_now", |b| {
b.iter(|| receiver.recv_deadline(now).unwrap_err())
});
}
{
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
c.bench_function("recv_deadline_now_closed", |b| {
b.iter(|| receiver.recv_deadline(now).unwrap_err())
});
}
}
fn bench_recv_timeout_zero(c: &mut Criterion) {
let zero = Duration::from_nanos(0);
{
let (_sender, receiver) = oneshot::channel::<u128>();
c.bench_function("recv_timeout_zero", |b| {
b.iter(|| receiver.recv_timeout(zero).unwrap_err())
});
}
{
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
c.bench_function("recv_timeout_zero_closed", |b| {
b.iter(|| receiver.recv_timeout(zero).unwrap_err())
});
}
}

View File

@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -eu
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
for example_path in examples/*.rs; do
example_filename=$(basename -- $example_path)
example=${example_filename%.*}
echo $example
cargo valgrind run --example "$example"
done

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
sender.send(9u128).unwrap();
});
assert_eq!(receiver.recv(), Ok(9));
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
std::mem::drop(sender);
});
assert!(receiver.recv().is_err());
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
sender.send(9u128).unwrap();
});
assert_eq!(receiver.recv_ref(), Ok(9));
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
std::mem::drop(sender);
});
assert!(receiver.recv_ref().is_err());
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
sender.send(9u128).unwrap();
});
assert_eq!(receiver.recv_timeout(Duration::from_millis(100)), Ok(9));
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,18 +0,0 @@
#[cfg(feature = "std")]
fn main() {
use std::thread;
use std::time::Duration;
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
std::mem::drop(sender);
});
assert!(receiver.recv_timeout(Duration::from_millis(100)).is_err());
t.join().unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,11 +0,0 @@
#[cfg(feature = "std")]
fn main() {
let (sender, receiver) = oneshot::channel::<u128>();
std::mem::drop(sender);
receiver.recv().unwrap_err();
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,11 +0,0 @@
#[cfg(feature = "std")]
fn main() {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
assert_eq!(receiver.recv(), Ok(19i128));
}
#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"sync\" feature is used");
}

View File

@ -1,7 +0,0 @@
use std::mem;
fn main() {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
mem::drop(receiver);
}

View File

@ -1,8 +0,0 @@
use std::mem;
fn main() {
let (sender, receiver) = oneshot::channel();
mem::drop(receiver);
let send_error = sender.send(5u128).unwrap_err();
assert_eq!(send_error.into_inner(), 5);
}

View File

@ -1,147 +0,0 @@
use super::{dealloc, Channel};
use core::fmt;
use core::mem;
use core::ptr::NonNull;
/// An error returned when trying to send on a closed channel. Returned from
/// [`Sender::send`](crate::Sender::send) if the corresponding [`Receiver`](crate::Receiver)
/// has already been dropped.
///
/// The message that could not be sent can be retreived again with [`SendError::into_inner`].
pub struct SendError<T> {
channel_ptr: NonNull<Channel<T>>,
}
unsafe impl<T: Send> Send for SendError<T> {}
unsafe impl<T: Sync> Sync for SendError<T> {}
impl<T> SendError<T> {
/// # Safety
///
/// By calling this function, the caller semantically transfers ownership of the
/// channel's resources to the created `SendError`. Thus the caller must ensure that the
/// pointer is not used in a way which would violate this ownership transfer. Moreover,
/// the caller must assert that the channel contains a valid, initialized message.
pub(crate) const unsafe fn new(channel_ptr: NonNull<Channel<T>>) -> Self {
Self { channel_ptr }
}
/// Consumes the error and returns the message that failed to be sent.
#[inline]
pub fn into_inner(self) -> T {
let channel_ptr = self.channel_ptr;
// Don't run destructor if we consumed ourselves. Freeing happens here.
mem::forget(self);
// SAFETY: we have ownership of the channel
let channel: &Channel<T> = unsafe { channel_ptr.as_ref() };
// SAFETY: we know that the message is initialized according to the safety requirements of
// `new`
let message = unsafe { channel.take_message() };
// SAFETY: we own the channel
unsafe { dealloc(channel_ptr) };
message
}
/// Get a reference to the message that failed to be sent.
#[inline]
pub fn as_inner(&self) -> &T {
unsafe { self.channel_ptr.as_ref().message().assume_init_ref() }
}
}
impl<T> Drop for SendError<T> {
fn drop(&mut self) {
// SAFETY: we have ownership of the channel and require that the message is initialized
// upon construction
unsafe {
self.channel_ptr.as_ref().drop_message();
dealloc(self.channel_ptr);
}
}
}
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"sending on a closed channel".fmt(f)
}
}
impl<T> fmt::Debug for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SendError<{}>(_)", stringify!(T))
}
}
#[cfg(feature = "std")]
impl<T> std::error::Error for SendError<T> {}
/// An error returned from the blocking [`Receiver::recv`](crate::Receiver::recv) method.
///
/// The receive operation can only fail if the corresponding [`Sender`](crate::Sender) was dropped
/// before sending any message, or if a message has already been sent and received on the channel.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct RecvError;
impl fmt::Display for RecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"receiving on a closed channel".fmt(f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for RecvError {}
/// An error returned when failing to receive a message in the non-blocking
/// [`Receiver::try_recv`](crate::Receiver::try_recv).
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TryRecvError {
/// The channel is still open, but there was no message present in it.
Empty,
/// The channel is closed. Either the sender was dropped before sending any message, or the
/// message has already been extracted from the receiver.
Disconnected,
}
impl fmt::Display for TryRecvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
TryRecvError::Empty => "receiving on an empty channel",
TryRecvError::Disconnected => "receiving on a closed channel",
};
msg.fmt(f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryRecvError {}
/// An error returned when failing to receive a message in
/// [`Receiver::recv_timeout`](crate::Receiver::recv_timeout).
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RecvTimeoutError {
/// No message arrived on the channel before the timeout was reached. The channel is still open.
Timeout,
/// The channel is closed. Either the sender was dropped before sending any message, or the
/// message has already been extracted from the receiver.
Disconnected,
}
impl fmt::Display for RecvTimeoutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
RecvTimeoutError::Timeout => "timed out waiting on channel",
RecvTimeoutError::Disconnected => "channel is empty and sending half is closed",
};
msg.fmt(f)
}
}
#[cfg(feature = "std")]
impl std::error::Error for RecvTimeoutError {}

File diff suppressed because it is too large Load Diff

View File

@ -1,151 +0,0 @@
use core::{borrow, fmt, hash, mem, ptr};
use loom::alloc;
pub struct Box<T: ?Sized> {
ptr: *mut T,
}
impl<T> Box<T> {
pub fn new(value: T) -> Self {
let layout = alloc::Layout::new::<T>();
let ptr = unsafe { alloc::alloc(layout) } as *mut T;
unsafe { ptr::write(ptr, value) };
Self { ptr }
}
}
impl<T: ?Sized> Box<T> {
#[inline]
pub fn into_raw(b: Box<T>) -> *mut T {
let ptr = b.ptr;
mem::forget(b);
ptr
}
pub const unsafe fn from_raw(ptr: *mut T) -> Box<T> {
Self { ptr }
}
}
impl<T: ?Sized> Drop for Box<T> {
fn drop(&mut self) {
unsafe {
let size = mem::size_of_val(&*self.ptr);
let align = mem::align_of_val(&*self.ptr);
let layout = alloc::Layout::from_size_align(size, align).unwrap();
ptr::drop_in_place(self.ptr);
alloc::dealloc(self.ptr as *mut u8, layout);
}
}
}
unsafe impl<T: Send> Send for Box<T> {}
unsafe impl<T: Sync> Sync for Box<T> {}
impl<T: ?Sized> core::ops::Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T: ?Sized> core::ops::DerefMut for Box<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<T: ?Sized> borrow::Borrow<T> for Box<T> {
fn borrow(&self) -> &T {
&**self
}
}
impl<T: ?Sized> borrow::BorrowMut<T> for Box<T> {
fn borrow_mut(&mut self) -> &mut T {
&mut **self
}
}
impl<T: ?Sized> AsRef<T> for Box<T> {
fn as_ref(&self) -> &T {
&**self
}
}
impl<T: ?Sized> AsMut<T> for Box<T> {
fn as_mut(&mut self) -> &mut T {
&mut **self
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for Box<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Box<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: Clone> Clone for Box<T> {
#[inline]
fn clone(&self) -> Box<T> {
Self::new(self.as_ref().clone())
}
}
impl<T: ?Sized + PartialEq> PartialEq for Box<T> {
#[inline]
fn eq(&self, other: &Box<T>) -> bool {
PartialEq::eq(&**self, &**other)
}
#[allow(clippy::partialeq_ne_impl)]
#[inline]
fn ne(&self, other: &Box<T>) -> bool {
PartialEq::ne(&**self, &**other)
}
}
impl<T: ?Sized + Eq> Eq for Box<T> {}
impl<T: ?Sized + PartialOrd> PartialOrd for Box<T> {
#[inline]
fn partial_cmp(&self, other: &Box<T>) -> Option<core::cmp::Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
#[inline]
fn lt(&self, other: &Box<T>) -> bool {
PartialOrd::lt(&**self, &**other)
}
#[inline]
fn le(&self, other: &Box<T>) -> bool {
PartialOrd::le(&**self, &**other)
}
#[inline]
fn ge(&self, other: &Box<T>) -> bool {
PartialOrd::ge(&**self, &**other)
}
#[inline]
fn gt(&self, other: &Box<T>) -> bool {
PartialOrd::gt(&**self, &**other)
}
}
impl<T: ?Sized + Ord> Ord for Box<T> {
#[inline]
fn cmp(&self, other: &Box<T>) -> core::cmp::Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: ?Sized + hash::Hash> hash::Hash for Box<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}

View File

@ -1,37 +0,0 @@
use oneshot::{Receiver, Sender};
use std::mem;
/// Just sanity check that both channel endpoints stay the size of a single pointer.
#[test]
fn channel_endpoints_single_pointer() {
const PTR_SIZE: usize = mem::size_of::<*const ()>();
assert_eq!(mem::size_of::<Sender<()>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Receiver<()>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Sender<u8>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Receiver<u8>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Sender<[u8; 1024]>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Receiver<[u8; 1024]>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Option<Sender<[u8; 1024]>>>(), PTR_SIZE);
assert_eq!(mem::size_of::<Option<Receiver<[u8; 1024]>>>(), PTR_SIZE);
}
/// Check that the `SendError` stays small. Useful to automatically detect if it is refactored
/// to become large. We do not want the stack requirement for calling `Sender::send` to grow.
#[test]
fn error_sizes() {
const PTR_SIZE: usize = mem::size_of::<usize>();
assert_eq!(mem::size_of::<oneshot::SendError<()>>(), PTR_SIZE);
assert_eq!(mem::size_of::<oneshot::SendError<u8>>(), PTR_SIZE);
assert_eq!(mem::size_of::<oneshot::SendError<[u8; 1024]>>(), PTR_SIZE);
// The type returned from `Sender::send` is also just pointer sized
assert_eq!(
mem::size_of::<Result<(), oneshot::SendError<[u8; 1024]>>>(),
PTR_SIZE
);
}

View File

@ -1,128 +0,0 @@
#![cfg(all(feature = "async", not(loom)))]
use core::mem;
use core::time::Duration;
mod helpers;
use helpers::DropCounter;
#[tokio::test]
async fn send_before_await_tokio() {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
assert_eq!(receiver.await, Ok(19i128));
}
#[async_std::test]
async fn send_before_await_async_std() {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
assert_eq!(receiver.await, Ok(19i128));
}
#[tokio::test]
async fn await_with_dropped_sender_tokio() {
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
receiver.await.unwrap_err();
}
#[async_std::test]
async fn await_with_dropped_sender_async_std() {
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
receiver.await.unwrap_err();
}
#[tokio::test]
async fn await_before_send_tokio() {
let (sender, receiver) = oneshot::channel();
let (message, counter) = DropCounter::new(79u128);
let t = tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(10)).await;
sender.send(message)
});
let returned_message = receiver.await.unwrap();
assert_eq!(counter.count(), 0);
assert_eq!(*returned_message.value(), 79u128);
mem::drop(returned_message);
assert_eq!(counter.count(), 1);
t.await.unwrap().unwrap();
}
#[async_std::test]
async fn await_before_send_async_std() {
let (sender, receiver) = oneshot::channel();
let (message, counter) = DropCounter::new(79u128);
let t = async_std::task::spawn(async move {
async_std::task::sleep(Duration::from_millis(10)).await;
sender.send(message)
});
let returned_message = receiver.await.unwrap();
assert_eq!(counter.count(), 0);
assert_eq!(*returned_message.value(), 79u128);
mem::drop(returned_message);
assert_eq!(counter.count(), 1);
t.await.unwrap();
}
#[tokio::test]
async fn await_before_send_then_drop_sender_tokio() {
let (sender, receiver) = oneshot::channel::<u128>();
let t = tokio::spawn(async {
tokio::time::sleep(Duration::from_millis(10)).await;
mem::drop(sender);
});
assert!(receiver.await.is_err());
t.await.unwrap();
}
#[async_std::test]
async fn await_before_send_then_drop_sender_async_std() {
let (sender, receiver) = oneshot::channel::<u128>();
let t = async_std::task::spawn(async {
async_std::task::sleep(Duration::from_millis(10)).await;
mem::drop(sender);
});
assert!(receiver.await.is_err());
t.await;
}
// Tests that the Receiver handles being used synchronously even after being polled
#[tokio::test]
async fn poll_future_and_then_try_recv() {
use core::future::Future;
use core::pin::Pin;
use core::task::{self, Poll};
struct StupidReceiverFuture(oneshot::Receiver<()>);
impl Future for StupidReceiverFuture {
type Output = Result<(), oneshot::RecvError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let poll_result = Future::poll(Pin::new(&mut self.0), cx);
self.0.try_recv().expect_err("Should never be a message");
poll_result
}
}
let (sender, receiver) = oneshot::channel();
let t = tokio::spawn(async {
tokio::time::sleep(Duration::from_millis(20)).await;
mem::drop(sender);
});
StupidReceiverFuture(receiver).await.unwrap_err();
t.await.unwrap();
}
#[tokio::test]
async fn poll_receiver_then_drop_it() {
let (sender, receiver) = oneshot::channel::<()>();
// This will poll the receiver and then give up after 100 ms.
tokio::time::timeout(Duration::from_millis(100), receiver)
.await
.unwrap_err();
// Make sure the receiver has been dropped by the runtime.
assert!(sender.send(()).is_err());
}

View File

@ -1,65 +0,0 @@
#![cfg(feature = "async")]
use core::{future, mem, pin, task};
#[cfg(loom)]
pub use loom::sync::{Arc, Mutex};
#[cfg(not(loom))]
pub use std::sync::{Arc, Mutex};
mod helpers;
use helpers::maybe_loom_model;
#[test]
fn multiple_receiver_polls_keeps_only_latest_waker() {
#[derive(Default)]
struct MockWaker {
cloned: usize,
dropped: usize,
}
fn clone_mock_waker(waker: *const ()) -> task::RawWaker {
let mock_waker = unsafe { Arc::from_raw(waker as *const Mutex<MockWaker>) };
mock_waker.lock().unwrap().cloned += 1;
let new_waker =
task::RawWaker::new(Arc::into_raw(mock_waker.clone()) as *const (), &VTABLE);
mem::forget(mock_waker);
new_waker
}
fn drop_mock_waker(waker: *const ()) {
let mock_waker = unsafe { Arc::from_raw(waker as *const Mutex<MockWaker>) };
mock_waker.lock().unwrap().dropped += 1;
}
const VTABLE: task::RawWakerVTable =
task::RawWakerVTable::new(clone_mock_waker, |_| (), |_| (), drop_mock_waker);
maybe_loom_model(|| {
let mock_waker1 = Arc::new(Mutex::new(MockWaker::default()));
let raw_waker1 =
task::RawWaker::new(Arc::into_raw(mock_waker1.clone()) as *const (), &VTABLE);
let waker1 = unsafe { task::Waker::from_raw(raw_waker1) };
let mut context1 = task::Context::from_waker(&waker1);
let (_sender, mut receiver) = oneshot::channel::<()>();
let poll_result = future::Future::poll(pin::Pin::new(&mut receiver), &mut context1);
assert_eq!(poll_result, task::Poll::Pending);
assert_eq!(mock_waker1.lock().unwrap().cloned, 1);
assert_eq!(mock_waker1.lock().unwrap().dropped, 0);
let mock_waker2 = Arc::new(Mutex::new(MockWaker::default()));
let raw_waker2 =
task::RawWaker::new(Arc::into_raw(mock_waker2.clone()) as *const (), &VTABLE);
let waker2 = unsafe { task::Waker::from_raw(raw_waker2) };
let mut context2 = task::Context::from_waker(&waker2);
let poll_result = future::Future::poll(pin::Pin::new(&mut receiver), &mut context2);
assert_eq!(poll_result, task::Poll::Pending);
assert_eq!(mock_waker2.lock().unwrap().cloned, 1);
assert_eq!(mock_waker2.lock().unwrap().dropped, 0);
assert_eq!(mock_waker1.lock().unwrap().cloned, 1);
assert_eq!(mock_waker1.lock().unwrap().dropped, 1);
});
}

View File

@ -1,63 +0,0 @@
#![allow(dead_code)]
extern crate alloc;
#[cfg(not(loom))]
use alloc::sync::Arc;
#[cfg(not(loom))]
use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
#[cfg(loom)]
use loom::sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc,
};
#[cfg(loom)]
pub mod waker;
pub fn maybe_loom_model(test: impl Fn() + Sync + Send + 'static) {
#[cfg(loom)]
loom::model(test);
#[cfg(not(loom))]
test();
}
pub struct DropCounter<T> {
drop_count: Arc<AtomicUsize>,
value: Option<T>,
}
pub struct DropCounterHandle(Arc<AtomicUsize>);
impl<T> DropCounter<T> {
pub fn new(value: T) -> (Self, DropCounterHandle) {
let drop_count = Arc::new(AtomicUsize::new(0));
(
Self {
drop_count: drop_count.clone(),
value: Some(value),
},
DropCounterHandle(drop_count),
)
}
pub fn value(&self) -> &T {
self.value.as_ref().unwrap()
}
pub fn into_value(mut self) -> T {
self.value.take().unwrap()
}
}
impl DropCounterHandle {
pub fn count(&self) -> usize {
self.0.load(SeqCst)
}
}
impl<T> Drop for DropCounter<T> {
fn drop(&mut self) {
self.drop_count.fetch_add(1, SeqCst);
}
}

View File

@ -1,64 +0,0 @@
//! Creates a Waker that can be observed from tests.
use std::mem::forget;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::task::{RawWaker, RawWakerVTable, Waker};
#[derive(Default)]
pub struct WakerHandle {
clone_count: AtomicU32,
drop_count: AtomicU32,
wake_count: AtomicU32,
}
impl WakerHandle {
pub fn clone_count(&self) -> u32 {
self.clone_count.load(Ordering::Relaxed)
}
pub fn drop_count(&self) -> u32 {
self.drop_count.load(Ordering::Relaxed)
}
pub fn wake_count(&self) -> u32 {
self.wake_count.load(Ordering::Relaxed)
}
}
pub fn waker() -> (Waker, Arc<WakerHandle>) {
let waker_handle = Arc::new(WakerHandle::default());
let waker_handle_ptr = Arc::into_raw(waker_handle.clone());
let raw_waker = RawWaker::new(waker_handle_ptr as *const _, waker_vtable());
(unsafe { Waker::from_raw(raw_waker) }, waker_handle)
}
pub(super) fn waker_vtable() -> &'static RawWakerVTable {
&RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw)
}
unsafe fn clone_raw(data: *const ()) -> RawWaker {
let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
handle.clone_count.fetch_add(1, Ordering::Relaxed);
forget(handle.clone());
forget(handle);
RawWaker::new(data, waker_vtable())
}
unsafe fn wake_raw(data: *const ()) {
let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
handle.wake_count.fetch_add(1, Ordering::Relaxed);
handle.drop_count.fetch_add(1, Ordering::Relaxed);
}
unsafe fn wake_by_ref_raw(data: *const ()) {
let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
handle.wake_count.fetch_add(1, Ordering::Relaxed);
forget(handle)
}
unsafe fn drop_raw(data: *const ()) {
let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
handle.drop_count.fetch_add(1, Ordering::Relaxed);
drop(handle)
}

View File

@ -1,223 +0,0 @@
#![cfg(loom)]
use oneshot::TryRecvError;
use loom::hint;
use loom::thread;
#[cfg(feature = "async")]
use std::future::Future;
#[cfg(feature = "async")]
use std::pin::Pin;
#[cfg(feature = "async")]
use std::task::{self, Poll};
#[cfg(feature = "std")]
use std::time::Duration;
mod helpers;
#[test]
fn try_recv() {
loom::model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || loop {
match receiver.try_recv() {
Ok(msg) => break msg,
Err(TryRecvError::Empty) => hint::spin_loop(),
Err(TryRecvError::Disconnected) => panic!("Should not be disconnected"),
}
});
assert!(sender.send(19).is_ok());
assert_eq!(t.join().unwrap(), 19);
})
}
#[cfg(feature = "std")]
#[test]
fn send_recv_different_threads() {
loom::model(|| {
let (sender, receiver) = oneshot::channel();
let t2 = thread::spawn(move || {
assert_eq!(receiver.recv_timeout(Duration::from_millis(1)), Ok(9));
});
let t1 = thread::spawn(move || {
sender.send(9u128).unwrap();
});
t1.join().unwrap();
t2.join().unwrap();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_drop_sender_different_threads() {
loom::model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t2 = thread::spawn(move || {
assert!(receiver.recv_timeout(Duration::from_millis(0)).is_err());
});
let t1 = thread::spawn(move || {
drop(sender);
});
t1.join().unwrap();
t2.join().unwrap();
})
}
#[cfg(feature = "async")]
#[test]
fn async_recv() {
loom::model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t1 = thread::spawn(move || {
sender.send(987).unwrap();
});
assert_eq!(loom::future::block_on(receiver), Ok(987));
t1.join().unwrap();
})
}
#[cfg(feature = "async")]
#[test]
fn send_then_poll() {
loom::model(|| {
let (sender, mut receiver) = oneshot::channel::<u128>();
sender.send(1234).unwrap();
let (waker, waker_handle) = helpers::waker::waker();
let mut context = task::Context::from_waker(&waker);
assert_eq!(
Pin::new(&mut receiver).poll(&mut context),
Poll::Ready(Ok(1234))
);
assert_eq!(waker_handle.clone_count(), 0);
assert_eq!(waker_handle.drop_count(), 0);
assert_eq!(waker_handle.wake_count(), 0);
})
}
#[cfg(feature = "async")]
#[test]
fn poll_then_send() {
loom::model(|| {
let (sender, mut receiver) = oneshot::channel::<u128>();
let (waker, waker_handle) = helpers::waker::waker();
let mut context = task::Context::from_waker(&waker);
assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 0);
assert_eq!(waker_handle.wake_count(), 0);
sender.send(1234).unwrap();
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 1);
assert_eq!(waker_handle.wake_count(), 1);
assert_eq!(
Pin::new(&mut receiver).poll(&mut context),
Poll::Ready(Ok(1234))
);
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 1);
assert_eq!(waker_handle.wake_count(), 1);
})
}
#[cfg(feature = "async")]
#[test]
fn poll_with_different_wakers() {
loom::model(|| {
let (sender, mut receiver) = oneshot::channel::<u128>();
let (waker1, waker_handle1) = helpers::waker::waker();
let mut context1 = task::Context::from_waker(&waker1);
assert_eq!(Pin::new(&mut receiver).poll(&mut context1), Poll::Pending);
assert_eq!(waker_handle1.clone_count(), 1);
assert_eq!(waker_handle1.drop_count(), 0);
assert_eq!(waker_handle1.wake_count(), 0);
let (waker2, waker_handle2) = helpers::waker::waker();
let mut context2 = task::Context::from_waker(&waker2);
assert_eq!(Pin::new(&mut receiver).poll(&mut context2), Poll::Pending);
assert_eq!(waker_handle1.clone_count(), 1);
assert_eq!(waker_handle1.drop_count(), 1);
assert_eq!(waker_handle1.wake_count(), 0);
assert_eq!(waker_handle2.clone_count(), 1);
assert_eq!(waker_handle2.drop_count(), 0);
assert_eq!(waker_handle2.wake_count(), 0);
// Sending should cause the waker from the latest poll to be woken up
sender.send(1234).unwrap();
assert_eq!(waker_handle1.clone_count(), 1);
assert_eq!(waker_handle1.drop_count(), 1);
assert_eq!(waker_handle1.wake_count(), 0);
assert_eq!(waker_handle2.clone_count(), 1);
assert_eq!(waker_handle2.drop_count(), 1);
assert_eq!(waker_handle2.wake_count(), 1);
})
}
#[cfg(feature = "async")]
#[test]
fn poll_then_try_recv() {
loom::model(|| {
let (_sender, mut receiver) = oneshot::channel::<u128>();
let (waker, waker_handle) = helpers::waker::waker();
let mut context = task::Context::from_waker(&waker);
assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 0);
assert_eq!(waker_handle.wake_count(), 0);
assert_eq!(receiver.try_recv(), Err(TryRecvError::Empty));
assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
assert_eq!(waker_handle.clone_count(), 2);
assert_eq!(waker_handle.drop_count(), 1);
assert_eq!(waker_handle.wake_count(), 0);
})
}
#[cfg(feature = "async")]
#[test]
fn poll_then_try_recv_while_sending() {
loom::model(|| {
let (sender, mut receiver) = oneshot::channel::<u128>();
let (waker, waker_handle) = helpers::waker::waker();
let mut context = task::Context::from_waker(&waker);
assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 0);
assert_eq!(waker_handle.wake_count(), 0);
let t = thread::spawn(move || {
sender.send(1234).unwrap();
});
let msg = loop {
match receiver.try_recv() {
Ok(msg) => break msg,
Err(TryRecvError::Empty) => hint::spin_loop(),
Err(TryRecvError::Disconnected) => panic!("Should not be disconnected"),
}
};
assert_eq!(msg, 1234);
assert_eq!(waker_handle.clone_count(), 1);
assert_eq!(waker_handle.drop_count(), 1);
assert_eq!(waker_handle.wake_count(), 1);
t.join().unwrap();
})
}

View File

@ -1,46 +0,0 @@
#![cfg(not(loom))]
use oneshot::{channel, Receiver, Sender};
#[test]
fn test_raw_sender() {
let (sender, receiver) = channel::<u32>();
let raw = sender.into_raw();
let recreated = unsafe { Sender::<u32>::from_raw(raw) };
recreated
.send(100)
.unwrap_or_else(|e| panic!("error sending after into_raw/from_raw roundtrip: {e}"));
assert_eq!(receiver.try_recv(), Ok(100))
}
#[test]
fn test_raw_receiver() {
let (sender, receiver) = channel::<u32>();
let raw = receiver.into_raw();
sender.send(100).unwrap();
let recreated = unsafe { Receiver::<u32>::from_raw(raw) };
assert_eq!(
recreated
.try_recv()
.unwrap_or_else(|e| panic!("error receiving after into_raw/from_raw roundtrip: {e}")),
100
)
}
#[test]
fn test_raw_sender_and_receiver() {
let (sender, receiver) = channel::<u32>();
let raw_receiver = receiver.into_raw();
let raw_sender = sender.into_raw();
let recreated_sender = unsafe { Sender::<u32>::from_raw(raw_sender) };
recreated_sender.send(100).unwrap();
let recreated_receiver = unsafe { Receiver::<u32>::from_raw(raw_receiver) };
assert_eq!(
recreated_receiver
.try_recv()
.unwrap_or_else(|e| panic!("error receiving after into_raw/from_raw roundtrip: {e}")),
100
)
}

View File

@ -1,343 +0,0 @@
use core::mem;
use oneshot::TryRecvError;
#[cfg(feature = "std")]
use oneshot::{RecvError, RecvTimeoutError};
#[cfg(feature = "std")]
use std::time::{Duration, Instant};
#[cfg(feature = "std")]
mod thread {
#[cfg(loom)]
pub use loom::thread::spawn;
#[cfg(not(loom))]
pub use std::thread::{sleep, spawn};
#[cfg(loom)]
pub fn sleep(_timeout: core::time::Duration) {
loom::thread::yield_now()
}
}
mod helpers;
use helpers::{maybe_loom_model, DropCounter};
#[test]
fn send_before_try_recv() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
assert_eq!(receiver.try_recv(), Ok(19i128));
assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected));
#[cfg(feature = "std")]
{
assert_eq!(receiver.recv_ref(), Err(RecvError));
assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
}
})
}
#[cfg(feature = "std")]
#[test]
fn send_before_recv() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<()>();
assert!(sender.send(()).is_ok());
assert_eq!(receiver.recv(), Ok(()));
});
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u8>();
assert!(sender.send(19).is_ok());
assert_eq!(receiver.recv(), Ok(19));
});
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u64>();
assert!(sender.send(21).is_ok());
assert_eq!(receiver.recv(), Ok(21));
});
// FIXME: This test does not work with loom. There is something that happens after the
// channel object becomes larger than ~500 bytes and that makes an atomic read from the state
// result in "signal: 10, SIGBUS: access to undefined memory"
#[cfg(not(loom))]
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<[u8; 4096]>();
assert!(sender.send([0b10101010; 4096]).is_ok());
assert!(receiver.recv().unwrap()[..] == [0b10101010; 4096][..]);
});
}
#[cfg(feature = "std")]
#[test]
fn send_before_recv_ref() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
assert_eq!(receiver.recv_ref(), Ok(19i128));
assert_eq!(receiver.recv_ref(), Err(RecvError));
assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected));
assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
})
}
#[cfg(feature = "std")]
#[test]
fn send_before_recv_timeout() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
let start = Instant::now();
let timeout = Duration::from_secs(1);
assert_eq!(receiver.recv_timeout(timeout), Ok(19i128));
assert!(start.elapsed() < Duration::from_millis(100));
assert!(receiver.recv_timeout(timeout).is_err());
assert!(receiver.try_recv().is_err());
assert!(receiver.recv().is_err());
})
}
#[test]
fn send_then_drop_receiver() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
assert!(sender.send(19i128).is_ok());
mem::drop(receiver);
})
}
#[test]
fn send_with_dropped_receiver() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
mem::drop(receiver);
let send_error = sender.send(5u128).unwrap_err();
assert_eq!(*send_error.as_inner(), 5);
assert_eq!(send_error.into_inner(), 5);
})
}
#[test]
fn try_recv_with_dropped_sender() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
receiver.try_recv().unwrap_err();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_with_dropped_sender() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
mem::drop(sender);
receiver.recv().unwrap_err();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_before_send() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
sender.send(9u128).unwrap();
});
assert_eq!(receiver.recv(), Ok(9));
t.join().unwrap();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_timeout_before_send() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(2));
sender.send(9u128).unwrap();
});
assert_eq!(receiver.recv_timeout(Duration::from_secs(1)), Ok(9));
t.join().unwrap();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_before_send_then_drop_sender() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
mem::drop(sender);
});
assert!(receiver.recv().is_err());
t.join().unwrap();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_timeout_before_send_then_drop_sender() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
mem::drop(sender);
});
assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
t.join().unwrap();
})
}
#[test]
fn try_recv() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
assert_eq!(receiver.try_recv(), Err(TryRecvError::Empty));
mem::drop(sender)
})
}
#[cfg(feature = "std")]
#[test]
fn try_recv_then_drop_receiver() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel::<u128>();
let t1 = thread::spawn(move || {
let _ = sender.send(42);
});
let t2 = thread::spawn(move || {
assert!(matches!(
receiver.try_recv(),
Ok(42) | Err(TryRecvError::Empty)
));
mem::drop(receiver);
});
t1.join().unwrap();
t2.join().unwrap();
})
}
#[cfg(feature = "std")]
#[test]
fn recv_deadline_and_timeout_no_time() {
maybe_loom_model(|| {
let (_sender, receiver) = oneshot::channel::<u128>();
let start = Instant::now();
assert_eq!(
receiver.recv_deadline(start),
Err(RecvTimeoutError::Timeout)
);
assert!(start.elapsed() < Duration::from_millis(200));
let start = Instant::now();
assert_eq!(
receiver.recv_timeout(Duration::from_millis(0)),
Err(RecvTimeoutError::Timeout)
);
assert!(start.elapsed() < Duration::from_millis(200));
})
}
// This test doesn't give meaningful results when run with oneshot_test_delay and loom
#[cfg(all(feature = "std", not(all(oneshot_test_delay, loom))))]
#[test]
fn recv_deadline_time_should_elapse() {
maybe_loom_model(|| {
let (_sender, receiver) = oneshot::channel::<u128>();
let start = Instant::now();
#[cfg(not(loom))]
let timeout = Duration::from_millis(100);
#[cfg(loom)]
let timeout = Duration::from_millis(1);
assert_eq!(
receiver.recv_deadline(start + timeout),
Err(RecvTimeoutError::Timeout)
);
assert!(start.elapsed() > timeout);
assert!(start.elapsed() < timeout * 3);
})
}
#[cfg(all(feature = "std", not(all(oneshot_test_delay, loom))))]
#[test]
fn recv_timeout_time_should_elapse() {
maybe_loom_model(|| {
let (_sender, receiver) = oneshot::channel::<u128>();
let start = Instant::now();
#[cfg(not(loom))]
let timeout = Duration::from_millis(100);
#[cfg(loom)]
let timeout = Duration::from_millis(1);
assert_eq!(
receiver.recv_timeout(timeout),
Err(RecvTimeoutError::Timeout)
);
assert!(start.elapsed() > timeout);
assert!(start.elapsed() < timeout * 3);
})
}
#[cfg(not(loom))]
#[test]
fn non_send_type_can_be_used_on_same_thread() {
use std::ptr;
#[derive(Debug, Eq, PartialEq)]
struct NotSend(*mut ());
let (sender, receiver) = oneshot::channel();
sender.send(NotSend(ptr::null_mut())).unwrap();
let reply = receiver.try_recv().unwrap();
assert_eq!(reply, NotSend(ptr::null_mut()));
}
#[test]
fn message_in_channel_dropped_on_receiver_drop() {
maybe_loom_model(|| {
let (sender, receiver) = oneshot::channel();
let (message, counter) = DropCounter::new(());
assert_eq!(counter.count(), 0);
sender.send(message).unwrap();
assert_eq!(counter.count(), 0);
mem::drop(receiver);
assert_eq!(counter.count(), 1);
})
}
#[test]
fn send_error_drops_message_correctly() {
maybe_loom_model(|| {
let (sender, _) = oneshot::channel();
let (message, counter) = DropCounter::new(());
let send_error = sender.send(message).unwrap_err();
assert_eq!(counter.count(), 0);
mem::drop(send_error);
assert_eq!(counter.count(), 1);
});
}
#[test]
fn send_error_drops_message_correctly_on_into_inner() {
maybe_loom_model(|| {
let (sender, _) = oneshot::channel();
let (message, counter) = DropCounter::new(());
let send_error = sender.send(message).unwrap_err();
assert_eq!(counter.count(), 0);
let message = send_error.into_inner();
assert_eq!(counter.count(), 0);
mem::drop(message);
assert_eq!(counter.count(), 1);
});
}

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"7f8e0b5d66e9c5621c5fb57d4b16b801a24061e37ee337e06f8a004e6895a8dc","LICENSE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","src/lib.rs":"ef4f143f99e9ef4f17dab02e1b80ed3ac484ae58e0bb64164920720e0ca9425f","src/shared.rs":"e0c98ea10f78491f567e2a18bcb41b3f0ae01ce587d8b3a16ce0dc1097919109","src/tables.rs":"1821b437dfb31164ce8180af3937ca42270f1edf963a2d2e41cbaaf999553c94"},"package":"3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"}

View File

@ -1,32 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.56"
name = "unicode-linebreak"
version = "0.1.5"
authors = ["Axel Forsman <axelsfor@gmail.com>"]
include = [
"src/**/*",
"LICENSE",
]
description = "Implementation of the Unicode Line Breaking Algorithm"
homepage = "https://github.com/axelf4/unicode-linebreak"
readme = "README.md"
keywords = [
"unicode",
"text",
"layout",
]
categories = ["internationalization"]
license = "Apache-2.0"
repository = "https://github.com/axelf4/unicode-linebreak"

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,160 +0,0 @@
//! Implementation of the Line Breaking Algorithm described in [Unicode Standard Annex #14][UAX14].
//!
//! Given an input text, locates "line break opportunities", or positions appropriate for wrapping
//! lines when displaying text.
//!
//! # Example
//!
//! ```
//! use unicode_linebreak::{linebreaks, BreakOpportunity::{Mandatory, Allowed}};
//!
//! let text = "a b \nc";
//! assert!(linebreaks(text).eq([
//! (2, Allowed), // May break after first space
//! (5, Mandatory), // Must break after line feed
//! (6, Mandatory) // Must break at end of text, so that there always is at least one LB
//! ]));
//! ```
//!
//! [UAX14]: https://www.unicode.org/reports/tr14/
#![no_std]
#![deny(missing_docs, missing_debug_implementations)]
use core::iter::once;
/// The [Unicode version](https://www.unicode.org/versions/) conformed to.
pub const UNICODE_VERSION: (u8, u8, u8) = (15, 0, 0);
include!("shared.rs");
include!("tables.rs");
/// Returns the line break property of the specified code point.
///
/// # Examples
///
/// ```
/// use unicode_linebreak::{BreakClass, break_property};
/// assert_eq!(break_property(0x2CF3), BreakClass::Alphabetic);
/// ```
#[inline(always)]
pub fn break_property(codepoint: u32) -> BreakClass {
const BMP_INDEX_LENGTH: u32 = BMP_LIMIT >> BMP_SHIFT;
const OMITTED_BMP_INDEX_1_LENGTH: u32 = BMP_LIMIT >> SHIFT_1;
let data_pos = if codepoint < BMP_LIMIT {
let i = codepoint >> BMP_SHIFT;
BREAK_PROP_TRIE_INDEX[i as usize] + (codepoint & (BMP_DATA_BLOCK_LENGTH - 1)) as u16
} else if codepoint < BREAK_PROP_TRIE_HIGH_START {
let i1 = codepoint >> SHIFT_1;
let i2 = BREAK_PROP_TRIE_INDEX
[(i1 + BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH) as usize]
+ ((codepoint >> SHIFT_2) & (INDEX_2_BLOCK_LENGTH - 1)) as u16;
let i3_block = BREAK_PROP_TRIE_INDEX[i2 as usize];
let i3_pos = ((codepoint >> SHIFT_3) & (INDEX_3_BLOCK_LENGTH - 1)) as u16;
debug_assert!(i3_block & 0x8000 == 0, "18-bit indices are unexpected");
let data_block = BREAK_PROP_TRIE_INDEX[(i3_block + i3_pos) as usize];
data_block + (codepoint & (SMALL_DATA_BLOCK_LENGTH - 1)) as u16
} else {
return XX;
};
BREAK_PROP_TRIE_DATA[data_pos as usize]
}
/// Break opportunity type.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BreakOpportunity {
/// A line must break at this spot.
Mandatory,
/// A line is allowed to end at this spot.
Allowed,
}
/// Returns an iterator over line break opportunities in the specified string.
///
/// Break opportunities are given as tuples of the byte index of the character succeeding the break
/// and the type.
///
/// Uses the default Line Breaking Algorithm with the tailoring that Complex-Context Dependent
/// (SA) characters get resolved to Ordinary Alphabetic and Symbol Characters (AL) regardless of
/// General_Category.
///
/// # Examples
///
/// ```
/// use unicode_linebreak::{linebreaks, BreakOpportunity::{Mandatory, Allowed}};
/// assert!(linebreaks("Hello world!").eq(vec![(6, Allowed), (12, Mandatory)]));
/// ```
pub fn linebreaks(s: &str) -> impl Iterator<Item = (usize, BreakOpportunity)> + Clone + '_ {
use BreakOpportunity::{Allowed, Mandatory};
s.char_indices()
.map(|(i, c)| (i, break_property(c as u32) as u8))
.chain(once((s.len(), eot)))
.scan((sot, false), |state, (i, cls)| {
// ZWJ is handled outside the table to reduce its size
let val = PAIR_TABLE[state.0 as usize][cls as usize];
let is_mandatory = val & MANDATORY_BREAK_BIT != 0;
let is_break = val & ALLOWED_BREAK_BIT != 0 && (!state.1 || is_mandatory);
*state = (
val & !(ALLOWED_BREAK_BIT | MANDATORY_BREAK_BIT),
cls == BreakClass::ZeroWidthJoiner as u8,
);
Some((i, is_break, is_mandatory))
})
.filter_map(|(i, is_break, is_mandatory)| {
if is_break {
Some((i, if is_mandatory { Mandatory } else { Allowed }))
} else {
None
}
})
}
/// Divides the string at the last index where further breaks do not depend on prior context.
///
/// The trivial index at `eot` is excluded.
///
/// A common optimization is to determine only the nearest line break opportunity before the first
/// character that would cause the line to become overfull, requiring backward traversal, of which
/// there are two approaches:
///
/// * Cache breaks from forward traversals
/// * Step backward and with `split_at_safe` find a pos to safely search forward from, repeatedly
///
/// # Examples
///
/// ```
/// use unicode_linebreak::{linebreaks, split_at_safe};
/// let s = "Not allowed to break within em dashes: — —";
/// let (prev, safe) = split_at_safe(s);
/// let n = prev.len();
/// assert!(linebreaks(safe).eq(linebreaks(s).filter_map(|(i, x)| i.checked_sub(n).map(|i| (i, x)))));
/// ```
pub fn split_at_safe(s: &str) -> (&str, &str) {
let mut chars = s.char_indices().rev().scan(None, |state, (i, c)| {
let cls = break_property(c as u32);
let is_safe_pair = state
.replace(cls)
.map_or(false, |prev| is_safe_pair(cls, prev)); // Reversed since iterating backwards
Some((i, is_safe_pair))
});
chars.find(|&(_, is_safe_pair)| is_safe_pair);
// Include preceding char for `linebreaks` to pick up break before match (disallowed after sot)
s.split_at(chars.next().map_or(0, |(i, _)| i))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(break_property(0xA), BreakClass::LineFeed);
assert_eq!(break_property(0xDB80), BreakClass::Surrogate);
assert_eq!(break_property(0xe01ef), BreakClass::CombiningMark);
assert_eq!(break_property(0x10ffff), BreakClass::Unknown);
}
}

View File

@ -1,134 +0,0 @@
/// Unicode line breaking class.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum BreakClass {
// Non-tailorable
/// Cause a line break (after)
Mandatory,
/// Cause a line break (after), except between CR and LF
CarriageReturn,
/// Cause a line break (after)
LineFeed,
/// Prohibit a line break between the character and the preceding character
CombiningMark,
/// Cause a line break (after)
NextLine,
/// Do not occur in well-formed text
Surrogate,
/// Prohibit line breaks before and after
WordJoiner,
/// Provide a break opportunity
ZeroWidthSpace,
/// Prohibit line breaks before and after
NonBreakingGlue,
/// Enable indirect line breaks
Space,
/// Prohibit line breaks within joiner sequences
ZeroWidthJoiner,
// Break opportunities
/// Provide a line break opportunity before and after the character
BeforeAndAfter,
/// Generally provide a line break opportunity after the character
After,
/// Generally provide a line break opportunity before the character
Before,
/// Provide a line break opportunity after the character, except in numeric context
Hyphen,
/// Provide a line break opportunity contingent on additional information
Contingent,
// Characters prohibiting certain breaks
/// Prohibit line breaks before
ClosePunctuation,
/// Prohibit line breaks before
CloseParenthesis,
/// Prohibit line breaks before
Exclamation,
/// Allow only indirect line breaks between pairs
Inseparable,
/// Allow only indirect line breaks before
NonStarter,
/// Prohibit line breaks after
OpenPunctuation,
/// Act like they are both opening and closing
Quotation,
// Numeric context
/// Prevent breaks after any and before numeric
InfixSeparator,
/// Form numeric expressions for line breaking purposes
Numeric,
/// Do not break following a numeric expression
Postfix,
/// Do not break in front of a numeric expression
Prefix,
/// Prevent a break before, and allow a break after
Symbol,
// Other characters
/// Act like AL when the resolved EAW is N; otherwise, act as ID
Ambiguous,
/// Are alphabetic characters or symbols that are used with alphabetic characters
Alphabetic,
/// Treat as NS or ID for strict or normal breaking.
ConditionalJapaneseStarter,
/// Do not break from following Emoji Modifier
EmojiBase,
/// Do not break from preceding Emoji Base
EmojiModifier,
/// Form Korean syllable blocks
HangulLvSyllable,
/// Form Korean syllable blocks
HangulLvtSyllable,
/// Do not break around a following hyphen; otherwise act as Alphabetic
HebrewLetter,
/// Break before or after, except in some numeric context
Ideographic,
/// Form Korean syllable blocks
HangulLJamo,
/// Form Korean syllable blocks
HangulVJamo,
/// Form Korean syllable blocks
HangulTJamo,
/// Keep pairs together. For pairs, break before and after other classes
RegionalIndicator,
/// Provide a line break opportunity contingent on additional, language-specific context analysis
ComplexContext,
/// Have as yet unknown line breaking behavior or unassigned code positions
Unknown,
}
use BreakClass::{
After as BA, Alphabetic as AL, Ambiguous as AI, Before as BB, BeforeAndAfter as B2,
CarriageReturn as CR, CloseParenthesis as CP, ClosePunctuation as CL, CombiningMark as CM,
ComplexContext as SA, ConditionalJapaneseStarter as CJ, Contingent as CB, EmojiBase as EB,
EmojiModifier as EM, Exclamation as EX, HangulLJamo as JL, HangulLvSyllable as H2,
HangulLvtSyllable as H3, HangulTJamo as JT, HangulVJamo as JV, HebrewLetter as HL,
Hyphen as HY, Ideographic as ID, InfixSeparator as IS, Inseparable as IN, LineFeed as LF,
Mandatory as BK, NextLine as NL, NonBreakingGlue as GL, NonStarter as NS, Numeric as NU,
OpenPunctuation as OP, Postfix as PO, Prefix as PR, Quotation as QU, RegionalIndicator as RI,
Space as SP, Surrogate as SG, Symbol as SY, Unknown as XX, WordJoiner as WJ,
ZeroWidthJoiner as ZWJ, ZeroWidthSpace as ZW,
};
/// Ceiling for code points in the Basic Multilingual Place (BMP).
const BMP_LIMIT: u32 = 0x10000;
/// Shift size for getting index-3 table offset.
const SHIFT_3: u32 = 4;
/// Shift size for getting index-2 table offset.
const SHIFT_2: u32 = 5 + SHIFT_3;
/// Shift size for getting index-1 table offset.
const SHIFT_1: u32 = 5 + SHIFT_2;
/// Shift size for getting BMP block start.
const BMP_SHIFT: u32 = 6;
const INDEX_2_BLOCK_LENGTH: u32 = 1 << (SHIFT_1 - SHIFT_2);
const INDEX_3_BLOCK_LENGTH: u32 = 1 << (SHIFT_2 - SHIFT_3);
const SMALL_DATA_BLOCK_LENGTH: u32 = 1 << SHIFT_3;
const BMP_DATA_BLOCK_LENGTH: u32 = 1 << BMP_SHIFT;
const ALLOWED_BREAK_BIT: u8 = 0x80;
const MANDATORY_BREAK_BIT: u8 = 0x40;
#[allow(non_upper_case_globals)]
const eot: u8 = 43;
#[allow(non_upper_case_globals)]
const sot: u8 = 44;

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"eb974d356d4da93a076434ff428c448f70e036a724bd9a0a7eae6b9ddff2346e","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","release.toml":"1aa1b131d4cc93b5eba8758a4401c70bc0d7fe5861e2ec147e9259fe7c0da472","src/cli.rs":"5c0b9bb93665f2f49f7e90335e65206887e26e96f2a533eb1203be27c9380c84","src/lib.rs":"422503d7cbac1360852287b1810c99663669625b9abf080a5fec22058bb73d8c","tests/ui/proc_macro_arc.rs":"fedc429603753e8ef953642a7295323ccb3f76fd3ae1ab181ad90c5eb88212bb","tests/ui/proc_macro_arc.stderr":"a24af227b907328c9cac6317ec9f43dbc45d7f7c77c603e5d72db7fa050e8b01","tests/ui/version_mismatch.rs":"16ea359e5853517ee0d0704c015ae8c825533109fbefd715130d0f4a51f15898","tests/ui/version_mismatch.stderr":"21dcb836253312ba8e3a0502cce6ff279818aaaadcea9628a41b196e0c8c94b6"},"package":"a5566fae48a5cb017005bf9cd622af5236b2a203a13fb548afde3506d3c68277"}
{"files":{"Cargo.toml":"7c45cfa5f95f1db4ec3cb139f8f5166281f7893b21a4313ee4a4f4070bef1816","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","release.toml":"1aa1b131d4cc93b5eba8758a4401c70bc0d7fe5861e2ec147e9259fe7c0da472","src/cli.rs":"5c0b9bb93665f2f49f7e90335e65206887e26e96f2a533eb1203be27c9380c84","src/lib.rs":"422503d7cbac1360852287b1810c99663669625b9abf080a5fec22058bb73d8c","tests/ui/proc_macro_arc.rs":"fedc429603753e8ef953642a7295323ccb3f76fd3ae1ab181ad90c5eb88212bb","tests/ui/proc_macro_arc.stderr":"a24af227b907328c9cac6317ec9f43dbc45d7f7c77c603e5d72db7fa050e8b01","tests/ui/version_mismatch.rs":"16ea359e5853517ee0d0704c015ae8c825533109fbefd715130d0f4a51f15898","tests/ui/version_mismatch.stderr":"21dcb836253312ba8e3a0502cce6ff279818aaaadcea9628a41b196e0c8c94b6"},"package":"cb3a4c447c50fcda7bc5604a8588b7e1f37ffbfd8838a1516a290398efa7c6f0"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -42,18 +42,18 @@ features = [
optional = true
[dependencies.uniffi_bindgen]
version = "=0.27.1"
version = "=0.27.3"
optional = true
[dependencies.uniffi_build]
version = "=0.27.1"
version = "=0.27.3"
optional = true
[dependencies.uniffi_core]
version = "=0.27.1"
version = "=0.27.3"
[dependencies.uniffi_macros]
version = "=0.27.1"
version = "=0.27.3"
[dev-dependencies.trybuild]
version = "1"
@ -69,4 +69,8 @@ cli = [
"dep:camino",
]
default = []
scaffolding-ffi-buffer-fns = [
"uniffi_core/scaffolding-ffi-buffer-fns",
"uniffi_macros/scaffolding-ffi-buffer-fns",
]
tokio = ["uniffi_core/tokio"]

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_bindgen"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (codegen and cli tooling)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -72,15 +72,17 @@ features = ["derive"]
[dependencies.textwrap]
version = "0.16"
features = ["smawk"]
default-features = false
[dependencies.toml]
version = "0.5"
[dependencies.uniffi_meta]
version = "=0.27.1"
version = "=0.27.3"
[dependencies.uniffi_testing]
version = "=0.27.1"
version = "=0.27.3"
[dependencies.uniffi_udl]
version = "=0.27.1"
version = "=0.27.3"

View File

@ -218,6 +218,12 @@ impl FfiFunction {
&self.name
}
/// Name of the FFI buffer version of this function that's generated when the
/// `scaffolding-ffi-buffer-fns` feature is enabled.
pub fn ffi_buffer_fn_name(&self) -> String {
uniffi_meta::ffi_buffer_symbol_name(&self.name)
}
pub fn is_async(&self) -> bool {
self.is_async
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"8fcf43ff5e6c1281a1ee5f9ed796b0f8115bd39ca9ce5b2d0c32e88d9eb2038f","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/lib.rs":"47ff3d1a18456164414af1c20cd5df129401e5257cc15552ecc39afed8970707"},"package":"45cba427aeb7b3a8b54830c4c915079a7a3c62608dd03dddba1d867a8a023eb4"}
{"files":{"Cargo.toml":"a79595948b5661477a6fd0506fa0abae05d83d3f0a4c31f16c20c143f6236b42","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/lib.rs":"47ff3d1a18456164414af1c20cd5df129401e5257cc15552ecc39afed8970707"},"package":"1c59b65d59685ff3a10569287c6419f76487b4052ac52d5a0df38b2253d7f440"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_build"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (build script helpers)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -32,7 +32,7 @@ version = "1"
version = "1.0.8"
[dependencies.uniffi_bindgen]
version = "=0.27.1"
version = "=0.27.3"
default-features = false
[features]

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"da89504b9007c2a1ea0e498a2e8ec6baeb0ff7391363cd9007e383247637792c","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/lib.rs":"44d2e2c595b14d33d16c71dfe4ef42ad0b9e010a878ee2ec49c2e929d60275ba"},"package":"ae7e5a6c33b1dec3f255f57ec0b6af0f0b2bb3021868be1d5eec7a38e2905ebc"}
{"files":{"Cargo.toml":"efec1e1624f962fd264a0003ac8d40311c4af50f69d7ed537119ca74c73255a0","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/lib.rs":"44d2e2c595b14d33d16c71dfe4ef42ad0b9e010a878ee2ec49c2e929d60275ba"},"package":"d5c400339a9d1d17be34257d0b407e91d64af335e5b4fa49f4bf28467fc8d635"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_checksum_derive"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (checksum custom derive)"
homepage = "https://mozilla.github.io/uniffi-rs"

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"c8969fbc6e8f6694e260ab78c94f9b4195d61afb7836b4c130b542d3b91b9200","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","release.toml":"b150796411fc6ff90b481218cb50f8ac7c07f5845aebdb8e17877d47e55b05b9","src/ffi/callbackinterface.rs":"f0184cf76bd86abb2815d260a87f85bd7060f5373ac6ef6f71955ece2a5075af","src/ffi/ffidefault.rs":"0db83fbcbc274c4c0daf7fb27833400568839b77a3496155840734c511d801e0","src/ffi/foreignbytes.rs":"d2b46e1a6317aa64801b855e0d12af6bcdef118d8036603d11c3cdaf6f35fdfe","src/ffi/foreigncallbacks.rs":"2b820a34b78705f5debc302a25c64d515a4aa7b3bdade083f4c1cfa2803664ae","src/ffi/foreignfuture.rs":"c1d621e41ea6af0c1d3959b46af8567c3fdc4164e7a82d635fcbb1da2c0737ac","src/ffi/handle.rs":"91f91469a81cb19edebb8bba433df62658cc66f6b54d5dc8520eb5793a85abd9","src/ffi/mod.rs":"30eea545299747838bf11b0698cfb71cedd3ca04d8cfb703c53198fcc44045c1","src/ffi/rustbuffer.rs":"0e725347f916834b17156413f406d5ca6c064b2cbc7437b051fe6692ad72c2aa","src/ffi/rustcalls.rs":"51c6499871c7d5eb4f80cabc806f26dd1df3b1090a2419d0d967aa9c5299a0a6","src/ffi/rustfuture/future.rs":"426cd0ad3c8cf008a7052a7d89856b6c6d5053b94e24325f5666d0281a40ec7f","src/ffi/rustfuture/mod.rs":"44568267e591f5b37f77acfdd6e60ae55ce48ab0a17fd81af3aeb31baa3d53e6","src/ffi/rustfuture/scheduler.rs":"c6484fff14c04596df5f306f2090366435dcff92561d317fde1ea9c097a9576b","src/ffi/rustfuture/tests.rs":"211241fb484a3a103eb0418e7d295850ea021bcd583fa1488f5efc68f33d5ab8","src/ffi_converter_impls.rs":"397c813f2e765462d7a7be524e6ac75e813a91a8ffd11c7e7df05f853213f77b","src/ffi_converter_traits.rs":"24c8cf6ada9b2f63b265e62c0f9092d640e533d0d7234e9156f92c3d1902f430","src/lib.rs":"1f6a031bbb160dfe46455a8bc24596f63b1e478f45579bfff62a62f58900bee4","src/metadata.rs":"83e463c377c0f501e58ac4eb5cc47c433c1473cecd47305fa89283e736b48d96","src/panichook.rs":"9f49c7994a8e5489c1105c488bb3f8c5571bc5f813e7be90441eca15da5c9851"},"package":"0ea3eb5474d50fc149b7e4d86b9c5bd4a61dcc167f0683902bf18ae7bbb3deef"}
{"files":{"Cargo.toml":"c2bc10be9bffda0a1b9ea884f39d6499fc5b4bc8acb32ec33842c67c8ad72305","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","release.toml":"b150796411fc6ff90b481218cb50f8ac7c07f5845aebdb8e17877d47e55b05b9","src/ffi/callbackinterface.rs":"f0184cf76bd86abb2815d260a87f85bd7060f5373ac6ef6f71955ece2a5075af","src/ffi/ffidefault.rs":"0db83fbcbc274c4c0daf7fb27833400568839b77a3496155840734c511d801e0","src/ffi/ffiserialize.rs":"d34312d09d8851c0fc356990d9f3a8a488ea9f6ab089901b1d0c42180ad06be4","src/ffi/foreignbytes.rs":"d2b46e1a6317aa64801b855e0d12af6bcdef118d8036603d11c3cdaf6f35fdfe","src/ffi/foreigncallbacks.rs":"2b820a34b78705f5debc302a25c64d515a4aa7b3bdade083f4c1cfa2803664ae","src/ffi/foreignfuture.rs":"673a7a44d27b88d5297dc6f2da507c9db4d22e6306d9ebc5439cba9c6720cbcb","src/ffi/handle.rs":"91f91469a81cb19edebb8bba433df62658cc66f6b54d5dc8520eb5793a85abd9","src/ffi/mod.rs":"e4d46229ccda04fa42496cb72b09d57936311a1ac1d82343293613e708644494","src/ffi/rustbuffer.rs":"9df75f156dc8ea9565ae320654cc8caab899e688e5414da4e4a43dfe74db9522","src/ffi/rustcalls.rs":"9754b01988983b47d8f0ef6d4e5675cee92ec4ddeb073236b9ccfcac2b12e3ca","src/ffi/rustfuture/future.rs":"426cd0ad3c8cf008a7052a7d89856b6c6d5053b94e24325f5666d0281a40ec7f","src/ffi/rustfuture/mod.rs":"44568267e591f5b37f77acfdd6e60ae55ce48ab0a17fd81af3aeb31baa3d53e6","src/ffi/rustfuture/scheduler.rs":"c6484fff14c04596df5f306f2090366435dcff92561d317fde1ea9c097a9576b","src/ffi/rustfuture/tests.rs":"211241fb484a3a103eb0418e7d295850ea021bcd583fa1488f5efc68f33d5ab8","src/ffi_converter_impls.rs":"397c813f2e765462d7a7be524e6ac75e813a91a8ffd11c7e7df05f853213f77b","src/ffi_converter_traits.rs":"24c8cf6ada9b2f63b265e62c0f9092d640e533d0d7234e9156f92c3d1902f430","src/lib.rs":"477a833a1e41b9e07f3a6f052c6b7f48d11930449ff22b5ff949c7eeaa3e80b9","src/metadata.rs":"83e463c377c0f501e58ac4eb5cc47c433c1473cecd47305fa89283e736b48d96","src/oneshot.rs":"8f1b7e87cc139d274cacef1a7ad6a9e9bf85423a140ad4e5b61e38be9c6e7bb6","src/panichook.rs":"9f49c7994a8e5489c1105c488bb3f8c5571bc5f813e7be90441eca15da5c9851"},"package":"a02e67ac9634b10da9e4aa63a29a7920b8f1395eafef1ea659b2dd76dda96906"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_core"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (runtime support code)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -44,11 +44,6 @@ version = "0.4"
[dependencies.once_cell]
version = "1.10.0"
[dependencies.oneshot]
version = "0.1.6"
features = ["async"]
package = "oneshot-uniffi"
[dependencies.paste]
version = "1.0"
@ -57,4 +52,5 @@ version = "1.1.0"
[features]
default = []
scaffolding-ffi-buffer-fns = []
tokio = ["dep:async-compat"]

View File

@ -0,0 +1,352 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::{Handle, RustBuffer, RustCallStatus, RustCallStatusCode};
use std::{mem::MaybeUninit, ptr::NonNull};
/// FFIBuffer element
///
/// This is the union of all possible primitive FFI types.
/// Composite FFI types like `RustBuffer` and `RustCallStatus` are stored using multiple elements.
#[repr(C)]
#[derive(Clone, Copy)]
pub union FfiBufferElement {
pub u8: u8,
pub i8: i8,
pub u16: u16,
pub i16: i16,
pub u32: u32,
pub i32: i32,
pub u64: u64,
pub i64: i64,
pub float: std::ffi::c_float,
pub double: std::ffi::c_double,
pub ptr: *const std::ffi::c_void,
}
impl Default for FfiBufferElement {
fn default() -> Self {
Self { u64: 0 }
}
}
/// Serialize a FFI value to a buffer
///
/// This trait allows FFI types to be read from/written to FFIBufferElement slices.
/// It's similar, to the [crate::Lift::read] and [crate::Lower::write] methods, but implemented on the FFI types rather than Rust types.
/// It's useful to compare the two:
///
/// - [crate::Lift] and [crate::Lower] are implemented on Rust types like String and user-defined records.
/// - [FfiSerialize] is implemented on the FFI types like RustBuffer, RustCallStatus, and vtable structs.
/// - All 3 traits are implemented for simple cases where the FFI type and Rust type are the same, for example numeric types.
/// - [FfiSerialize] uses FFIBuffer elements rather than u8 elements. Using a union eliminates the need to cast values and creates better alignment.
/// - [FfiSerialize] uses a constant size to store each type.
///
/// [FfiSerialize] is used to generate alternate forms of the scaffolding functions that simplify work needed to implement the bindings on the other side.
/// This is currently only used in the gecko-js bindings for Firefox, but could maybe be useful for other external bindings or even some of the builtin bindings like Python/Kotlin.
///
/// The FFI-buffer version of the scaffolding functions:
/// - Input two pointers to ffi buffers, one to read arguments from and one to write the return value to.
/// - Rather than inputting an out pointer for `RustCallStatus` it's written to the return buffer after the normal return value.
///
pub trait FfiSerialize: Sized {
/// Number of elements required to store this FFI type
const SIZE: usize;
/// Get a value from a ffi buffer
///
/// Note: `buf` should be thought of as `&[FFIBufferElement; Self::SIZE]`, but it can't be spelled out that way
/// since Rust doesn't support that usage of const generics yet.
fn get(buf: &[FfiBufferElement]) -> Self;
/// Put a value to a ffi buffer
///
/// Note: `buf` should be thought of as `&[FFIBufferElement; Self::SIZE]`, but it can't be spelled out that way
/// since Rust doesn't support that usage of const generics yet.
fn put(buf: &mut [FfiBufferElement], value: Self);
/// Read a value from a ffi buffer ref and advance it
///
/// buf must have a length of at least `Self::Size`
fn read(buf: &mut &[FfiBufferElement]) -> Self {
let value = Self::get(buf);
*buf = &buf[Self::SIZE..];
value
}
/// Write a value to a ffi buffer ref and advance it
///
/// buf must have a length of at least `Self::Size`
fn write(buf: &mut &mut [FfiBufferElement], value: Self) {
Self::put(buf, value);
// Lifetime dance taken from `bytes::BufMut`
let (_, new_buf) = core::mem::take(buf).split_at_mut(Self::SIZE);
*buf = new_buf;
}
}
/// Get the FFI buffer size for list of types
#[macro_export]
macro_rules! ffi_buffer_size {
($($T:ty),* $(,)?) => {
(
0
$(
+ <$T as $crate::FfiSerialize>::SIZE
)*
)
}
}
macro_rules! define_ffi_serialize_simple_cases {
($(($name: ident, $T:ty)),* $(,)?) => {
$(
impl FfiSerialize for $T {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
unsafe { buf[0].$name }
}
fn put(buf: &mut[FfiBufferElement], value: Self) {
buf[0].$name = value
}
}
)*
};
}
define_ffi_serialize_simple_cases! {
(i8, i8),
(u8, u8),
(i16, i16),
(u16, u16),
(i32, i32),
(u32, u32),
(i64, i64),
(u64, u64),
(ptr, *const std::ffi::c_void),
}
impl FfiSerialize for f32 {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
unsafe { buf[0].float as Self }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
// Use a cast since it's theoretically possible for float to not be f32 on some systems.
buf[0].float = value as std::ffi::c_float;
}
}
impl FfiSerialize for f64 {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
unsafe { buf[0].double as Self }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
// Use a cast since it's theoretically possible for double to not be f64 on some systems.
buf[0].double = value as std::ffi::c_double;
}
}
impl FfiSerialize for bool {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
unsafe { buf[0].i8 == 1 }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].i8 = if value { 1 } else { 0 }
}
}
impl FfiSerialize for () {
const SIZE: usize = 0;
fn get(_buf: &[FfiBufferElement]) -> Self {}
fn put(_buf: &mut [FfiBufferElement], _value: Self) {}
}
impl<T> FfiSerialize for NonNull<T> {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: this relies on the foreign code passing us valid pointers
unsafe { Self::new_unchecked(buf[0].ptr as *mut T) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].ptr = value.as_ptr() as *const std::ffi::c_void
}
}
impl FfiSerialize for Handle {
const SIZE: usize = 1;
fn get(buf: &[FfiBufferElement]) -> Self {
unsafe { Handle::from_raw_unchecked(buf[0].u64) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].u64 = value.as_raw()
}
}
impl FfiSerialize for RustBuffer {
const SIZE: usize = 3;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
let (capacity, len, data) = unsafe { (buf[0].u64, buf[1].u64, buf[2].ptr as *mut u8) };
unsafe { crate::RustBuffer::from_raw_parts(data, len, capacity) }
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].u64 = value.capacity;
buf[1].u64 = value.len;
buf[2].ptr = value.data as *const std::ffi::c_void;
}
}
impl FfiSerialize for RustCallStatus {
const SIZE: usize = 4;
fn get(buf: &[FfiBufferElement]) -> Self {
// Safety: the foreign bindings are responsible for sending us the correct data.
let code = unsafe { buf[0].i8 };
Self {
code: RustCallStatusCode::try_from(code).unwrap_or(RustCallStatusCode::UnexpectedError),
error_buf: MaybeUninit::new(RustBuffer::get(&buf[1..])),
}
}
fn put(buf: &mut [FfiBufferElement], value: Self) {
buf[0].i8 = value.code as i8;
// Safety: This is okay even if the error buf is not initialized. It just means we'll be
// copying the garbage data.
unsafe { RustBuffer::put(&mut buf[1..], value.error_buf.assume_init()) }
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{Handle, RustBuffer, RustCallStatus, RustCallStatusCode};
#[test]
fn test_ffi_buffer_size() {
assert_eq!(ffi_buffer_size!(u8), 1);
assert_eq!(ffi_buffer_size!(i8), 1);
assert_eq!(ffi_buffer_size!(u16), 1);
assert_eq!(ffi_buffer_size!(i16), 1);
assert_eq!(ffi_buffer_size!(u32), 1);
assert_eq!(ffi_buffer_size!(i32), 1);
assert_eq!(ffi_buffer_size!(u64), 1);
assert_eq!(ffi_buffer_size!(i64), 1);
assert_eq!(ffi_buffer_size!(f32), 1);
assert_eq!(ffi_buffer_size!(f64), 1);
assert_eq!(ffi_buffer_size!(bool), 1);
assert_eq!(ffi_buffer_size!(*const std::ffi::c_void), 1);
assert_eq!(ffi_buffer_size!(RustBuffer), 3);
assert_eq!(ffi_buffer_size!(RustCallStatus), 4);
assert_eq!(ffi_buffer_size!(Handle), 1);
assert_eq!(ffi_buffer_size!(()), 0);
assert_eq!(ffi_buffer_size!(u8, f32, bool, Handle, (), RustBuffer), 7);
}
#[test]
fn test_ffi_serialize() {
let mut some_data = vec![1, 2, 3];
let void_ptr = some_data.as_mut_ptr() as *const std::ffi::c_void;
let rust_buffer = unsafe { RustBuffer::from_raw_parts(some_data.as_mut_ptr(), 2, 3) };
let orig_rust_buffer_data = (
rust_buffer.data_pointer(),
rust_buffer.len(),
rust_buffer.capacity(),
);
let handle = Handle::from_raw(101).unwrap();
let rust_call_status = RustCallStatus::new();
let rust_call_status_error_buf = unsafe { rust_call_status.error_buf.assume_init_ref() };
let orig_rust_call_status_buffer_data = (
rust_call_status_error_buf.data_pointer(),
rust_call_status_error_buf.len(),
rust_call_status_error_buf.capacity(),
);
let mut buf = [FfiBufferElement::default(); 21];
let mut buf_writer = buf.as_mut_slice();
<u8 as FfiSerialize>::write(&mut buf_writer, 0);
<i8 as FfiSerialize>::write(&mut buf_writer, 1);
<u16 as FfiSerialize>::write(&mut buf_writer, 2);
<i16 as FfiSerialize>::write(&mut buf_writer, 3);
<u32 as FfiSerialize>::write(&mut buf_writer, 4);
<i32 as FfiSerialize>::write(&mut buf_writer, 5);
<u64 as FfiSerialize>::write(&mut buf_writer, 6);
<i64 as FfiSerialize>::write(&mut buf_writer, 7);
<f32 as FfiSerialize>::write(&mut buf_writer, 0.1);
<f64 as FfiSerialize>::write(&mut buf_writer, 0.2);
<bool as FfiSerialize>::write(&mut buf_writer, true);
<*const std::ffi::c_void as FfiSerialize>::write(&mut buf_writer, void_ptr);
<RustBuffer as FfiSerialize>::write(&mut buf_writer, rust_buffer);
<RustCallStatus as FfiSerialize>::write(&mut buf_writer, rust_call_status);
<Handle as FfiSerialize>::write(&mut buf_writer, handle);
#[allow(unknown_lints)]
#[allow(clippy::needless_borrows_for_generic_args)]
<() as FfiSerialize>::write(&mut buf_writer, ());
let mut buf_reader = buf.as_slice();
assert_eq!(<u8 as FfiSerialize>::read(&mut buf_reader), 0);
assert_eq!(<i8 as FfiSerialize>::read(&mut buf_reader), 1);
assert_eq!(<u16 as FfiSerialize>::read(&mut buf_reader), 2);
assert_eq!(<i16 as FfiSerialize>::read(&mut buf_reader), 3);
assert_eq!(<u32 as FfiSerialize>::read(&mut buf_reader), 4);
assert_eq!(<i32 as FfiSerialize>::read(&mut buf_reader), 5);
assert_eq!(<u64 as FfiSerialize>::read(&mut buf_reader), 6);
assert_eq!(<i64 as FfiSerialize>::read(&mut buf_reader), 7);
assert_eq!(<f32 as FfiSerialize>::read(&mut buf_reader), 0.1);
assert_eq!(<f64 as FfiSerialize>::read(&mut buf_reader), 0.2);
assert!(<bool as FfiSerialize>::read(&mut buf_reader));
assert_eq!(
<*const std::ffi::c_void as FfiSerialize>::read(&mut buf_reader),
void_ptr
);
let rust_buffer2 = <RustBuffer as FfiSerialize>::read(&mut buf_reader);
assert_eq!(
(
rust_buffer2.data_pointer(),
rust_buffer2.len(),
rust_buffer2.capacity()
),
orig_rust_buffer_data,
);
let rust_call_status2 = <RustCallStatus as FfiSerialize>::read(&mut buf_reader);
assert_eq!(rust_call_status2.code, RustCallStatusCode::Success);
let rust_call_status2_error_buf = unsafe { rust_call_status2.error_buf.assume_init() };
assert_eq!(
(
rust_call_status2_error_buf.data_pointer(),
rust_call_status2_error_buf.len(),
rust_call_status2_error_buf.capacity(),
),
orig_rust_call_status_buffer_data
);
assert_eq!(<Handle as FfiSerialize>::read(&mut buf_reader), handle);
// Ensure that `read` with a unit struct doesn't panic. No need to assert anything, since
// the return type is ().
<() as FfiSerialize>::read(&mut buf_reader);
}
}

View File

@ -4,7 +4,7 @@
//! This module defines a Rust Future that wraps an async foreign function call.
//!
//! The general idea is to create a [oneshot::Channel], hand the sender to the foreign side, and
//! The general idea is to create a oneshot channel, hand the sender to the foreign side, and
//! await the receiver side on the Rust side.
//!
//! The foreign side should:
@ -17,7 +17,7 @@
//! * Wait for the [ForeignFutureHandle::free] function to be called to free the task object.
//! If this is called before the task completes, then the task will be cancelled.
use crate::{LiftReturn, RustCallStatus, UnexpectedUniFFICallbackError};
use crate::{oneshot, LiftReturn, RustCallStatus};
/// Handle for a foreign future
pub type ForeignFutureHandle = u64;
@ -69,15 +69,8 @@ where
// The important thing is that the ForeignFuture will be dropped when this Future is.
let _foreign_future =
call_scaffolding_function(foreign_future_complete::<T, UT>, sender.into_raw() as u64);
match receiver.await {
Ok(result) => T::lift_foreign_return(result.return_value, result.call_status),
Err(e) => {
// This shouldn't happen in practice, but we can do our best to recover
T::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(format!(
"Error awaiting foreign future: {e}"
)))
}
}
let result = receiver.await;
T::lift_foreign_return(result.return_value, result.call_status)
}
pub extern "C" fn foreign_future_complete<T: LiftReturn<UT>, UT>(
@ -85,10 +78,7 @@ pub extern "C" fn foreign_future_complete<T: LiftReturn<UT>, UT>(
result: ForeignFutureResult<T::ReturnType>,
) {
let channel = unsafe { oneshot::Sender::from_raw(oneshot_handle as *mut ()) };
// Ignore errors in send.
//
// Error means the receiver was already dropped which will happen when the future is cancelled.
let _ = channel.send(result);
channel.send(result);
}
#[cfg(test)]

View File

@ -6,6 +6,8 @@
pub mod callbackinterface;
pub mod ffidefault;
#[cfg(feature = "scaffolding-ffi-buffer-fns")]
pub mod ffiserialize;
pub mod foreignbytes;
pub mod foreigncallbacks;
pub mod foreignfuture;
@ -16,6 +18,8 @@ pub mod rustfuture;
pub use callbackinterface::*;
pub use ffidefault::FfiDefault;
#[cfg(feature = "scaffolding-ffi-buffer-fns")]
pub use ffiserialize::FfiSerialize;
pub use foreignbytes::*;
pub use foreigncallbacks::*;
pub use foreignfuture::*;

View File

@ -53,12 +53,12 @@ use crate::ffi::{rust_call, ForeignBytes, RustCallStatus};
pub struct RustBuffer {
/// The allocated capacity of the underlying `Vec<u8>`.
/// In Rust this is a `usize`, but we use an `u64` to keep the foreign binding code simple.
capacity: u64,
pub(crate) capacity: u64,
/// The occupied length of the underlying `Vec<u8>`.
/// In Rust this is a `usize`, but we use an `u64` to keep the foreign binding code simple.
len: u64,
pub(crate) len: u64,
/// The pointer to the allocated buffer of the `Vec<u8>`.
data: *mut u8,
pub(crate) data: *mut u8,
}
// Mark `RustBuffer` as safe to send between threads, despite the `u8` pointer. The only mutable
@ -107,6 +107,12 @@ impl RustBuffer {
.expect("buffer length negative or overflowed")
}
pub fn capacity(&self) -> usize {
self.capacity
.try_into()
.expect("buffer length negative or overflowed")
}
/// Get a pointer to the data
pub fn data_pointer(&self) -> *const u8 {
self.data

View File

@ -106,6 +106,20 @@ pub enum RustCallStatusCode {
Cancelled = 3,
}
impl TryFrom<i8> for RustCallStatusCode {
type Error = i8;
fn try_from(value: i8) -> Result<Self, i8> {
match value {
0 => Ok(Self::Success),
1 => Ok(Self::Error),
2 => Ok(Self::UnexpectedError),
3 => Ok(Self::Cancelled),
n => Err(n),
}
}
}
/// Handle a scaffolding calls
///
/// `callback` is responsible for making the actual Rust call and returning a special result type:

View File

@ -42,7 +42,10 @@ pub mod ffi;
mod ffi_converter_impls;
mod ffi_converter_traits;
pub mod metadata;
mod oneshot;
#[cfg(feature = "scaffolding-ffi-buffer-fns")]
pub use ffi::ffiserialize::FfiBufferElement;
pub use ffi::*;
pub use ffi_converter_traits::{
ConvertError, FfiConverter, FfiConverterArc, HandleAlloc, Lift, LiftRef, LiftReturn, Lower,
@ -58,7 +61,6 @@ pub mod deps {
pub use async_compat;
pub use bytes;
pub use log;
pub use oneshot;
pub use static_assertions;
}

View File

@ -0,0 +1,77 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Implements a simple oneshot channel.
//!
//! We used to use the `oneshot` crate for this, but the dependency was hard to manage
//! (https://github.com/mozilla/uniffi-rs/issues/1736)
//!
//! This implementation is less efficient, but the difference is probably negligible for most
//! use-cases involving UniFFI.
use std::{
future::Future,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
};
pub struct Sender<T>(Arc<Mutex<OneshotInner<T>>>);
pub struct Receiver<T>(Arc<Mutex<OneshotInner<T>>>);
struct OneshotInner<T> {
value: Option<T>,
waker: Option<Waker>,
}
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let arc = Arc::new(Mutex::new(OneshotInner {
value: None,
waker: None,
}));
(Sender(arc.clone()), Receiver(arc))
}
impl<T> Sender<T> {
/// Send a value to the receiver
pub fn send(self, value: T) {
let mut inner = self.0.lock().unwrap();
inner.value = Some(value);
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
/// Convert a Sender into a raw pointer
///
/// from_raw must be called with this pointer or else the sender will leak
pub fn into_raw(self) -> *const () {
Arc::into_raw(self.0) as *const ()
}
/// Convert a raw pointer back to a Sender
///
/// # Safety
///
/// `raw_ptr` must have come from into_raw(). Once a pointer is passed into `from_raw` it must
/// not be used again.
pub unsafe fn from_raw(raw_ptr: *const ()) -> Self {
Self(Arc::from_raw(raw_ptr as *const Mutex<OneshotInner<T>>))
}
}
impl<T> Future for Receiver<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut inner = self.0.lock().unwrap();
match inner.value.take() {
Some(v) => Poll::Ready(v),
None => {
inner.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"a292239ca3c72852768fdf0e7bc2dd6386af7bf1ab0ef56dff01e1c9e781b2ca","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/custom.rs":"36cd6c2eeb8efdc34e59dff634a22e79471ab17f49ceb0f131da5f144313f7e4","src/default.rs":"77466ac54da69094bcdccc5927d0980b1e9dd0095647ca825830673c48847a53","src/enum_.rs":"afe0a6534d8e7f68047e3f1afad9369d5d5650f4c7555e8d4173f24126c715ba","src/error.rs":"30168378da9a23e6530ffe68647bf6618d07a0aaa236d5009137a922798a0e88","src/export.rs":"42c5e784c1dccc796c8b6ea29c2dc1811e48a531488a3ed0e2a59330778a7e41","src/export/attributes.rs":"c848f8c309c4cf7a168f038834752dc4816b5c853768d7c331ea4cd5ce0841b7","src/export/callback_interface.rs":"794b0665dc7eb02ea854c61c8bb2781e0b4ac1de646d95a8fd7791f770f2e6e3","src/export/item.rs":"4e86875692c2d2993fde12e78dbde2cbffa5675ede143577d5620126401efe05","src/export/scaffolding.rs":"b25167d2213b6d6c5ba653622f26791e8c3e74a5ecce6512ec27009fc8bf68e4","src/export/trait_interface.rs":"f07f9908ee28661de4586d89b693f3d93dae5e5cba8a089eff25f20bbf6b373b","src/export/utrait.rs":"b55533d3eef8262944d3c0d9a3a9cba0615d2d5af8608f0919abc7699989e2a8","src/fnsig.rs":"5e434a1cc87166c5245424bb14e896eb766bf680d4d50d4b8536852f91487d7c","src/lib.rs":"a28bbfd2d1dc835306ff6072f75761bb6b3a158477bba966057776c527fe6d70","src/object.rs":"5419ed64c8120aef811a77c2205f58a7a537bdf34ae04f9c92dd3aaa176eed39","src/record.rs":"29072542cc2f3e027bd7c59b45ba913458f8213d1b2b33bc70d140baa98fcdc8","src/setup_scaffolding.rs":"173fdc916967d54bd6532def16d12e5bb85467813a46a031d3338b77625756bb","src/test.rs":"1673f282bb35d6b0740ad0e5f11826c2852d7a0db29604c2258f457415b537e8","src/util.rs":"a2c3693343e78dffb2a7f7b39eeb9b7f298b66688f1766a7c08113cf9431ef4c"},"package":"18331d35003f46f0d04047fbe4227291815b83a937a8c32bc057f990962182c4"}
{"files":{"Cargo.toml":"32636b759af83af70f08c4573c101f0467eb8469c318d08dcfbc4578fe456660","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/custom.rs":"36cd6c2eeb8efdc34e59dff634a22e79471ab17f49ceb0f131da5f144313f7e4","src/default.rs":"77466ac54da69094bcdccc5927d0980b1e9dd0095647ca825830673c48847a53","src/enum_.rs":"afe0a6534d8e7f68047e3f1afad9369d5d5650f4c7555e8d4173f24126c715ba","src/error.rs":"30168378da9a23e6530ffe68647bf6618d07a0aaa236d5009137a922798a0e88","src/export.rs":"42c5e784c1dccc796c8b6ea29c2dc1811e48a531488a3ed0e2a59330778a7e41","src/export/attributes.rs":"c848f8c309c4cf7a168f038834752dc4816b5c853768d7c331ea4cd5ce0841b7","src/export/callback_interface.rs":"794b0665dc7eb02ea854c61c8bb2781e0b4ac1de646d95a8fd7791f770f2e6e3","src/export/item.rs":"4e86875692c2d2993fde12e78dbde2cbffa5675ede143577d5620126401efe05","src/export/scaffolding.rs":"03abb762391f67b43bec89dae8f57b54b014133193a8337436f4129de51fb987","src/export/trait_interface.rs":"f07f9908ee28661de4586d89b693f3d93dae5e5cba8a089eff25f20bbf6b373b","src/export/utrait.rs":"b55533d3eef8262944d3c0d9a3a9cba0615d2d5af8608f0919abc7699989e2a8","src/fnsig.rs":"5e434a1cc87166c5245424bb14e896eb766bf680d4d50d4b8536852f91487d7c","src/lib.rs":"a28bbfd2d1dc835306ff6072f75761bb6b3a158477bba966057776c527fe6d70","src/object.rs":"5419ed64c8120aef811a77c2205f58a7a537bdf34ae04f9c92dd3aaa176eed39","src/record.rs":"29072542cc2f3e027bd7c59b45ba913458f8213d1b2b33bc70d140baa98fcdc8","src/setup_scaffolding.rs":"173fdc916967d54bd6532def16d12e5bb85467813a46a031d3338b77625756bb","src/test.rs":"1673f282bb35d6b0740ad0e5f11826c2852d7a0db29604c2258f457415b537e8","src/util.rs":"d1b7b80d0bcc43b7794cd2dbdd89194f84b098343f9e0a3f1843e91c95e1ded3"},"package":"b6f08d5592c669b80a8af5066027098bebec4b4af17a9b8b299bac5f518ab89e"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_macros"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (convenience macros)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -61,13 +61,14 @@ features = [
version = "0.5.9"
[dependencies.uniffi_build]
version = "=0.27.1"
version = "=0.27.3"
optional = true
[dependencies.uniffi_meta]
version = "=0.27.1"
version = "=0.27.3"
[features]
default = []
nightly = []
scaffolding-ffi-buffer-fns = []
trybuild = ["dep:uniffi_build"]

View File

@ -244,6 +244,12 @@ pub(super) fn gen_ffi_function(
let return_impl = &sig.lower_return_impl();
Ok(if !sig.is_async {
let scaffolding_fn_ffi_buffer_version = ffi_buffer_scaffolding_fn(
&ffi_ident,
&quote! { <#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>::ReturnType },
&param_types,
true,
);
quote! {
#[doc(hidden)]
#[no_mangle]
@ -267,12 +273,16 @@ pub(super) fn gen_ffi_function(
)
})
}
#scaffolding_fn_ffi_buffer_version
}
} else {
let mut future_expr = rust_fn_call;
if matches!(ar, Some(AsyncRuntime::Tokio(_))) {
future_expr = quote! { ::uniffi::deps::async_compat::Compat::new(#future_expr) }
}
let scaffolding_fn_ffi_buffer_version =
ffi_buffer_scaffolding_fn(&ffi_ident, &quote! { ::uniffi::Handle}, &param_types, false);
quote! {
#[doc(hidden)]
@ -300,6 +310,71 @@ pub(super) fn gen_ffi_function(
},
}
}
#scaffolding_fn_ffi_buffer_version
}
})
}
#[cfg(feature = "scaffolding-ffi-buffer-fns")]
fn ffi_buffer_scaffolding_fn(
fn_ident: &Ident,
return_type: &TokenStream,
param_types: &[TokenStream],
has_rust_call_status: bool,
) -> TokenStream {
let fn_name = fn_ident.to_string();
let ffi_buffer_fn_name = uniffi_meta::ffi_buffer_symbol_name(&fn_name);
let ident = Ident::new(&ffi_buffer_fn_name, proc_macro2::Span::call_site());
let type_list: Vec<_> = param_types.iter().map(|ty| quote! { #ty }).collect();
if has_rust_call_status {
quote! {
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ident(
arg_ptr: *mut ::uniffi::FfiBufferElement,
return_ptr: *mut ::uniffi::FfiBufferElement,
) {
let mut arg_buf = unsafe { ::std::slice::from_raw_parts(arg_ptr, ::uniffi::ffi_buffer_size!(#(#type_list),*)) };
let mut return_buf = unsafe { ::std::slice::from_raw_parts_mut(return_ptr, ::uniffi::ffi_buffer_size!(#return_type, ::uniffi::RustCallStatus)) };
let mut out_status = ::uniffi::RustCallStatus::default();
let return_value = #fn_ident(
#(
<#type_list as ::uniffi::FfiSerialize>::read(&mut arg_buf),
)*
&mut out_status,
);
<#return_type as ::uniffi::FfiSerialize>::write(&mut return_buf, return_value);
<::uniffi::RustCallStatus as ::uniffi::FfiSerialize>::write(&mut return_buf, out_status);
}
}
} else {
quote! {
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn #ident(
arg_ptr: *mut ::uniffi::FfiBufferElement,
return_ptr: *mut ::uniffi::FfiBufferElement,
) {
let mut arg_buf = unsafe { ::std::slice::from_raw_parts(arg_ptr, ::uniffi::ffi_buffer_size!(#(#type_list),*)) };
let mut return_buf = unsafe { ::std::slice::from_raw_parts_mut(return_ptr, ::uniffi::ffi_buffer_size!(#return_type)) };
let return_value = #fn_ident(#(
<#type_list as ::uniffi::FfiSerialize>::read(&mut arg_buf),
)*);
<#return_type as ::uniffi::FfiSerialize>::put(&mut return_buf, return_value);
}
}
}
}
#[cfg(not(feature = "scaffolding-ffi-buffer-fns"))]
fn ffi_buffer_scaffolding_fn(
_fn_ident: &Ident,
_return_type: &TokenStream,
_param_types: &[TokenStream],
_add_rust_call_status: bool,
) -> TokenStream {
quote! {}
}

View File

@ -110,7 +110,6 @@ pub fn create_metadata_items(
let const_ident =
format_ident!("UNIFFI_META_CONST_{crate_name_upper}_{kind_upper}_{name_upper}");
let static_ident = format_ident!("UNIFFI_META_{crate_name_upper}_{kind_upper}_{name_upper}");
let checksum_fn = checksum_fn_name.map(|name| {
let ident = Ident::new(&name, Span::call_site());
quote! {

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"5620cf9840477b158641547703ba353e3ad8427ec7b20b9dd5e5f5fe4df7d6d2","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/ffi_names.rs":"ca38b700a0a103c9faaf456ed91b67adf46d4e750aee9e9cd01ad97fb1840494","src/group.rs":"d0a43f3c528aba9403649715981ad3a8849d7a370f4ef9e2d618b88f60a3102f","src/lib.rs":"3f00d5214e2785e4b3045bc48899f6f6b1dce32ab3da6be3ebce716ee9d24c5f","src/metadata.rs":"3f236b337a1fd5082ea9cc4fee6800193a903ee88b81f1c3202843402f122a14","src/reader.rs":"579e2b87d8dd9d703b8811294abfb992621c0a46765800e4db2fad2906db2208","src/types.rs":"c2c5188da8cdf5af7f8496d4660bcfaa971b81ed73b64486c05b47256048544f"},"package":"f7224422c4cfd181c7ca9fca2154abca4d21db962f926f270f996edd38b0c4b8"}
{"files":{"Cargo.toml":"3d53a00cb590863149f9e68505581ccbfc635ac1bb2b59d692f5d679cd1732bc","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/ffi_names.rs":"240e04f257fd199e268ff16d5979f521cb9d18e21d494981718fa2b96956324d","src/group.rs":"d0a43f3c528aba9403649715981ad3a8849d7a370f4ef9e2d618b88f60a3102f","src/lib.rs":"3f00d5214e2785e4b3045bc48899f6f6b1dce32ab3da6be3ebce716ee9d24c5f","src/metadata.rs":"3f236b337a1fd5082ea9cc4fee6800193a903ee88b81f1c3202843402f122a14","src/reader.rs":"579e2b87d8dd9d703b8811294abfb992621c0a46765800e4db2fad2906db2208","src/types.rs":"c2c5188da8cdf5af7f8496d4660bcfaa971b81ed73b64486c05b47256048544f"},"package":"583bab49f2bdf5681f9732f8b67a7e555ad920dbb5427be21450217bf1818189"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_meta"
version = "0.27.1"
version = "0.27.3"
description = "uniffi_meta"
homepage = "https://mozilla.github.io/uniffi-rs"
readme = "README.md"
@ -33,4 +33,4 @@ version = "1.3"
version = "0.3"
[dependencies.uniffi_checksum_derive]
version = "0.27.1"
version = "0.27.3"

View File

@ -73,3 +73,12 @@ pub fn method_checksum_symbol_name(namespace: &str, object_name: &str, name: &st
let name = name.to_ascii_lowercase();
format!("uniffi_{namespace}_checksum_method_{object_name}_{name}")
}
/// Get the symbol name for a FFI-buffer version of a function
pub fn ffi_buffer_symbol_name(fn_name: &str) -> String {
match fn_name.strip_prefix("uniffi_") {
Some(rest) => format!("uniffi_ffibuffer_{rest}"),
// this should never happen, but if it does let's try our best to prefix things properl.
None => format!("uniffi_ffibuffer_{fn_name}"),
}
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"f29ebbc363e01ee31c5a351aa6c19bc99343b4d293d5ea7954bca3f49e77ad54","README.md":"ec6aba24af9a011ef6647422aa22efabdee519cdee3da1a9f9033b07b7cbdb0d","src/lib.rs":"e19f60aed5a137401203b9054ead27894b98490bab3e2f680a23549c8ee8a13a"},"package":"f8ce878d0bdfc288b58797044eaaedf748526c56eef3575380bb4d4b19d69eee"}
{"files":{"Cargo.toml":"02452729580c718585b4cc1cbaa2d09aa97ebec62c4d0bd3fdb50d6063cfe036","README.md":"ec6aba24af9a011ef6647422aa22efabdee519cdee3da1a9f9033b07b7cbdb0d","src/lib.rs":"e19f60aed5a137401203b9054ead27894b98490bab3e2f680a23549c8ee8a13a"},"package":"13963044ca9bde9b709d2eee68bc11dafc7acea144ae0fdc0cf29ed4add44481"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_testing"
version = "0.27.1"
version = "0.27.3"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (testing helpers)"
homepage = "https://mozilla.github.io/uniffi-rs"

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"e4553df91528daadbc52244f436c3ae17d9c6a4629d232643a20ed63c1311625","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/attributes.rs":"f1cdee01db837920dbd109d60b39dd4b0ece56f83797fe4ace1e0caf97c02483","src/collectors.rs":"00c9cd8e8f7be6949996f069b449f120f0b0694ff2f1ca92b79201721c18c594","src/converters/callables.rs":"1ad26c2782629e98272edc75c38343f58194ebf9626ae451ba84546a60d45a48","src/converters/enum_.rs":"aa0ca7a7a50521a45e296c6f53be5f1981a41872443946f72c2ca8baebe4d69b","src/converters/interface.rs":"6042d4abd5e236ed6158c5f6d4d9b81bb01fbbcb8b42bf8a5b385b978c68f6f8","src/converters/mod.rs":"c34a30e3b7a2e3c092a7074f4bab6f6c34364177c096af59a7dda13e44ffdc3c","src/finder.rs":"3586ffd3772151eabbc3d6062725933c88978e1b5feb64312bbf42cd43af30fa","src/lib.rs":"56c50ce61ba5e7266fe7fc9fa9d0022cdbfbe9801730753bd4ee66fed040221c","src/literal.rs":"4f28ad49a17246b4dc30a677cfff65b345bfac0924856e19f58e7574a74c2c40","src/resolver.rs":"c4ff362055dc4ed489e94a063ec0fd3becb8787ad35ce65cdec437a1aae518a0"},"package":"8c43c9ed40a8d20a5c3eae2d23031092db6b96dc8e571beb449ba9757484cea0"}
{"files":{"Cargo.toml":"eda87b82f785000e7ac4d07d24285dc5a961d8109bd90dfd134f8545a86f3b56","README.md":"37c1af00ec81a9f1bc206ab3578356e5f9ad4077dc46dd1bb623d81d804948b8","src/attributes.rs":"f1cdee01db837920dbd109d60b39dd4b0ece56f83797fe4ace1e0caf97c02483","src/collectors.rs":"00c9cd8e8f7be6949996f069b449f120f0b0694ff2f1ca92b79201721c18c594","src/converters/callables.rs":"1ad26c2782629e98272edc75c38343f58194ebf9626ae451ba84546a60d45a48","src/converters/enum_.rs":"aa0ca7a7a50521a45e296c6f53be5f1981a41872443946f72c2ca8baebe4d69b","src/converters/interface.rs":"6042d4abd5e236ed6158c5f6d4d9b81bb01fbbcb8b42bf8a5b385b978c68f6f8","src/converters/mod.rs":"c34a30e3b7a2e3c092a7074f4bab6f6c34364177c096af59a7dda13e44ffdc3c","src/finder.rs":"3586ffd3772151eabbc3d6062725933c88978e1b5feb64312bbf42cd43af30fa","src/lib.rs":"56c50ce61ba5e7266fe7fc9fa9d0022cdbfbe9801730753bd4ee66fed040221c","src/literal.rs":"4f28ad49a17246b4dc30a677cfff65b345bfac0924856e19f58e7574a74c2c40","src/resolver.rs":"c4ff362055dc4ed489e94a063ec0fd3becb8787ad35ce65cdec437a1aae518a0"},"package":"b92f984bb0d9a06778f256aec963e1e9a80714014f7a90fb0e01008821fe5a97"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2021"
name = "uniffi_udl"
version = "0.27.1"
version = "0.27.3"
description = "udl parsing for the uniffi project"
homepage = "https://mozilla.github.io/uniffi-rs"
documentation = "https://mozilla.github.io/uniffi-rs"
@ -29,12 +29,14 @@ version = "1"
[dependencies.textwrap]
version = "0.16"
features = ["smawk"]
default-features = false
[dependencies.uniffi_meta]
version = "=0.27.1"
version = "=0.27.3"
[dependencies.uniffi_testing]
version = "=0.27.1"
version = "=0.27.3"
[dependencies.weedle2]
version = "5.0.0"