From 48a75485b67fa17d8a37340c0e7fa5db9b322518 Mon Sep 17 00:00:00 2001 From: Julien THILLARD Date: Fri, 20 Mar 2026 09:47:32 +0100 Subject: [PATCH] Add more from the std --- .cargo/config.toml | 4 +- .gitignore | 2 + .gitmodules | 7 +- Cargo.toml | 4 +- crates/os-std-macros/Cargo.toml | 12 - crates/os-std-macros/src/lib.rs | 1 - crates/std/Cargo.toml | 14 - crates/std/crates/panic_abort/Cargo.toml | 19 - crates/std/crates/panic_abort/src/android.rs | 51 - crates/std/crates/panic_abort/src/lib.rs | 94 - crates/std/crates/panic_abort/src/zkvm.rs | 26 - crates/std/crates/std_detect/Cargo.toml | 24 - crates/std/crates/std_detect/README.md | 69 - .../std_detect/src/detect/arch/aarch64.rs | 264 -- .../crates/std_detect/src/detect/arch/arm.rs | 32 - .../std_detect/src/detect/arch/loongarch.rs | 58 - .../crates/std_detect/src/detect/arch/mips.rs | 15 - .../std_detect/src/detect/arch/mips64.rs | 15 - .../crates/std_detect/src/detect/arch/mod.rs | 83 - .../std_detect/src/detect/arch/powerpc.rs | 33 - .../std_detect/src/detect/arch/powerpc64.rs | 33 - .../std_detect/src/detect/arch/riscv.rs | 380 -- .../std_detect/src/detect/arch/s390x.rs | 61 - .../crates/std_detect/src/detect/arch/x86.rs | 280 -- .../std/crates/std_detect/src/detect/bit.rs | 9 - .../std/crates/std_detect/src/detect/cache.rs | 203 - .../crates/std_detect/src/detect/macros.rs | 203 - .../std/crates/std_detect/src/detect/mod.rs | 125 - .../std_detect/src/detect/os/aarch64.rs | 127 - .../src/detect/os/darwin/aarch64.rs | 166 - .../src/detect/os/freebsd/aarch64.rs | 3 - .../std_detect/src/detect/os/freebsd/arm.rs | 36 - .../src/detect/os/freebsd/auxvec.rs | 63 - .../std_detect/src/detect/os/freebsd/mod.rs | 25 - .../src/detect/os/freebsd/powerpc.rs | 21 - .../std_detect/src/detect/os/linux/aarch64.rs | 409 -- .../src/detect/os/linux/aarch64/tests.rs | 70 - .../std_detect/src/detect/os/linux/arm.rs | 34 - .../std_detect/src/detect/os/linux/auxvec.rs | 221 -- .../src/detect/os/linux/auxvec/tests.rs | 108 - .../src/detect/os/linux/loongarch.rs | 60 - .../std_detect/src/detect/os/linux/mips.rs | 23 - .../std_detect/src/detect/os/linux/mod.rs | 74 - .../std_detect/src/detect/os/linux/powerpc.rs | 35 - .../std_detect/src/detect/os/linux/riscv.rs | 323 -- .../std_detect/src/detect/os/linux/s390x.rs | 152 - .../src/detect/os/openbsd/aarch64.rs | 57 - .../src/detect/os/openbsd/auxvec.rs | 54 - .../std_detect/src/detect/os/openbsd/mod.rs | 21 - .../src/detect/os/openbsd/powerpc.rs | 21 - .../crates/std_detect/src/detect/os/other.rs | 8 - .../crates/std_detect/src/detect/os/riscv.rs | 159 - .../std_detect/src/detect/os/riscv/tests.rs | 64 - .../src/detect/os/windows/aarch64.rs | 125 - .../crates/std_detect/src/detect/os/x86.rs | 330 -- .../test_data/linux-artificial-aarch64.auxv | Bin 336 -> 0 bytes .../test_data/linux-empty-hwcap2-aarch64.auxv | Bin 336 -> 0 bytes .../test_data/linux-hwcap2-aarch64.auxv | Bin 336 -> 0 bytes .../test_data/linux-no-hwcap2-aarch64.auxv | Bin 320 -> 0 bytes .../src/detect/test_data/linux-rpi3.auxv | Bin 160 -> 0 bytes .../macos-virtualbox-linux-x86-4850HQ.auxv | Bin 160 -> 0 bytes crates/std/crates/std_detect/src/lib.rs | 35 - .../crates/std_detect/tests/cpu-detection.rs | 335 -- .../std_detect/tests/macro_trailing_commas.rs | 110 - .../crates/std_detect/tests/x86-specific.rs | 90 - crates/std/patches.sed | 6 - crates/std/patches/collections/hash/map.sed | 2 - crates/std/patches/collections/hash/set.sed | 2 - crates/std/patches/sys/alloc/mod.sed | 3 - crates/std/src/backtrace/tests.rs | 98 - crates/std/src/collections/hash/map.rs | 3038 --------------- crates/std/src/collections/hash/map/tests.rs | 1059 ----- crates/std/src/collections/hash/mod.rs | 4 - crates/std/src/collections/hash/set.rs | 2519 ------------ crates/std/src/collections/hash/set/tests.rs | 529 --- crates/std/src/collections/mod.rs | 459 --- crates/std/src/ffi/c_str.rs | 14 - crates/std/src/ffi/mod.rs | 207 - crates/std/src/ffi/os_str.rs | 1851 --------- crates/std/src/ffi/os_str/tests.rs | 311 -- crates/std/src/fs/tests.rs | 2548 ------------ crates/std/src/hash/mod.rs | 91 - crates/std/src/hash/random.rs | 159 - crates/std/src/io/buffered/bufreader.rs | 601 --- .../std/src/io/buffered/bufreader/buffer.rs | 155 - crates/std/src/io/buffered/bufwriter.rs | 680 ---- crates/std/src/io/buffered/linewriter.rs | 235 -- crates/std/src/io/buffered/linewritershim.rs | 297 -- crates/std/src/io/buffered/mod.rs | 189 - crates/std/src/io/buffered/tests.rs | 1087 ------ crates/std/src/io/copy.rs | 297 -- crates/std/src/io/copy/tests.rs | 147 - crates/std/src/io/cursor.rs | 757 ---- crates/std/src/io/cursor/tests.rs | 567 --- crates/std/src/io/error.rs | 1085 ------ crates/std/src/io/error/repr_bitpacked.rs | 411 -- crates/std/src/io/error/repr_unpacked.rs | 50 - crates/std/src/io/error/tests.rs | 191 - crates/std/src/io/impls.rs | 717 ---- crates/std/src/io/impls/tests.rs | 57 - crates/std/src/io/mod.rs | 3408 ----------------- crates/std/src/io/pipe.rs | 295 -- crates/std/src/io/pipe/tests.rs | 18 - crates/std/src/io/prelude.rs | 14 - crates/std/src/io/stdio.rs | 1290 ------- crates/std/src/io/stdio/tests.rs | 166 - crates/std/src/io/tests.rs | 947 ----- crates/std/src/io/util.rs | 448 --- crates/std/src/io/util/tests.rs | 185 - crates/std/src/net/hostname.rs | 22 - crates/std/src/net/ip_addr.rs | 10 - crates/std/src/net/ip_addr/tests.rs | 8 - crates/std/src/net/mod.rs | 72 - crates/std/src/net/socket_addr.rs | 268 -- crates/std/src/net/socket_addr/tests.rs | 306 -- crates/std/src/net/tcp.rs | 1083 ------ crates/std/src/net/tcp/tests.rs | 940 ----- crates/std/src/net/test.rs | 44 - crates/std/src/net/udp.rs | 848 ---- crates/std/src/net/udp/tests.rs | 381 -- crates/std/src/num/f128.rs | 1086 ------ crates/std/src/num/f16.rs | 1047 ----- crates/std/src/num/f32.rs | 1276 ------ crates/std/src/num/f64.rs | 1276 ------ crates/std/src/num/mod.rs | 28 - crates/std/src/prelude/mod.rs | 192 - crates/std/src/prelude/v1.rs | 186 - crates/std/src/process/tests.rs | 669 ---- crates/std/src/sync/barrier.rs | 167 - crates/std/src/sync/lazy_lock.rs | 426 --- crates/std/src/sync/mod.rs | 307 -- crates/std/src/sync/mpmc/array.rs | 569 --- crates/std/src/sync/mpmc/context.rs | 159 - crates/std/src/sync/mpmc/counter.rs | 136 - crates/std/src/sync/mpmc/error.rs | 49 - crates/std/src/sync/mpmc/list.rs | 668 ---- crates/std/src/sync/mpmc/mod.rs | 1388 ------- crates/std/src/sync/mpmc/select.rs | 71 - crates/std/src/sync/mpmc/tests.rs | 14 - crates/std/src/sync/mpmc/utils.rs | 137 - crates/std/src/sync/mpmc/waker.rs | 209 - crates/std/src/sync/mpmc/zero.rs | 319 -- crates/std/src/sync/mpsc.rs | 1214 ------ crates/std/src/sync/nonpoison.rs | 45 - crates/std/src/sync/nonpoison/condvar.rs | 444 --- crates/std/src/sync/nonpoison/mutex.rs | 649 ---- crates/std/src/sync/nonpoison/rwlock.rs | 1140 ------ crates/std/src/sync/once.rs | 402 -- crates/std/src/sync/once_lock.rs | 709 ---- crates/std/src/sync/oneshot.rs | 466 --- crates/std/src/sync/poison.rs | 389 -- crates/std/src/sync/poison/condvar.rs | 510 --- crates/std/src/sync/poison/mutex.rs | 946 ----- crates/std/src/sync/poison/rwlock.rs | 1274 ------ crates/std/src/sync/reentrant_lock.rs | 432 --- crates/std/src/sys/alloc/mod.rs | 113 - crates/std/src/sys/alloc/survos.rs | 14 - crates/std/src/thread/builder.rs | 263 -- crates/std/src/thread/current.rs | 332 -- crates/std/src/thread/functions.rs | 696 ---- crates/std/src/thread/id.rs | 120 - crates/std/src/thread/join_handle.rs | 186 - crates/std/src/thread/lifecycle.rs | 265 -- crates/std/src/thread/local.rs | 865 ----- crates/std/src/thread/main_thread.rs | 56 - crates/std/src/thread/mod.rs | 268 -- crates/std/src/thread/scoped.rs | 358 -- crates/std/src/thread/spawnhook.rs | 153 - crates/std/src/thread/tests.rs | 427 --- crates/std/src/thread/thread.rs | 326 -- justfile | 11 +- library/.gitignore | 14 + library/Cargo.toml | 88 + .../crates/backtrace-rs => library/backtrace | 0 {crates/std => library}/justfile | 194 +- library/proc_macro | 1 + library/profiler_builtins | 1 + library/std/Cargo.toml | 177 + {crates => library}/std/build.rs | 0 .../std/patches/sys/args/mod.sed | 0 .../std/patches/sys/io/error/mod.sed | 0 library/std/patches/sys/pal/mod.sed | 6 + .../std/patches/sys/random/mod.sed | 0 .../std/patches/sys/thread_local/mod.sed | 0 library/std/riscv64.json | 22 + {crates => library}/std/src/alloc.rs | 0 {crates => library}/std/src/ascii.rs | 0 library/std/src/backtrace | 1 + {crates => library}/std/src/backtrace.rs | 0 {crates => library}/std/src/bstr.rs | 2 +- library/std/src/collections | 1 + {crates => library}/std/src/env.rs | 0 {crates => library}/std/src/error.rs | 2 +- library/std/src/ffi | 1 + library/std/src/fs | 1 + {crates => library}/std/src/fs.rs | 0 library/std/src/hash | 1 + library/std/src/io | 1 + {crates => library}/std/src/keyword_docs.rs | 0 {crates => library}/std/src/lib.rs | 236 +- {crates => library}/std/src/macros.rs | 2 +- library/std/src/net | 1 + library/std/src/num | 1 + {crates => library}/std/src/os/mod.rs | 0 {crates => library}/std/src/os/raw/mod.rs | 2 +- {crates => library}/std/src/os/raw/tests.rs | 0 {crates => library}/std/src/panic.rs | 0 {crates => library}/std/src/panicking.rs | 0 {crates => library}/std/src/pat.rs | 0 {crates => library}/std/src/path.rs | 0 library/std/src/prelude | 1 + library/std/src/process | 1 + {crates => library}/std/src/process.rs | 0 {crates => library}/std/src/random.rs | 0 {crates => library}/std/src/rt.rs | 0 library/std/src/sync | 1 + {crates => library}/std/src/sys/args/mod.rs | 0 .../std/src/sys/args/unsupported.rs | 0 {crates => library}/std/src/sys/backtrace.rs | 0 {crates => library}/std/src/sys/cmath.rs | 0 .../std/src/sys/configure_builtins.rs | 0 {crates => library}/std/src/sys/env/common.rs | 0 {crates => library}/std/src/sys/env/mod.rs | 0 .../std/src/sys/env/unsupported.rs | 0 {crates => library}/std/src/sys/env_consts.rs | 0 {crates => library}/std/src/sys/exit.rs | 0 {crates => library}/std/src/sys/fd/mod.rs | 0 {crates => library}/std/src/sys/fs/common.rs | 0 {crates => library}/std/src/sys/fs/mod.rs | 0 .../std/src/sys/fs/unsupported.rs | 0 .../std/src/sys/helpers/mod.rs | 0 .../std/src/sys/helpers/small_c_string.rs | 0 .../std/src/sys/helpers/tests.rs | 0 .../std/src/sys/helpers/wstr.rs | 0 .../std/src/sys/io/error/generic.rs | 0 .../std/src/sys/io/error/mod.rs | 0 .../std/src/sys/io/io_slice/unsupported.rs | 0 .../std/src/sys/io/is_terminal/unsupported.rs | 0 .../std/src/sys/io/kernel_copy/mod.rs | 0 {crates => library}/std/src/sys/io/mod.rs | 0 {crates => library}/std/src/sys/mod.rs | 0 .../std/src/sys/net/connection/mod.rs | 0 .../std/src/sys/net/connection/unsupported.rs | 0 .../std/src/sys/net/hostname/mod.rs | 0 .../std/src/sys/net/hostname/unsupported.rs | 0 {crates => library}/std/src/sys/net/mod.rs | 0 .../std/src/sys/os_str/bytes.rs | 0 .../std/src/sys/os_str/bytes/tests.rs | 0 {crates => library}/std/src/sys/os_str/mod.rs | 0 {crates => library}/std/src/sys/pal/mod.rs | 6 + library/std/src/sys/pal/survos.rs | 11 + .../std/src/sys/pal/unsupported/common.rs | 0 .../std/src/sys/pal/unsupported/mod.rs | 0 .../std/src/sys/pal/unsupported/os.rs | 0 {crates => library}/std/src/sys/path/mod.rs | 0 {crates => library}/std/src/sys/path/unix.rs | 0 .../std/src/sys/personality/dwarf/eh.rs | 0 .../std/src/sys/personality/dwarf/mod.rs | 0 .../std/src/sys/personality/dwarf/tests.rs | 0 .../std/src/sys/personality/mod.rs | 0 {crates => library}/std/src/sys/pipe/mod.rs | 0 .../std/src/sys/pipe/unsupported.rs | 0 .../std/src/sys/platform_version/mod.rs | 0 .../std/src/sys/process/env.rs | 0 .../std/src/sys/process/mod.rs | 0 .../std/src/sys/process/unsupported.rs | 0 {crates => library}/std/src/sys/random/mod.rs | 0 .../std/src/sys/random/unsupported.rs | 0 {crates => library}/std/src/sys/stdio/mod.rs | 0 .../std/src/sys/stdio/unsupported.rs | 0 .../std/src/sys/sync/condvar/mod.rs | 0 .../std/src/sys/sync/condvar/no_threads.rs | 0 {crates => library}/std/src/sys/sync/mod.rs | 0 .../std/src/sys/sync/mutex/mod.rs | 0 .../std/src/sys/sync/mutex/no_threads.rs | 0 .../std/src/sys/sync/once/mod.rs | 0 .../std/src/sys/sync/once/no_threads.rs | 0 .../std/src/sys/sync/once_box.rs | 0 .../std/src/sys/sync/rwlock/mod.rs | 0 .../std/src/sys/sync/rwlock/no_threads.rs | 0 .../std/src/sys/sync/thread_parking/mod.rs | 0 .../sys/sync/thread_parking/unsupported.rs | 0 {crates => library}/std/src/sys/thread/mod.rs | 0 .../std/src/sys/thread/unsupported.rs | 0 .../std/src/sys/thread_local/mod.rs | 0 .../std/src/sys/thread_local/no_threads.rs | 0 .../std/src/sys/thread_local/os.rs | 0 {crates => library}/std/src/sys/time/mod.rs | 0 .../std/src/sys/time/unsupported.rs | 0 library/std/src/thread | 1 + {crates => library}/std/src/time.rs | 0 library/test | 1 + sysroot/lib/rustlib/src/rust/library/library | 1 + user/shell/Cargo.toml | 4 +- user/shell/src/main.rs | 2 - user/test_pic/Cargo.toml | 4 +- user/test_pic/src/main.rs | 10 +- 297 files changed, 598 insertions(+), 60308 deletions(-) delete mode 100644 crates/os-std-macros/Cargo.toml delete mode 100644 crates/os-std-macros/src/lib.rs delete mode 100644 crates/std/Cargo.toml delete mode 100644 crates/std/crates/panic_abort/Cargo.toml delete mode 100644 crates/std/crates/panic_abort/src/android.rs delete mode 100644 crates/std/crates/panic_abort/src/lib.rs delete mode 100644 crates/std/crates/panic_abort/src/zkvm.rs delete mode 100644 crates/std/crates/std_detect/Cargo.toml delete mode 100644 crates/std/crates/std_detect/README.md delete mode 100644 crates/std/crates/std_detect/src/detect/arch/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/arm.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/loongarch.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/mips.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/mips64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/mod.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/powerpc.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/powerpc64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/riscv.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/s390x.rs delete mode 100644 crates/std/crates/std_detect/src/detect/arch/x86.rs delete mode 100644 crates/std/crates/std_detect/src/detect/bit.rs delete mode 100644 crates/std/crates/std_detect/src/detect/cache.rs delete mode 100644 crates/std/crates/std_detect/src/detect/macros.rs delete mode 100644 crates/std/crates/std_detect/src/detect/mod.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/freebsd/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/freebsd/powerpc.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/aarch64/tests.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/arm.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/loongarch.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/mips.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/mod.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/riscv.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/linux/s390x.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/openbsd/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/openbsd/powerpc.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/other.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/riscv.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/riscv/tests.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs delete mode 100644 crates/std/crates/std_detect/src/detect/os/x86.rs delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/linux-artificial-aarch64.auxv delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/linux-hwcap2-aarch64.auxv delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/linux-no-hwcap2-aarch64.auxv delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/linux-rpi3.auxv delete mode 100644 crates/std/crates/std_detect/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv delete mode 100644 crates/std/crates/std_detect/src/lib.rs delete mode 100644 crates/std/crates/std_detect/tests/cpu-detection.rs delete mode 100644 crates/std/crates/std_detect/tests/macro_trailing_commas.rs delete mode 100644 crates/std/crates/std_detect/tests/x86-specific.rs delete mode 100644 crates/std/patches.sed delete mode 100644 crates/std/patches/collections/hash/map.sed delete mode 100644 crates/std/patches/collections/hash/set.sed delete mode 100644 crates/std/patches/sys/alloc/mod.sed delete mode 100644 crates/std/src/backtrace/tests.rs delete mode 100644 crates/std/src/collections/hash/map.rs delete mode 100644 crates/std/src/collections/hash/map/tests.rs delete mode 100644 crates/std/src/collections/hash/mod.rs delete mode 100644 crates/std/src/collections/hash/set.rs delete mode 100644 crates/std/src/collections/hash/set/tests.rs delete mode 100644 crates/std/src/collections/mod.rs delete mode 100644 crates/std/src/ffi/c_str.rs delete mode 100644 crates/std/src/ffi/mod.rs delete mode 100644 crates/std/src/ffi/os_str.rs delete mode 100644 crates/std/src/ffi/os_str/tests.rs delete mode 100644 crates/std/src/fs/tests.rs delete mode 100644 crates/std/src/hash/mod.rs delete mode 100644 crates/std/src/hash/random.rs delete mode 100644 crates/std/src/io/buffered/bufreader.rs delete mode 100644 crates/std/src/io/buffered/bufreader/buffer.rs delete mode 100644 crates/std/src/io/buffered/bufwriter.rs delete mode 100644 crates/std/src/io/buffered/linewriter.rs delete mode 100644 crates/std/src/io/buffered/linewritershim.rs delete mode 100644 crates/std/src/io/buffered/mod.rs delete mode 100644 crates/std/src/io/buffered/tests.rs delete mode 100644 crates/std/src/io/copy.rs delete mode 100644 crates/std/src/io/copy/tests.rs delete mode 100644 crates/std/src/io/cursor.rs delete mode 100644 crates/std/src/io/cursor/tests.rs delete mode 100644 crates/std/src/io/error.rs delete mode 100644 crates/std/src/io/error/repr_bitpacked.rs delete mode 100644 crates/std/src/io/error/repr_unpacked.rs delete mode 100644 crates/std/src/io/error/tests.rs delete mode 100644 crates/std/src/io/impls.rs delete mode 100644 crates/std/src/io/impls/tests.rs delete mode 100644 crates/std/src/io/mod.rs delete mode 100644 crates/std/src/io/pipe.rs delete mode 100644 crates/std/src/io/pipe/tests.rs delete mode 100644 crates/std/src/io/prelude.rs delete mode 100644 crates/std/src/io/stdio.rs delete mode 100644 crates/std/src/io/stdio/tests.rs delete mode 100644 crates/std/src/io/tests.rs delete mode 100644 crates/std/src/io/util.rs delete mode 100644 crates/std/src/io/util/tests.rs delete mode 100644 crates/std/src/net/hostname.rs delete mode 100644 crates/std/src/net/ip_addr.rs delete mode 100644 crates/std/src/net/ip_addr/tests.rs delete mode 100644 crates/std/src/net/mod.rs delete mode 100644 crates/std/src/net/socket_addr.rs delete mode 100644 crates/std/src/net/socket_addr/tests.rs delete mode 100644 crates/std/src/net/tcp.rs delete mode 100644 crates/std/src/net/tcp/tests.rs delete mode 100644 crates/std/src/net/test.rs delete mode 100644 crates/std/src/net/udp.rs delete mode 100644 crates/std/src/net/udp/tests.rs delete mode 100644 crates/std/src/num/f128.rs delete mode 100644 crates/std/src/num/f16.rs delete mode 100644 crates/std/src/num/f32.rs delete mode 100644 crates/std/src/num/f64.rs delete mode 100644 crates/std/src/num/mod.rs delete mode 100644 crates/std/src/prelude/mod.rs delete mode 100644 crates/std/src/prelude/v1.rs delete mode 100644 crates/std/src/process/tests.rs delete mode 100644 crates/std/src/sync/barrier.rs delete mode 100644 crates/std/src/sync/lazy_lock.rs delete mode 100644 crates/std/src/sync/mod.rs delete mode 100644 crates/std/src/sync/mpmc/array.rs delete mode 100644 crates/std/src/sync/mpmc/context.rs delete mode 100644 crates/std/src/sync/mpmc/counter.rs delete mode 100644 crates/std/src/sync/mpmc/error.rs delete mode 100644 crates/std/src/sync/mpmc/list.rs delete mode 100644 crates/std/src/sync/mpmc/mod.rs delete mode 100644 crates/std/src/sync/mpmc/select.rs delete mode 100644 crates/std/src/sync/mpmc/tests.rs delete mode 100644 crates/std/src/sync/mpmc/utils.rs delete mode 100644 crates/std/src/sync/mpmc/waker.rs delete mode 100644 crates/std/src/sync/mpmc/zero.rs delete mode 100644 crates/std/src/sync/mpsc.rs delete mode 100644 crates/std/src/sync/nonpoison.rs delete mode 100644 crates/std/src/sync/nonpoison/condvar.rs delete mode 100644 crates/std/src/sync/nonpoison/mutex.rs delete mode 100644 crates/std/src/sync/nonpoison/rwlock.rs delete mode 100644 crates/std/src/sync/once.rs delete mode 100644 crates/std/src/sync/once_lock.rs delete mode 100644 crates/std/src/sync/oneshot.rs delete mode 100644 crates/std/src/sync/poison.rs delete mode 100644 crates/std/src/sync/poison/condvar.rs delete mode 100644 crates/std/src/sync/poison/mutex.rs delete mode 100644 crates/std/src/sync/poison/rwlock.rs delete mode 100644 crates/std/src/sync/reentrant_lock.rs delete mode 100644 crates/std/src/sys/alloc/mod.rs delete mode 100644 crates/std/src/sys/alloc/survos.rs delete mode 100644 crates/std/src/thread/builder.rs delete mode 100644 crates/std/src/thread/current.rs delete mode 100644 crates/std/src/thread/functions.rs delete mode 100644 crates/std/src/thread/id.rs delete mode 100644 crates/std/src/thread/join_handle.rs delete mode 100644 crates/std/src/thread/lifecycle.rs delete mode 100644 crates/std/src/thread/local.rs delete mode 100644 crates/std/src/thread/main_thread.rs delete mode 100644 crates/std/src/thread/mod.rs delete mode 100644 crates/std/src/thread/scoped.rs delete mode 100644 crates/std/src/thread/spawnhook.rs delete mode 100644 crates/std/src/thread/tests.rs delete mode 100644 crates/std/src/thread/thread.rs create mode 100644 library/.gitignore create mode 100644 library/Cargo.toml rename crates/std/crates/backtrace-rs => library/backtrace (100%) rename {crates/std => library}/justfile (51%) create mode 120000 library/proc_macro create mode 120000 library/profiler_builtins create mode 100644 library/std/Cargo.toml rename {crates => library}/std/build.rs (100%) rename {crates => library}/std/patches/sys/args/mod.sed (100%) rename {crates => library}/std/patches/sys/io/error/mod.sed (100%) create mode 100644 library/std/patches/sys/pal/mod.sed rename {crates => library}/std/patches/sys/random/mod.sed (100%) rename {crates => library}/std/patches/sys/thread_local/mod.sed (100%) create mode 100644 library/std/riscv64.json rename {crates => library}/std/src/alloc.rs (100%) rename {crates => library}/std/src/ascii.rs (100%) create mode 120000 library/std/src/backtrace rename {crates => library}/std/src/backtrace.rs (100%) rename {crates => library}/std/src/bstr.rs (70%) create mode 120000 library/std/src/collections rename {crates => library}/std/src/env.rs (100%) rename {crates => library}/std/src/error.rs (99%) create mode 120000 library/std/src/ffi create mode 120000 library/std/src/fs rename {crates => library}/std/src/fs.rs (100%) create mode 120000 library/std/src/hash create mode 120000 library/std/src/io rename {crates => library}/std/src/keyword_docs.rs (100%) rename {crates => library}/std/src/lib.rs (78%) rename {crates => library}/std/src/macros.rs (99%) create mode 120000 library/std/src/net create mode 120000 library/std/src/num rename {crates => library}/std/src/os/mod.rs (100%) rename {crates => library}/std/src/os/raw/mod.rs (84%) rename {crates => library}/std/src/os/raw/tests.rs (100%) rename {crates => library}/std/src/panic.rs (100%) rename {crates => library}/std/src/panicking.rs (100%) rename {crates => library}/std/src/pat.rs (100%) rename {crates => library}/std/src/path.rs (100%) create mode 120000 library/std/src/prelude create mode 120000 library/std/src/process rename {crates => library}/std/src/process.rs (100%) rename {crates => library}/std/src/random.rs (100%) rename {crates => library}/std/src/rt.rs (100%) create mode 120000 library/std/src/sync rename {crates => library}/std/src/sys/args/mod.rs (100%) rename {crates => library}/std/src/sys/args/unsupported.rs (100%) rename {crates => library}/std/src/sys/backtrace.rs (100%) rename {crates => library}/std/src/sys/cmath.rs (100%) rename {crates => library}/std/src/sys/configure_builtins.rs (100%) rename {crates => library}/std/src/sys/env/common.rs (100%) rename {crates => library}/std/src/sys/env/mod.rs (100%) rename {crates => library}/std/src/sys/env/unsupported.rs (100%) rename {crates => library}/std/src/sys/env_consts.rs (100%) rename {crates => library}/std/src/sys/exit.rs (100%) rename {crates => library}/std/src/sys/fd/mod.rs (100%) rename {crates => library}/std/src/sys/fs/common.rs (100%) rename {crates => library}/std/src/sys/fs/mod.rs (100%) rename {crates => library}/std/src/sys/fs/unsupported.rs (100%) rename {crates => library}/std/src/sys/helpers/mod.rs (100%) rename {crates => library}/std/src/sys/helpers/small_c_string.rs (100%) rename {crates => library}/std/src/sys/helpers/tests.rs (100%) rename {crates => library}/std/src/sys/helpers/wstr.rs (100%) rename {crates => library}/std/src/sys/io/error/generic.rs (100%) rename {crates => library}/std/src/sys/io/error/mod.rs (100%) rename {crates => library}/std/src/sys/io/io_slice/unsupported.rs (100%) rename {crates => library}/std/src/sys/io/is_terminal/unsupported.rs (100%) rename {crates => library}/std/src/sys/io/kernel_copy/mod.rs (100%) rename {crates => library}/std/src/sys/io/mod.rs (100%) rename {crates => library}/std/src/sys/mod.rs (100%) rename {crates => library}/std/src/sys/net/connection/mod.rs (100%) rename {crates => library}/std/src/sys/net/connection/unsupported.rs (100%) rename {crates => library}/std/src/sys/net/hostname/mod.rs (100%) rename {crates => library}/std/src/sys/net/hostname/unsupported.rs (100%) rename {crates => library}/std/src/sys/net/mod.rs (100%) rename {crates => library}/std/src/sys/os_str/bytes.rs (100%) rename {crates => library}/std/src/sys/os_str/bytes/tests.rs (100%) rename {crates => library}/std/src/sys/os_str/mod.rs (100%) rename {crates => library}/std/src/sys/pal/mod.rs (90%) create mode 100644 library/std/src/sys/pal/survos.rs rename {crates => library}/std/src/sys/pal/unsupported/common.rs (100%) rename {crates => library}/std/src/sys/pal/unsupported/mod.rs (100%) rename {crates => library}/std/src/sys/pal/unsupported/os.rs (100%) rename {crates => library}/std/src/sys/path/mod.rs (100%) rename {crates => library}/std/src/sys/path/unix.rs (100%) rename {crates => library}/std/src/sys/personality/dwarf/eh.rs (100%) rename {crates => library}/std/src/sys/personality/dwarf/mod.rs (100%) rename {crates => library}/std/src/sys/personality/dwarf/tests.rs (100%) rename {crates => library}/std/src/sys/personality/mod.rs (100%) rename {crates => library}/std/src/sys/pipe/mod.rs (100%) rename {crates => library}/std/src/sys/pipe/unsupported.rs (100%) rename {crates => library}/std/src/sys/platform_version/mod.rs (100%) rename {crates => library}/std/src/sys/process/env.rs (100%) rename {crates => library}/std/src/sys/process/mod.rs (100%) rename {crates => library}/std/src/sys/process/unsupported.rs (100%) rename {crates => library}/std/src/sys/random/mod.rs (100%) rename {crates => library}/std/src/sys/random/unsupported.rs (100%) rename {crates => library}/std/src/sys/stdio/mod.rs (100%) rename {crates => library}/std/src/sys/stdio/unsupported.rs (100%) rename {crates => library}/std/src/sys/sync/condvar/mod.rs (100%) rename {crates => library}/std/src/sys/sync/condvar/no_threads.rs (100%) rename {crates => library}/std/src/sys/sync/mod.rs (100%) rename {crates => library}/std/src/sys/sync/mutex/mod.rs (100%) rename {crates => library}/std/src/sys/sync/mutex/no_threads.rs (100%) rename {crates => library}/std/src/sys/sync/once/mod.rs (100%) rename {crates => library}/std/src/sys/sync/once/no_threads.rs (100%) rename {crates => library}/std/src/sys/sync/once_box.rs (100%) rename {crates => library}/std/src/sys/sync/rwlock/mod.rs (100%) rename {crates => library}/std/src/sys/sync/rwlock/no_threads.rs (100%) rename {crates => library}/std/src/sys/sync/thread_parking/mod.rs (100%) rename {crates => library}/std/src/sys/sync/thread_parking/unsupported.rs (100%) rename {crates => library}/std/src/sys/thread/mod.rs (100%) rename {crates => library}/std/src/sys/thread/unsupported.rs (100%) rename {crates => library}/std/src/sys/thread_local/mod.rs (100%) rename {crates => library}/std/src/sys/thread_local/no_threads.rs (100%) rename {crates => library}/std/src/sys/thread_local/os.rs (100%) rename {crates => library}/std/src/sys/time/mod.rs (100%) rename {crates => library}/std/src/sys/time/unsupported.rs (100%) create mode 120000 library/std/src/thread rename {crates => library}/std/src/time.rs (100%) create mode 120000 library/test create mode 120000 sysroot/lib/rustlib/src/rust/library/library diff --git a/.cargo/config.toml b/.cargo/config.toml index f072b50..05b1498 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,8 +3,8 @@ target = "riscv64.json" [unstable] json-target-spec = true -build-std = ["core", "compiler_builtins", "alloc"] -build-std-features = ["compiler-builtins-mem"] +# build-std = [] +# build-std-features = ["compiler-builtins-mem"] [target.riscv64] rustflags = [ diff --git a/.gitignore b/.gitignore index 55983a2..6ea937b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ disk.img **/*.mem mnt + +sysroot/lib/rustlib/riscv64 diff --git a/.gitmodules b/.gitmodules index d11b511..22a9e10 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "backtrace-rs"] - path = backtrace-rs - url = https://github.com/rust-lang/backtrace-rs.git -[submodule "crates/std/crates/backtrace-rs"] - path = crates/std/crates/backtrace-rs +[submodule "library/backtrace"] + path = library/backtrace url = https://github.com/rust-lang/backtrace-rs.git diff --git a/Cargo.toml b/Cargo.toml index b52f444..3840326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "3" -members = ["crates/bytes-struct","crates/io", "crates/shared", "user/*"] -exclude = ["crates/std/crates/backtrace-rs"] +members = [ "user/*"] +exclude = ["library"] [package] name = "kernel-rust" diff --git a/crates/os-std-macros/Cargo.toml b/crates/os-std-macros/Cargo.toml deleted file mode 100644 index 77bc004..0000000 --- a/crates/os-std-macros/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "os-std-macros" -version = "0.1.0" -edition = "2024" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1" -quote = "1" -syn = { version = "2", features = ["full"] } diff --git a/crates/os-std-macros/src/lib.rs b/crates/os-std-macros/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/crates/os-std-macros/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/std/Cargo.toml b/crates/std/Cargo.toml deleted file mode 100644 index 5837b57..0000000 --- a/crates/std/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "std" -version = "0.1.0" -edition = "2024" - -[dependencies] -cfg-if = { version = "1.0" } -rustc-demangle = { version = "0.1.27" } -std_detect = { path = "crates/std_detect" } -panic_abort = { path = "crates/panic_abort" } -hashbrown = { version = "0.16.1", default-features = false, features = ["nightly", "rustc-internal-api"] } -os-std-macros = { path = "../os-std-macros" } -shared = { path = "../shared", features = ["user"] } -io_crate = { package = "io", path = "../io", features = ["alloc"] } diff --git a/crates/std/crates/panic_abort/Cargo.toml b/crates/std/crates/panic_abort/Cargo.toml deleted file mode 100644 index d10834b..0000000 --- a/crates/std/crates/panic_abort/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "panic_abort" -version = "0.0.0" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/rust.git" -description = "Implementation of Rust panics via process aborts" -edition = "2024" - -[lib] -test = false -bench = false -doc = false - -[dependencies] - -[target.'cfg(target_os = "android")'.dependencies] -libc = { version = "0.2", default-features = false } - -[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies] diff --git a/crates/std/crates/panic_abort/src/android.rs b/crates/std/crates/panic_abort/src/android.rs deleted file mode 100644 index 1cc2077..0000000 --- a/crates/std/crates/panic_abort/src/android.rs +++ /dev/null @@ -1,51 +0,0 @@ -use alloc::string::String; -use core::mem::transmute; -use core::panic::PanicPayload; -use core::ptr::copy_nonoverlapping; - -const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0"; -type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> (); - -// Forward the abort message to libc's android_set_abort_message. We try our best to populate the -// message but as this function may already be called as part of a failed allocation, it might not be -// possible to do so. -// -// Some methods of core are on purpose avoided (such as try_reserve) as these rely on the correct -// resolution of rust_eh_personality which is loosely defined in panic_abort. -// -// Weakly resolve the symbol for android_set_abort_message. This function is only available -// for API >= 21. -pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn PanicPayload) { - let func_addr = unsafe { - libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char) - as usize - }; - if func_addr == 0 { - return; - } - - let payload = payload.get(); - let msg = match payload.downcast_ref::<&'static str>() { - Some(msg) => msg.as_bytes(), - None => match payload.downcast_ref::() { - Some(msg) => msg.as_bytes(), - None => &[], - }, - }; - if msg.is_empty() { - return; - } - - // Allocate a new buffer to append the null byte. - let size = msg.len() + 1usize; - let buf = unsafe { libc::malloc(size) as *mut libc::c_char }; - if buf.is_null() { - return; // allocation failure - } - unsafe { - copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, msg.len()); - buf.add(msg.len()).write(0); - let func = transmute::(func_addr); - func(buf); - } -} diff --git a/crates/std/crates/panic_abort/src/lib.rs b/crates/std/crates/panic_abort/src/lib.rs deleted file mode 100644 index d1706b6..0000000 --- a/crates/std/crates/panic_abort/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Implementation of Rust panics via process aborts -//! -//! When compared to the implementation via unwinding, this crate is *much* -//! simpler! That being said, it's not quite as versatile, but here goes! - -#![no_std] -#![unstable(feature = "panic_abort", issue = "32837")] -#![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] -#![panic_runtime] -#![feature(panic_runtime)] -#![feature(std_internals)] -#![feature(staged_api)] -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[cfg(target_os = "android")] -mod android; - -#[cfg(target_os = "zkvm")] -mod zkvm; - -use core::any::Any; -use core::panic::PanicPayload; - -#[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { - unreachable!() -} - -// "Leak" the payload and shim to the relevant abort on the platform in question. -#[rustc_std_internal_symbol] -pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { - // Android has the ability to attach a message as part of the abort. - #[cfg(target_os = "android")] - unsafe { - android::android_set_abort_message(_payload); - } - #[cfg(target_os = "zkvm")] - unsafe { - zkvm::zkvm_set_abort_message(_payload); - } - - unsafe extern "Rust" { - // This is defined in std::rt. - #[rustc_std_internal_symbol] - safe fn __rust_abort() -> !; - } - - __rust_abort() -} - -// This... is a bit of an oddity. The tl;dr; is that this is required to link -// correctly, the longer explanation is below. -// -// Right now the binaries of core/std that we ship are all compiled with -// `-C panic=unwind`. This is done to ensure that the binaries are maximally -// compatible with as many situations as possible. The compiler, however, -// requires a "personality function" for all functions compiled with `-C -// panic=unwind`. This personality function is hardcoded to the symbol -// `rust_eh_personality` and is defined by the `eh_personality` lang item. -// -// So... why not just define that lang item here? Good question! The way that -// panic runtimes are linked in is actually a little subtle in that they're -// "sort of" in the compiler's crate store, but only actually linked if another -// isn't actually linked. This ends up meaning that both this crate and the -// panic_unwind crate can appear in the compiler's crate store, and if both -// define the `eh_personality` lang item then that'll hit an error. -// -// To handle this the compiler only requires the `eh_personality` is defined if -// the panic runtime being linked in is the unwinding runtime, and otherwise -// it's not required to be defined (rightfully so). In this case, however, this -// library just defines this symbol so there's at least some personality -// somewhere. -// -// Essentially this symbol is just defined to get wired up to core/std -// binaries, but it should never be called as we don't link in an unwinding -// runtime at all. -pub mod personalities { - // In the past this module used to contain stubs for the personality - // functions of various platforms, but these where removed when personality - // functions were moved to std. - - // This corresponds to the `eh_catch_typeinfo` lang item - // that's only used on Emscripten currently. - // - // Since panics don't generate exceptions and foreign exceptions are - // currently UB with -C panic=abort (although this may be subject to - // change), any catch_unwind calls will never use this typeinfo. - #[rustc_std_internal_symbol] - #[allow(non_upper_case_globals)] - #[cfg(target_os = "emscripten")] - static rust_eh_catch_typeinfo: [usize; 2] = [0; 2]; -} diff --git a/crates/std/crates/panic_abort/src/zkvm.rs b/crates/std/crates/panic_abort/src/zkvm.rs deleted file mode 100644 index 7b1e89c..0000000 --- a/crates/std/crates/panic_abort/src/zkvm.rs +++ /dev/null @@ -1,26 +0,0 @@ -use alloc::string::String; -use core::panic::PanicPayload; - -// Forward the abort message to zkVM's sys_panic. This is implemented by RISC Zero's -// platform crate which exposes system calls specifically for the zkVM. -pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { - let payload = payload.get(); - let msg = match payload.downcast_ref::<&'static str>() { - Some(msg) => msg.as_bytes(), - None => match payload.downcast_ref::() { - Some(msg) => msg.as_bytes(), - None => &[], - }, - }; - if msg.is_empty() { - return; - } - - unsafe extern "C" { - fn sys_panic(msg_ptr: *const u8, len: usize) -> !; - } - - unsafe { - sys_panic(msg.as_ptr(), msg.len()); - } -} diff --git a/crates/std/crates/std_detect/Cargo.toml b/crates/std/crates/std_detect/Cargo.toml deleted file mode 100644 index 4153e2f..0000000 --- a/crates/std/crates/std_detect/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "std_detect" -version = "0.1.5" -authors = [ - "Alex Crichton ", - "Andrew Gallant ", - "Gonzalo Brito Gadeschi ", -] -description = "`std::detect` - Rust's standard library run-time CPU feature detection." -license = "MIT OR Apache-2.0" -edition = "2024" - -[badges] -is-it-maintained-issue-resolution = { repository = "rust-lang/stdarch" } -is-it-maintained-open-issues = { repository = "rust-lang/stdarch" } -maintenance = { status = "experimental" } - -[dependencies] - -[target.'cfg(not(windows))'.dependencies] -libc = { version = "0.2.0", default-features = false } - -[features] -std_detect_env_override = [] diff --git a/crates/std/crates/std_detect/README.md b/crates/std/crates/std_detect/README.md deleted file mode 100644 index 895f342..0000000 --- a/crates/std/crates/std_detect/README.md +++ /dev/null @@ -1,69 +0,0 @@ -`std::detect` - Rust's standard library run-time CPU feature detection -======= - -The private `std::detect` module implements run-time feature detection in Rust's -standard library. This allows detecting whether the CPU the binary runs on -supports certain features, like SIMD instructions. - -# Usage - -`std::detect` APIs are available as part of `libstd`. Prefer using it via the -standard library than through this crate. Unstable features of `std::detect` are -available on nightly Rust behind various feature-gates. - -If you need run-time feature detection in `#[no_std]` environments, Rust `core` -library cannot help you. By design, Rust `core` is platform independent, but -performing run-time feature detection requires a certain level of cooperation -from the platform. - -You can then manually include `std_detect` as a dependency to get similar -run-time feature detection support than the one offered by Rust's standard -library. We intend to make `std_detect` more flexible and configurable in this -regard to better serve the needs of `#[no_std]` targets. - -# Platform support - -* All `x86`/`x86_64` targets are supported on all platforms by querying the - `cpuid` instruction directly for the features supported by the hardware and - the operating system. `std_detect` assumes that the binary is an user-space - application. - -* Linux/Android: - * `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `loongarch{32,64}`, `s390x`: - `std_detect` supports these on Linux by querying ELF auxiliary vectors (using `getauxval` - when available), and if that fails, by querying `/proc/self/auxv`. - * `arm64`: partial support for doing run-time feature detection by directly - querying `mrs` is implemented for Linux >= 4.11, but not enabled by default. - * `riscv{32,64}`: - `std_detect` supports these on Linux by querying `riscv_hwprobe`, and - by querying ELF auxiliary vectors (using `getauxval` when available). - -* FreeBSD: - * `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF - auxiliary vectors using `elf_aux_info`. - * `arm64`: run-time feature detection is implemented by directly querying `mrs`. - -* OpenBSD: - * `powerpc64`: `std_detect` supports these on OpenBSD by querying ELF auxiliary - vectors using `elf_aux_info`. - * `arm64`: run-time feature detection is implemented by querying `sysctl`. - -* Windows: - * `arm64`: run-time feature detection is implemented by querying `IsProcessorFeaturePresent`. - -# License - -This project is licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - http://opensource.org/licenses/MIT) - -at your option. - -# Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in `std_detect` by you, as defined in the Apache-2.0 license, -shall be dual licensed as above, without any additional terms or conditions. diff --git a/crates/std/crates/std_detect/src/detect/arch/aarch64.rs b/crates/std/crates/std_detect/src/detect/arch/aarch64.rs deleted file mode 100644 index 5e85e96..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/aarch64.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! Aarch64 run-time features. - -features! { - @TARGET: aarch64; - @CFG: any(target_arch = "aarch64", target_arch = "arm64ec"); - @MACRO_NAME: is_aarch64_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - /// - /// This macro takes one argument which is a string literal of the feature being tested for. - /// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture - /// Reference Manual][docs]. - /// - /// Currently most features are only supported on linux-based platforms: on other platforms the - /// runtime check will always return `false`. - /// - /// ## Supported arguments - /// - /// * `"aes"` - FEAT_AES & FEAT_PMULL - /// * `"asimd"` or "neon" - FEAT_AdvSIMD - /// * `"bf16"` - FEAT_BF16 - /// * `"bti"` - FEAT_BTI - /// * `"crc"` - FEAT_CRC - /// * `"cssc"` - FEAT_CSSC - /// * `"dit"` - FEAT_DIT - /// * `"dotprod"` - FEAT_DotProd - /// * `"dpb"` - FEAT_DPB - /// * `"dpb2"` - FEAT_DPB2 - /// * `"ecv"` - FEAT_ECV - /// * `"f32mm"` - FEAT_F32MM - /// * `"f64mm"` - FEAT_F64MM - /// * `"faminmax"` - FEAT_FAMINMAX - /// * `"fcma"` - FEAT_FCMA - /// * `"fhm"` - FEAT_FHM - /// * `"flagm"` - FEAT_FLAGM - /// * `"flagm2"` - FEAT_FLAGM2 - /// * `"fp"` - FEAT_FP - /// * `"fp16"` - FEAT_FP16 - /// * `"fp8"` - FEAT_FP8 - /// * `"fp8dot2"` - FEAT_FP8DOT2 - /// * `"fp8dot4"` - FEAT_FP8DOT4 - /// * `"fp8fma"` - FEAT_FP8FMA - /// * `"fpmr"` - FEAT_FPMR - /// * `"frintts"` - FEAT_FRINTTS - /// * `"hbc"` - FEAT_HBC - /// * `"i8mm"` - FEAT_I8MM - /// * `"jsconv"` - FEAT_JSCVT - /// * `"lse"` - FEAT_LSE - /// * `"lse128"` - FEAT_LSE128 - /// * `"lse2"` - FEAT_LSE2 - /// * `"lut"` - FEAT_LUT - /// * `"mops"` - FEAT_MOPS - /// * `"mte"` - FEAT_MTE & FEAT_MTE2 - /// * `"paca"` - FEAT_PAuth (address authentication) - /// * `"pacg"` - FEAT_Pauth (generic authentication) - /// * `"pauth-lr"` - FEAT_PAuth_LR - /// * `"pmull"` - FEAT_PMULL - /// * `"rand"` - FEAT_RNG - /// * `"rcpc"` - FEAT_LRCPC - /// * `"rcpc2"` - FEAT_LRCPC2 - /// * `"rcpc3"` - FEAT_LRCPC3 - /// * `"rdm"` - FEAT_RDM - /// * `"sb"` - FEAT_SB - /// * `"sha2"` - FEAT_SHA1 & FEAT_SHA256 - /// * `"sha3"` - FEAT_SHA512 & FEAT_SHA3 - /// * `"sm4"` - FEAT_SM3 & FEAT_SM4 - /// * `"sme"` - FEAT_SME - /// * `"sme-b16b16"` - FEAT_SME_B16B16 - /// * `"sme-f16f16"` - FEAT_SME_F16F16 - /// * `"sme-f64f64"` - FEAT_SME_F64F64 - /// * `"sme-f8f16"` - FEAT_SME_F8F16 - /// * `"sme-f8f32"` - FEAT_SME_F8F32 - /// * `"sme-fa64"` - FEAT_SME_FA64 - /// * `"sme-i16i64"` - FEAT_SME_I16I64 - /// * `"sme-lutv2"` - FEAT_SME_LUTv2 - /// * `"sme2"` - FEAT_SME2 - /// * `"sme2p1"` - FEAT_SME2p1 - /// * `"ssbs"` - FEAT_SSBS & FEAT_SSBS2 - /// * `"ssve-fp8dot2"` - FEAT_SSVE_FP8DOT2 - /// * `"ssve-fp8dot4"` - FEAT_SSVE_FP8DOT4 - /// * `"ssve-fp8fma"` - FEAT_SSVE_FP8FMA - /// * `"sve"` - FEAT_SVE - /// * `"sve-b16b16"` - FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - /// * `"sve2"` - FEAT_SVE2 - /// * `"sve2-aes"` - FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto) - /// * `"sve2-bitperm"` - FEAT_SVE2_BitPerm - /// * `"sve2-sha3"` - FEAT_SVE2_SHA3 - /// * `"sve2-sm4"` - FEAT_SVE2_SM4 - /// * `"sve2p1"` - FEAT_SVE2p1 - /// * `"tme"` - FEAT_TME - /// * `"wfxt"` - FEAT_WFxT - /// - /// [docs]: https://developer.arm.com/documentation/ddi0487/latest - #[stable(feature = "simd_aarch64", since = "1.60.0")] - @BIND_FEATURE_NAME: "asimd"; "neon"; - @NO_RUNTIME_DETECTION: "ras"; - @NO_RUNTIME_DETECTION: "v8.1a"; - @NO_RUNTIME_DETECTION: "v8.2a"; - @NO_RUNTIME_DETECTION: "v8.3a"; - @NO_RUNTIME_DETECTION: "v8.4a"; - @NO_RUNTIME_DETECTION: "v8.5a"; - @NO_RUNTIME_DETECTION: "v8.6a"; - @NO_RUNTIME_DETECTION: "v8.7a"; - @NO_RUNTIME_DETECTION: "v8.8a"; - @NO_RUNTIME_DETECTION: "v8.9a"; - @NO_RUNTIME_DETECTION: "v9.1a"; - @NO_RUNTIME_DETECTION: "v9.2a"; - @NO_RUNTIME_DETECTION: "v9.3a"; - @NO_RUNTIME_DETECTION: "v9.4a"; - @NO_RUNTIME_DETECTION: "v9.5a"; - @NO_RUNTIME_DETECTION: "v9a"; - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] asimd: "neon"; - /// FEAT_AdvSIMD (Advanced SIMD/NEON) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pmull: "pmull"; - implied by target_features: ["aes"]; - /// FEAT_PMULL (Polynomial Multiply) - Implied by `aes` target_feature - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp: "fp"; - implied by target_features: ["neon"]; - /// FEAT_FP (Floating point support) - Implied by `neon` target_feature - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] aes: "aes"; - /// FEAT_AES (AES SIMD instructions) & FEAT_PMULL (PMULL{2}, 64-bit operand variants) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bf16: "bf16"; - /// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bti: "bti"; - /// FEAT_BTI (Branch Target Identification) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] crc: "crc"; - /// FEAT_CRC32 (Cyclic Redundancy Check) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] cssc: "cssc"; - /// FEAT_CSSC (Common Short Sequence Compression instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dit: "dit"; - /// FEAT_DIT (Data Independent Timing instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb: "dpb"; - /// FEAT_DPB (aka dcpop - data cache clean to point of persistence) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb2: "dpb2"; - /// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dotprod: "dotprod"; - /// FEAT_DotProd (Vector Dot-Product - ASIMDDP) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ecv: "ecv"; - /// FEAT_ECV (Enhanced Counter Virtualization) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f32mm: "f32mm"; - /// FEAT_F32MM (single-precision matrix multiplication) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f64mm: "f64mm"; - /// FEAT_F64MM (double-precision matrix multiplication) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] faminmax: "faminmax"; - /// FEAT_FAMINMAX (FAMIN and FAMAX SIMD/SVE/SME instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fcma: "fcma"; - /// FEAT_FCMA (float complex number operations) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fhm: "fhm"; - /// FEAT_FHM (fp16 multiplication instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] flagm: "flagm"; - /// FEAT_FLAGM (flag manipulation instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] flagm2: "flagm2"; - /// FEAT_FLAGM2 (flag manipulation instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp16: "fp16"; - /// FEAT_FP16 (Half-float support) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8: "fp8"; - /// FEAT_FP8 (F8CVT Instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot2: "fp8dot2"; - /// FEAT_FP8DOT2 (F8DP2 Instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot4: "fp8dot4"; - /// FEAT_FP8DOT4 (F8DP4 Instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8fma: "fp8fma"; - /// FEAT_FP8FMA (F8FMA Instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fpmr: "fpmr"; - without cfg check: true; - /// FEAT_FPMR (Special-purpose AArch64-FPMR register) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] frintts: "frintts"; - /// FEAT_FRINTTS (float to integer rounding instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] hbc: "hbc"; - /// FEAT_HBC (Hinted conditional branches) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] i8mm: "i8mm"; - /// FEAT_I8MM (integer matrix multiplication, plus ASIMD support) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] jsconv: "jsconv"; - /// FEAT_JSCVT (JavaScript float conversion instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse: "lse"; - /// FEAT_LSE (Large System Extension - atomics) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lse128: "lse128"; - /// FEAT_LSE128 (128-bit atomics) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse2: "lse2"; - /// FEAT_LSE2 (unaligned and register-pair atomics) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lut: "lut"; - /// FEAT_LUT (Lookup Table Instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] mops: "mops"; - /// FEAT_MOPS (Standardization of memory operations) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] mte: "mte"; - /// FEAT_MTE & FEAT_MTE2 (Memory Tagging Extension) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] paca: "paca"; - /// FEAT_PAuth (address authentication) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pacg: "pacg"; - /// FEAT_PAuth (generic authentication) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] pauth_lr: "pauth-lr"; - /// FEAT_PAuth_LR - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rand: "rand"; - /// FEAT_RNG (Random Number Generator) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc: "rcpc"; - /// FEAT_LRCPC (Release consistent Processor consistent) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc2: "rcpc2"; - /// FEAT_LRCPC2 (RCPC with immediate offsets) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] rcpc3: "rcpc3"; - /// FEAT_LRCPC3 (RCPC Instructions v3) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rdm: "rdm"; - /// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sb: "sb"; - /// FEAT_SB (speculation barrier) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha2: "sha2"; - /// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha3: "sha3"; - /// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sm4: "sm4"; - /// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme: "sme"; - /// FEAT_SME (Scalable Matrix Extension) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2: "sme2"; - /// FEAT_SME2 (SME Version 2) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2p1: "sme2p1"; - /// FEAT_SME2p1 (SME Version 2.1) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_b16b16: "sme-b16b16"; - /// FEAT_SME_B16B16 - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f16f16: "sme-f16f16"; - /// FEAT_SME_F16F16 (Non-widening half-precision FP16 to FP16 arithmetic for SME2) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f64f64: "sme-f64f64"; - /// FEAT_SME_F64F64 (Double-precision floating-point outer product instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f16: "sme-f8f16"; - /// FEAT_SME_F8F16 - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f32: "sme-f8f32"; - /// FEAT_SME_F8F32 - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_fa64: "sme-fa64"; - /// FEAT_SME_FA64 (Full A64 instruction set support in Streaming SVE mode) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_i16i64: "sme-i16i64"; - /// FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_lutv2: "sme-lutv2"; - /// FEAT_SME_LUTv2 (LUTI4 Instruction) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] ssbs: "ssbs"; - /// FEAT_SSBS & FEAT_SSBS2 (speculative store bypass safe) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot2: "ssve-fp8dot2"; - /// FEAT_SSVE_FP8DOT2 - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot4: "ssve-fp8dot4"; - /// FEAT_SSVE_FP8DOT4 - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8fma: "ssve-fp8fma"; - /// FEAT_SSVE_FP8FMA - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve: "sve"; - /// FEAT_SVE (Scalable Vector Extension) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2: "sve2"; - /// FEAT_SVE2 (Scalable Vector Extension 2) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve2p1: "sve2p1"; - /// FEAT_SVE2p1 (Scalable Vector Extension 2.1) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_aes: "sve2-aes"; - /// FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve_b16b16: "sve-b16b16"; - /// FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_bitperm: "sve2-bitperm"; - /// FEAT_SVE_BitPerm (SVE2 bit permutation instructions) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sha3: "sve2-sha3"; - /// FEAT_SVE_SHA3 (SVE2 SHA3 crypto) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sm4: "sve2-sm4"; - /// FEAT_SVE_SM4 (SVE2 SM4 crypto) - @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] tme: "tme"; - /// FEAT_TME (Transactional Memory Extensions) - @FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] wfxt: "wfxt"; - /// FEAT_WFxT (WFET and WFIT Instructions) -} diff --git a/crates/std/crates/std_detect/src/detect/arch/arm.rs b/crates/std/crates/std_detect/src/detect/arch/arm.rs deleted file mode 100644 index 75b8ca9..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/arm.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Run-time feature detection on ARM Aarch32. - -features! { - @TARGET: arm; - @CFG: target_arch = "arm"; - @MACRO_NAME: is_arm_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] - @NO_RUNTIME_DETECTION: "v7"; - @NO_RUNTIME_DETECTION: "vfp2"; - @NO_RUNTIME_DETECTION: "vfp3"; - @NO_RUNTIME_DETECTION: "vfp4"; - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] neon: "neon"; - /// ARM Advanced SIMD (NEON) - Aarch32 - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pmull: "pmull"; - without cfg check: true; - /// Polynomial Multiply - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] crc: "crc"; - /// CRC32 (Cyclic Redundancy Check) - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] aes: "aes"; - /// FEAT_AES (AES instructions) - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] sha2: "sha2"; - /// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions) - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] i8mm: "i8mm"; - /// FEAT_I8MM (integer matrix multiplication, plus ASIMD support) - @FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] dotprod: "dotprod"; - /// FEAT_DotProd (Vector Dot-Product - ASIMDDP) -} diff --git a/crates/std/crates/std_detect/src/detect/arch/loongarch.rs b/crates/std/crates/std_detect/src/detect/arch/loongarch.rs deleted file mode 100644 index 6299627..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/loongarch.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Run-time feature detection on LoongArch. - -features! { - @TARGET: loongarch; - @CFG: any(target_arch = "loongarch32", target_arch = "loongarch64"); - @MACRO_NAME: is_loongarch_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - /// - /// Supported arguments are: - /// - /// * `"32s"` - /// * `"f"` - /// * `"d"` - /// * `"frecipe"` - /// * `"div32"` - /// * `"lsx"` - /// * `"lasx"` - /// * `"lam-bh"` - /// * `"lamcas"` - /// * `"ld-seq-sa"` - /// * `"scq"` - /// * `"lbt"` - /// * `"lvz"` - /// * `"ual"` - #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] _32s: "32s"; - /// 32S - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] f: "f"; - /// F - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] d: "d"; - /// D - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] frecipe: "frecipe"; - /// Frecipe - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] div32: "div32"; - /// Div32 - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lsx: "lsx"; - /// LSX - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lasx: "lasx"; - /// LASX - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lam_bh: "lam-bh"; - /// LAM-BH - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lamcas: "lamcas"; - /// LAM-CAS - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ld_seq_sa: "ld-seq-sa"; - /// LD-SEQ-SA - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] scq: "scq"; - /// SCQ - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lbt: "lbt"; - /// LBT - @FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lvz: "lvz"; - /// LVZ - @FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ual: "ual"; - /// UAL -} diff --git a/crates/std/crates/std_detect/src/detect/arch/mips.rs b/crates/std/crates/std_detect/src/detect/arch/mips.rs deleted file mode 100644 index 9e1960e..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/mips.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Run-time feature detection on MIPS. - -features! { - @TARGET: mips; - @CFG: target_arch = "mips"; - @MACRO_NAME: is_mips_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] - @FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa"; - /// MIPS SIMD Architecture (MSA) -} diff --git a/crates/std/crates/std_detect/src/detect/arch/mips64.rs b/crates/std/crates/std_detect/src/detect/arch/mips64.rs deleted file mode 100644 index 2bb44ba..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/mips64.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Run-time feature detection on MIPS64. - -features! { - @TARGET: mips64; - @CFG: target_arch = "mips64"; - @MACRO_NAME: is_mips64_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] - @FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa"; - /// MIPS SIMD Architecture (MSA) -} diff --git a/crates/std/crates/std_detect/src/detect/arch/mod.rs b/crates/std/crates/std_detect/src/detect/arch/mod.rs deleted file mode 100644 index 2be7f09..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -#![allow(dead_code)] - -// Export the macros for all supported architectures. -#[macro_use] -mod x86; -#[macro_use] -mod arm; -#[macro_use] -mod aarch64; -#[macro_use] -mod riscv; -#[macro_use] -mod powerpc; -#[macro_use] -mod powerpc64; -#[macro_use] -mod mips; -#[macro_use] -mod mips64; -#[macro_use] -mod loongarch; -#[macro_use] -mod s390x; - -cfg_select! { - any(target_arch = "x86", target_arch = "x86_64") => { - #[stable(feature = "simd_x86", since = "1.27.0")] - pub use x86::*; - } - target_arch = "arm" => { - #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] - pub use arm::*; - } - any(target_arch = "aarch64", target_arch = "arm64ec") => { - #[stable(feature = "simd_aarch64", since = "1.60.0")] - pub use aarch64::*; - } - any(target_arch = "riscv32", target_arch = "riscv64") => { - #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] - pub use riscv::*; - } - target_arch = "powerpc" => { - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - pub use powerpc::*; - } - target_arch = "powerpc64" => { - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - pub use powerpc64::*; - } - target_arch = "mips" => { - #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] - pub use mips::*; - } - target_arch = "mips64" => { - #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] - pub use mips64::*; - } - any(target_arch = "loongarch32", target_arch = "loongarch64") => { - #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] - pub use loongarch::*; - } - target_arch = "s390x" => { - #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] - pub use s390x::*; - } - _ => { - // Unimplemented architecture: - #[doc(hidden)] - pub(crate) enum Feature { - Null - } - #[doc(hidden)] - #[unstable(feature = "stdarch_internal", issue = "none")] - pub mod __is_feature_detected {} - - impl Feature { - #[doc(hidden)] - pub(crate) fn from_str(_s: &str) -> Result { Err(()) } - #[doc(hidden)] - pub(crate) fn to_str(self) -> &'static str { "" } - } - } -} diff --git a/crates/std/crates/std_detect/src/detect/arch/powerpc.rs b/crates/std/crates/std_detect/src/detect/arch/powerpc.rs deleted file mode 100644 index be2db0b..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/powerpc.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Run-time feature detection on PowerPC. - -features! { - @TARGET: powerpc; - @CFG: target_arch = "powerpc"; - @MACRO_NAME: is_powerpc_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec"; - /// Altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx"; - /// VSX - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8"; - without cfg check: true; - /// Power8 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec"; - /// Power8 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector"; - /// Power8 vector - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto"; - /// Power8 crypto - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9"; - without cfg check: true; - /// Power9 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec"; - /// Power9 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector"; - /// Power9 vector -} diff --git a/crates/std/crates/std_detect/src/detect/arch/powerpc64.rs b/crates/std/crates/std_detect/src/detect/arch/powerpc64.rs deleted file mode 100644 index 98e8d5f..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/powerpc64.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Run-time feature detection on PowerPC64. - -features! { - @TARGET: powerpc64; - @CFG: target_arch = "powerpc64"; - @MACRO_NAME: is_powerpc64_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec"; - /// Altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx"; - /// VSX - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8"; - without cfg check: true; - /// Power8 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec"; - /// Power8 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector"; - /// Power8 vector - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto"; - /// Power8 crypto - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9"; - without cfg check: true; - /// Power9 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec"; - /// Power9 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector"; - /// Power9 vector -} diff --git a/crates/std/crates/std_detect/src/detect/arch/riscv.rs b/crates/std/crates/std_detect/src/detect/arch/riscv.rs deleted file mode 100644 index 0e6bab5..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/riscv.rs +++ /dev/null @@ -1,380 +0,0 @@ -//! Run-time feature detection on RISC-V. - -features! { - @TARGET: riscv; - @CFG: any(target_arch = "riscv32", target_arch = "riscv64"); - @MACRO_NAME: is_riscv_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - /// - /// RISC-V standard defined the base sets and the extension sets. - /// The base sets are RV32I, RV64I, RV32E or RV128I. Any RISC-V platform - /// must support one base set and/or multiple extension sets. - /// - /// Any RISC-V standard instruction sets can be in state of either ratified, - /// frozen or draft. The version and status of current standard instruction - /// sets can be checked out from preface section of the [ISA manual]. - /// - /// Platform may define and support their own custom instruction sets with - /// ISA prefix X. These sets are highly platform specific and should be - /// detected with their own platform support crates. - /// - /// [ISA manual]: https://riscv.org/specifications/ratified/ - /// - /// # Platform-specific/agnostic Behavior and Availability - /// - /// Runtime detection depends on the platform-specific feature detection - /// facility and its availability per feature is - /// highly platform/version-specific. - /// - /// Still, a best-effort attempt is performed to enable subset/dependent - /// features if a superset feature is enabled regardless of the platform. - /// For instance, if the A extension (`"a"`) is enabled, its subsets (the - /// Zalrsc and Zaamo extensions; `"zalrsc"` and `"zaamo"`) are also enabled. - /// Likewise, if the F extension (`"f"`) is enabled, one of its dependencies - /// (the Zicsr extension `"zicsr"`) is also enabled. - /// - /// # Unprivileged Specification - /// - /// The supported ratified RISC-V instruction sets are as follows (OS - /// columns denote runtime feature detection support with or without the - /// minimum supported version): - /// - /// | Literal | Base | Linux | - /// |:---------- |:------- |:---------- | - /// | `"rv32e"` | RV32E | No | - /// | `"rv32i"` | RV32I | Yes [^ima] | - /// | `"rv64i"` | RV64I | Yes [^ima] | - /// - /// | Literal | Extension | Linux | - /// |:--------------- |:----------- |:------------------- | - /// | `"a"` | A | Yes [^ima] | - /// | `"b"` | B | 6.5 | - /// | `"c"` | C | Yes | - /// | `"d"` | D | Yes | - /// | `"f"` | F | Yes | - /// | `"m"` | M | Yes [^ima] | - /// | `"q"` | Q | No | - /// | `"v"` | V | 6.5 | - /// | `"zaamo"` | Zaamo | 6.15 [^ima] [^dep] | - /// | `"zabha"` | Zabha | 6.16 | - /// | `"zacas"` | Zacas | 6.8 | - /// | `"zalrsc"` | Zalrsc | 6.15 [^ima] [^dep] | - /// | `"zawrs"` | Zawrs | 6.11 | - /// | `"zba"` | Zba | 6.5 | - /// | `"zbb"` | Zbb | 6.5 | - /// | `"zbc"` | Zbc | 6.8 | - /// | `"zbkb"` | Zbkb | 6.8 | - /// | `"zbkc"` | Zbkc | 6.8 | - /// | `"zbkx"` | Zbkx | 6.8 | - /// | `"zbs"` | Zbs | 6.5 | - /// | `"zca"` | Zca | 6.11 [^dep] | - /// | `"zcb"` | Zcb | 6.11 | - /// | `"zcd"` | Zcd | 6.11 [^dep] | - /// | `"zcf"` | Zcf | 6.11 [^dep] | - /// | `"zcmop"` | Zcmop | 6.11 | - /// | `"zdinx"` | Zdinx | No | - /// | `"zfa"` | Zfa | 6.8 | - /// | `"zfbfmin"` | Zfbfmin | 6.15 | - /// | `"zfh"` | Zfh | 6.8 | - /// | `"zfhmin"` | Zfhmin | 6.8 | - /// | `"zfinx"` | Zfinx | No | - /// | `"zhinx"` | Zhinx | No | - /// | `"zhinxmin"` | Zhinxmin | No | - /// | `"zicbom"` | Zicbom | 6.15 | - /// | `"zicboz"` | Zicboz | 6.7 | - /// | `"zicntr"` | Zicntr | 6.15 [^ima] [^cntr] | - /// | `"zicond"` | Zicond | 6.8 | - /// | `"zicsr"` | Zicsr | No [^ima] [^dep] | - /// | `"zifencei"` | Zifencei | No [^ima] | - /// | `"zihintntl"` | Zihintntl | 6.8 | - /// | `"zihintpause"` | Zihintpause | 6.10 | - /// | `"zihpm"` | Zihpm | 6.15 [^cntr] | - /// | `"zimop"` | Zimop | 6.11 | - /// | `"zk"` | Zk | No [^zkr] | - /// | `"zkn"` | Zkn | 6.8 | - /// | `"zknd"` | Zknd | 6.8 | - /// | `"zkne"` | Zkne | 6.8 | - /// | `"zknh"` | Zknh | 6.8 | - /// | `"zkr"` | Zkr | No [^zkr] | - /// | `"zks"` | Zks | 6.8 | - /// | `"zksed"` | Zksed | 6.8 | - /// | `"zksh"` | Zksh | 6.8 | - /// | `"zkt"` | Zkt | 6.8 | - /// | `"ztso"` | Ztso | 6.8 | - /// | `"zvbb"` | Zvbb | 6.8 | - /// | `"zvbc"` | Zvbc | 6.8 | - /// | `"zve32f"` | Zve32f | 6.11 [^dep] | - /// | `"zve32x"` | Zve32x | 6.11 [^dep] | - /// | `"zve64d"` | Zve64d | 6.11 [^dep] | - /// | `"zve64f"` | Zve64f | 6.11 [^dep] | - /// | `"zve64x"` | Zve64x | 6.11 [^dep] | - /// | `"zvfbfmin"` | Zvfbfmin | 6.15 | - /// | `"zvfbfwma"` | Zvfbfwma | 6.15 | - /// | `"zvfh"` | Zvfh | 6.8 | - /// | `"zvfhmin"` | Zvfhmin | 6.8 | - /// | `"zvkb"` | Zvkb | 6.8 | - /// | `"zvkg"` | Zvkg | 6.8 | - /// | `"zvkn"` | Zvkn | 6.8 | - /// | `"zvknc"` | Zvknc | 6.8 | - /// | `"zvkned"` | Zvkned | 6.8 | - /// | `"zvkng"` | Zvkng | 6.8 | - /// | `"zvknha"` | Zvknha | 6.8 | - /// | `"zvknhb"` | Zvknhb | 6.8 | - /// | `"zvks"` | Zvks | 6.8 | - /// | `"zvksc"` | Zvksc | 6.8 | - /// | `"zvksed"` | Zvksed | 6.8 | - /// | `"zvksg"` | Zvksg | 6.8 | - /// | `"zvksh"` | Zvksh | 6.8 | - /// | `"zvkt"` | Zvkt | 6.8 | - /// - /// [^ima]: Or enabled when the IMA base behavior is detected on the Linux - /// kernel version 6.4 or later (for bases, the only matching one -- either - /// `"rv32i"` or `"rv64i"` -- is enabled). - /// - /// [^cntr]: Even if this extension is available, it does not necessarily - /// mean all performance counters are accessible. - /// For example, accesses to all performance counters except `time` - /// (wall-clock) are blocked by default on the Linux kernel - /// version 6.6 or later. - /// Also beware that, even if performance counters like `cycle` and - /// `instret` are accessible, their value can be unreliable (e.g. returning - /// the constant value) under certain circumstances. - /// - /// [^dep]: Or enabled as a dependency of another extension (a superset) - /// even if runtime detection of this feature itself is not supported (as - /// long as the runtime detection of the superset is supported). - /// - /// [^zkr]: Linux does not report existence of this extension even if - /// supported by the hardware mainly because the `seed` CSR on the Zkr - /// extension (which provides hardware-based randomness) is normally - /// inaccessible from the user mode. - /// For the Zk extension features except this CSR, check existence of both - /// `"zkn"` and `"zkt"` features instead. - /// - /// There's also bases and extensions marked as standard instruction set, - /// but they are in frozen or draft state. These instruction sets are also - /// reserved by this macro and can be detected in the future platforms. - /// - /// Draft RISC-V instruction sets: - /// - /// * RV128I: `"rv128i"` - /// * J: `"j"` - /// * P: `"p"` - /// * Zam: `"zam"` - /// - /// # Performance Hints - /// - /// The two features below define performance hints for unaligned - /// scalar/vector memory accesses, respectively. If enabled, it denotes that - /// corresponding unaligned memory access is reasonably fast. - /// - /// * `"unaligned-scalar-mem"` - /// * Runtime detection requires Linux kernel version 6.4 or later. - /// * `"unaligned-vector-mem"` - /// * Runtime detection requires Linux kernel version 6.13 or later. - #[stable(feature = "riscv_ratified", since = "1.78.0")] - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32i: "rv32i"; - without cfg check: true; - /// RV32I Base Integer Instruction Set - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32e: "rv32e"; - without cfg check: true; - /// RV32E Base Integer Instruction Set - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv64i: "rv64i"; - without cfg check: true; - /// RV64I Base Integer Instruction Set - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv128i: "rv128i"; - without cfg check: true; - /// RV128I Base Integer Instruction Set - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem"; - /// Has reasonably performant unaligned scalar - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem"; - /// Has reasonably performant unaligned vector - - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicsr: "zicsr"; - /// "Zicsr" Extension for Control and Status Register (CSR) Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicntr: "zicntr"; - /// "Zicntr" Extension for Base Counters and Timers - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihpm: "zihpm"; - /// "Zihpm" Extension for Hardware Performance Counters - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zifencei: "zifencei"; - /// "Zifencei" Extension for Instruction-Fetch Fence - - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintntl: "zihintntl"; - /// "Zihintntl" Extension for Non-Temporal Locality Hints - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintpause: "zihintpause"; - /// "Zihintpause" Extension for Pause Hint - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zimop: "zimop"; - /// "Zimop" Extension for May-Be-Operations - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicbom: "zicbom"; - /// "Zicbom" Extension for Cache-Block Management Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicboz: "zicboz"; - /// "Zicboz" Extension for Cache-Block Zero Instruction - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicond: "zicond"; - /// "Zicond" Extension for Integer Conditional Operations - - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m"; - /// "M" Extension for Integer Multiplication and Division - - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a"; - /// "A" Extension for Atomic Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zalrsc: "zalrsc"; - /// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zaamo: "zaamo"; - /// "Zaamo" Extension for Atomic Memory Operations - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zawrs: "zawrs"; - /// "Zawrs" Extension for Wait-on-Reservation-Set Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zabha: "zabha"; - /// "Zabha" Extension for Byte and Halfword Atomic Memory Operations - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zacas: "zacas"; - /// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam"; - without cfg check: true; - /// "Zam" Extension for Misaligned Atomics - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] ztso: "ztso"; - /// "Ztso" Extension for Total Store Ordering - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] f: "f"; - /// "F" Extension for Single-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] d: "d"; - /// "D" Extension for Double-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] q: "q"; - without cfg check: true; - /// "Q" Extension for Quad-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfh: "zfh"; - /// "Zfh" Extension for Half-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfhmin: "zfhmin"; - /// "Zfhmin" Extension for Minimal Half-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfa: "zfa"; - /// "Zfa" Extension for Additional Floating-Point Instructions - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfbfmin: "zfbfmin"; - /// "Zfbfmin" Extension for Scalar BF16 Converts - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfinx: "zfinx"; - /// "Zfinx" Extension for Single-Precision Floating-Point in Integer Registers - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zdinx: "zdinx"; - /// "Zdinx" Extension for Double-Precision Floating-Point in Integer Registers - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinx: "zhinx"; - /// "Zhinx" Extension for Half-Precision Floating-Point in Integer Registers - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinxmin: "zhinxmin"; - /// "Zhinxmin" Extension for Minimal Half-Precision Floating-Point in Integer Registers - - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c"; - /// "C" Extension for Compressed Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zca: "zca"; - /// "Zca" Compressed Instructions excluding Floating-Point Loads/Stores - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcf: "zcf"; - without cfg check: true; - /// "Zcf" Compressed Instructions for Single-Precision Floating-Point Loads/Stores on RV32 - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcd: "zcd"; - without cfg check: true; - /// "Zcd" Compressed Instructions for Double-Precision Floating-Point Loads/Stores - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcb: "zcb"; - /// "Zcb" Simple Code-size Saving Compressed Instructions - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcmop: "zcmop"; - /// "Zcmop" Extension for Compressed May-Be-Operations - - @FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] b: "b"; - /// "B" Extension for Bit Manipulation - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zba: "zba"; - /// "Zba" Extension for Address Generation - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbb: "zbb"; - /// "Zbb" Extension for Basic Bit-Manipulation - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbc: "zbc"; - /// "Zbc" Extension for Carry-less Multiplication - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbs: "zbs"; - /// "Zbs" Extension for Single-Bit Instructions - - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkb: "zbkb"; - /// "Zbkb" Extension for Bit-Manipulation for Cryptography - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkc: "zbkc"; - /// "Zbkc" Extension for Carry-less Multiplication for Cryptography - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkx: "zbkx"; - /// "Zbkx" Extension for Crossbar Permutations - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknd: "zknd"; - /// "Zknd" Cryptography Extension for NIST Suite: AES Decryption - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkne: "zkne"; - /// "Zkne" Cryptography Extension for NIST Suite: AES Encryption - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknh: "zknh"; - /// "Zknh" Cryptography Extension for NIST Suite: Hash Function Instructions - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksed: "zksed"; - /// "Zksed" Cryptography Extension for ShangMi Suite: SM4 Block Cipher Instructions - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksh: "zksh"; - /// "Zksh" Cryptography Extension for ShangMi Suite: SM3 Hash Function Instructions - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkr: "zkr"; - /// "Zkr" Entropy Source Extension - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkn: "zkn"; - /// "Zkn" Cryptography Extension for NIST Algorithm Suite - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zks: "zks"; - /// "Zks" Cryptography Extension for ShangMi Algorithm Suite - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zk: "zk"; - /// "Zk" Cryptography Extension for Standard Scalar Cryptography - @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkt: "zkt"; - /// "Zkt" Cryptography Extension for Data Independent Execution Latency - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v"; - /// "V" Extension for Vector Operations - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x"; - /// "Zve32x" Vector Extension for Embedded Processors (32-bit+; Integer) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f"; - /// "Zve32f" Vector Extension for Embedded Processors (32-bit+; with Single-Precision Floating-Point) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x"; - /// "Zve64x" Vector Extension for Embedded Processors (64-bit+; Integer) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f"; - /// "Zve64f" Vector Extension for Embedded Processors (64-bit+; with Single-Precision Floating-Point) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d"; - /// "Zve64d" Vector Extension for Embedded Processors (64-bit+; with Double-Precision Floating-Point) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh"; - /// "Zvfh" Vector Extension for Half-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin"; - /// "Zvfhmin" Vector Extension for Minimal Half-Precision Floating-Point - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfmin: "zvfbfmin"; - /// "Zvfbfmin" Vector Extension for BF16 Converts - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfwma: "zvfbfwma"; - /// "Zvfbfwma" Vector Extension for BF16 Widening Multiply-Add - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb"; - /// "Zvbb" Extension for Vector Basic Bit-Manipulation - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc"; - /// "Zvbc" Extension for Vector Carryless Multiplication - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb"; - /// "Zvkb" Extension for Vector Cryptography Bit-Manipulation - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg"; - /// "Zvkg" Cryptography Extension for Vector GCM/GMAC - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned"; - /// "Zvkned" Cryptography Extension for NIST Suite: Vector AES Block Cipher - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha"; - /// "Zvknha" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb"; - /// "Zvknhb" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256/512) - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed"; - /// "Zvksed" Cryptography Extension for ShangMi Suite: Vector SM4 Block Cipher - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh"; - /// "Zvksh" Cryptography Extension for ShangMi Suite: Vector SM3 Secure Hash - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn"; - /// "Zvkn" Cryptography Extension for NIST Algorithm Suite - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc"; - /// "Zvknc" Cryptography Extension for NIST Algorithm Suite with Carryless Multiply - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng"; - /// "Zvkng" Cryptography Extension for NIST Algorithm Suite with GCM - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks"; - /// "Zvks" Cryptography Extension for ShangMi Algorithm Suite - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc"; - /// "Zvksc" Cryptography Extension for ShangMi Algorithm Suite with Carryless Multiply - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg"; - /// "Zvksg" Cryptography Extension for ShangMi Algorithm Suite with GCM - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt"; - /// "Zvkt" Extension for Vector Data-Independent Execution Latency - - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] j: "j"; - without cfg check: true; - /// "J" Extension for Dynamically Translated Languages - @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] p: "p"; - without cfg check: true; - /// "P" Extension for Packed-SIMD Instructions -} diff --git a/crates/std/crates/std_detect/src/detect/arch/s390x.rs b/crates/std/crates/std_detect/src/detect/arch/s390x.rs deleted file mode 100644 index 63f7390..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/s390x.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Run-time feature detection on s390x. - -features! { - @TARGET: s390x; - @CFG: target_arch = "s390x"; - @MACRO_NAME: is_s390x_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] concurrent_functions: "concurrent-functions"; - /// s390x concurrent-functions facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] deflate_conversion: "deflate-conversion"; - /// s390x deflate-conversion facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] enhanced_sort: "enhanced-sort"; - /// s390x enhanced-sort facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] guarded_storage: "guarded-storage"; - /// s390x guarded-storage facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] high_word: "high-word"; - /// s390x high-word facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension3: "message-security-assist-extension3"; - /// s390x message-security-assist-extension3 facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension4: "message-security-assist-extension4"; - /// s390x message-security-assist-extension4 facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension5: "message-security-assist-extension5"; - /// s390x message-security-assist-extension5 facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension8: "message-security-assist-extension8"; - /// s390x message-security-assist-extension8 facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension9: "message-security-assist-extension9"; - /// s390x message-security-assist-extension9 facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension12: "message-security-assist-extension12"; - /// s390x message-security-assist-extension12 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_2: "miscellaneous-extensions-2"; - /// s390x miscellaneous-extensions-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_3: "miscellaneous-extensions-3"; - /// s390x miscellaneous-extensions-3 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_4: "miscellaneous-extensions-4"; - /// s390x miscellaneous-extensions-4 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] nnp_assist: "nnp-assist"; - /// s390x nnp-assist facility - @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] transactional_execution: "transactional-execution"; - /// s390x transactional-execution facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector: "vector"; - /// s390x vector facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_1: "vector-enhancements-1"; - /// s390x vector-enhancements-1 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_2: "vector-enhancements-2"; - /// s390x vector-enhancements-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_3: "vector-enhancements-3"; - /// s390x vector-enhancements-3 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal: "vector-packed-decimal"; - /// s390x vector-packed-decimal facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement: "vector-packed-decimal-enhancement"; - /// s390x vector-packed-decimal-enhancement facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_2: "vector-packed-decimal-enhancement-2"; - /// s390x vector-packed-decimal-enhancement-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_3: "vector-packed-decimal-enhancement-3"; - /// s390x vector-packed-decimal-enhancement-3 facility -} diff --git a/crates/std/crates/std_detect/src/detect/arch/x86.rs b/crates/std/crates/std_detect/src/detect/arch/x86.rs deleted file mode 100644 index 4c3a71e..0000000 --- a/crates/std/crates/std_detect/src/detect/arch/x86.rs +++ /dev/null @@ -1,280 +0,0 @@ -//! This module implements minimal run-time feature detection for x86. -//! -//! The features are detected using the `detect_features` function below. -//! This function uses the CPUID instruction to read the feature flags from the -//! CPU and encodes them in a `usize` where each bit position represents -//! whether a feature is available (bit is set) or unavailable (bit is cleared). -//! -//! The enum `Feature` is used to map bit positions to feature names, and the -//! the `__crate::detect::check_for!` macro is used to map string literals (e.g., -//! "avx") to these bit positions (e.g., `Feature::avx`). -//! -//! The run-time feature detection is performed by the -//! `__crate::detect::check_for(Feature) -> bool` function. On its first call, -//! this functions queries the CPU for the available features and stores them -//! in a global `AtomicUsize` variable. The query is performed by just checking -//! whether the feature bit in this global variable is set or cleared. - -features! { - @TARGET: x86; - @CFG: any(target_arch = "x86", target_arch = "x86_64"); - @MACRO_NAME: is_x86_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - /// - /// Runtime detection currently relies mostly on the `cpuid` instruction. - /// - /// This macro only takes one argument which is a string literal of the feature - /// being tested for. The feature names supported are the lowercase versions of - /// the ones defined by Intel in [their documentation][docs]. - /// - /// ## Supported arguments - /// - /// This macro supports the same names that `#[target_feature]` supports. Unlike - /// `#[target_feature]`, however, this macro does not support names separated - /// with a comma. Instead testing for multiple features must be done through - /// separate macro invocations for now. - /// - /// Supported arguments are: - /// - /// * `"aes"` - /// * `"pclmulqdq"` - /// * `"rdrand"` - /// * `"rdseed"` - /// * `"tsc"` - /// * `"mmx"` - /// * `"sse"` - /// * `"sse2"` - /// * `"sse3"` - /// * `"ssse3"` - /// * `"sse4.1"` - /// * `"sse4.2"` - /// * `"sse4a"` - /// * `"sha"` - /// * `"avx"` - /// * `"avx2"` - /// * `"sha512"` - /// * `"sm3"` - /// * `"sm4"` - /// * `"avx512f"` - /// * `"avx512cd"` - /// * `"avx512er"` - /// * `"avx512pf"` - /// * `"avx512bw"` - /// * `"avx512dq"` - /// * `"avx512vl"` - /// * `"avx512ifma"` - /// * `"avx512vbmi"` - /// * `"avx512vpopcntdq"` - /// * `"avx512vbmi2"` - /// * `"gfni"` - /// * `"vaes"` - /// * `"vpclmulqdq"` - /// * `"avx512vnni"` - /// * `"avx512bitalg"` - /// * `"avx512bf16"` - /// * `"avx512vp2intersect"` - /// * `"avx512fp16"` - /// * `"avxvnni"` - /// * `"avxifma"` - /// * `"avxneconvert"` - /// * `"avxvnniint8"` - /// * `"avxvnniint16"` - /// * `"amx-tile"` - /// * `"amx-int8"` - /// * `"amx-bf16"` - /// * `"amx-fp16"` - /// * `"amx-complex"` - /// * `"amx-avx512"` - /// * `"amx-fp8"` - /// * `"amx-movrs"` - /// * `"amx-tf32"` - /// * `"f16c"` - /// * `"fma"` - /// * `"bmi1"` - /// * `"bmi2"` - /// * `"abm"` - /// * `"lzcnt"` - /// * `"tbm"` - /// * `"popcnt"` - /// * `"fxsr"` - /// * `"xsave"` - /// * `"xsaveopt"` - /// * `"xsaves"` - /// * `"xsavec"` - /// * `"cmpxchg16b"` - /// * `"kl"` - /// * `"widekl"` - /// * `"adx"` - /// * `"rtm"` - /// * `"movbe"` - /// * `"ermsb"` - /// * `"movrs"` - /// * `"xop"` - /// - /// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide - #[stable(feature = "simd_x86", since = "1.27.0")] - @BIND_FEATURE_NAME: "abm"; "lzcnt"; // abm is a synonym for lzcnt - @BIND_FEATURE_NAME: "avx512gfni"; "gfni"; #[deprecated(since = "1.67.0", note = "the `avx512gfni` feature has been renamed to `gfni`")]; - @BIND_FEATURE_NAME: "avx512vaes"; "vaes"; #[deprecated(since = "1.67.0", note = "the `avx512vaes` feature has been renamed to `vaes`")]; - @BIND_FEATURE_NAME: "avx512vpclmulqdq"; "vpclmulqdq"; #[deprecated(since = "1.67.0", note = "the `avx512vpclmulqdq` feature has been renamed to `vpclmulqdq`")]; - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] aes: "aes"; - /// AES (Advanced Encryption Standard New Instructions AES-NI) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] pclmulqdq: "pclmulqdq"; - /// CLMUL (Carry-less Multiplication) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdrand: "rdrand"; - /// RDRAND - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdseed: "rdseed"; - /// RDSEED - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tsc: "tsc"; - without cfg check: true; - /// TSC (Time Stamp Counter) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] mmx: "mmx"; - without cfg check: true; - /// MMX (MultiMedia eXtensions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse: "sse"; - /// SSE (Streaming SIMD Extensions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse2: "sse2"; - /// SSE2 (Streaming SIMD Extensions 2) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse3: "sse3"; - /// SSE3 (Streaming SIMD Extensions 3) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ssse3: "ssse3"; - /// SSSE3 (Supplemental Streaming SIMD Extensions 3) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_1: "sse4.1"; - /// SSE4.1 (Streaming SIMD Extensions 4.1) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_2: "sse4.2"; - /// SSE4.2 (Streaming SIMD Extensions 4.2) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4a: "sse4a"; - /// SSE4a (Streaming SIMD Extensions 4a) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sha: "sha"; - /// SHA - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx: "avx"; - /// AVX (Advanced Vector Extensions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx2: "avx2"; - /// AVX2 (Advanced Vector Extensions 2) - @FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sha512: "sha512"; - /// SHA512 - @FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm3: "sm3"; - /// SM3 - @FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm4: "sm4"; - /// SM4 - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512f: "avx512f" ; - /// AVX-512 F (Foundation) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512cd: "avx512cd" ; - /// AVX-512 CD (Conflict Detection Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512er: "avx512er"; - without cfg check: true; - /// AVX-512 ER (Expo nential and Reciprocal Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512pf: "avx512pf"; - without cfg check: true; - /// AVX-512 PF (Prefetch Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bw: "avx512bw"; - /// AVX-512 BW (Byte and Word Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512dq: "avx512dq"; - /// AVX-512 DQ (Doubleword and Quadword) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vl: "avx512vl"; - /// AVX-512 VL (Vector Length Extensions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512ifma: "avx512ifma"; - /// AVX-512 IFMA (Integer Fused Multiply Add) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi: "avx512vbmi"; - /// AVX-512 VBMI (Vector Byte Manipulation Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vpopcntdq: "avx512vpopcntdq"; - /// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and Quadword) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi2: "avx512vbmi2"; - /// AVX-512 VBMI2 (Additional byte, word, dword and qword capabilities) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] gfni: "gfni"; - /// AVX-512 GFNI (Galois Field New Instruction) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vaes: "vaes"; - /// AVX-512 VAES (Vector AES instruction) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vpclmulqdq: "vpclmulqdq"; - /// AVX-512 VPCLMULQDQ (Vector PCLMULQDQ instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vnni: "avx512vnni"; - /// AVX-512 VNNI (Vector Neural Network Instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bitalg: "avx512bitalg"; - /// AVX-512 BITALG (Support for VPOPCNT\[B,W\] and VPSHUFBITQMB) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bf16: "avx512bf16"; - /// AVX-512 BF16 (BFLOAT16 instructions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vp2intersect: "avx512vp2intersect"; - /// AVX-512 P2INTERSECT - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512fp16: "avx512fp16"; - /// AVX-512 FP16 (FLOAT16 instructions) - @FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxifma: "avxifma"; - /// AVX-IFMA (Integer Fused Multiply Add) - @FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxneconvert: "avxneconvert"; - /// AVX-NE-CONVERT (Exceptionless Convert) - @FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnni: "avxvnni"; - /// AVX-VNNI (Vector Neural Network Instructions) - @FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint16: "avxvnniint16"; - /// AVX-VNNI_INT8 (VNNI with 16-bit Integers) - @FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint8: "avxvnniint8"; - /// AVX-VNNI_INT16 (VNNI with 8-bit integers) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tile: "amx-tile"; - /// AMX (Advanced Matrix Extensions) - Tile load/store - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_int8: "amx-int8"; - /// AMX-INT8 (Operations on 8-bit integers) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_bf16: "amx-bf16"; - /// AMX-BF16 (BFloat16 Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp16: "amx-fp16"; - /// AMX-FP16 (Float16 Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_complex: "amx-complex"; - /// AMX-COMPLEX (Complex number Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_avx512: "amx-avx512"; - /// AMX-AVX512 (AVX512 operations extended to matrices) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp8: "amx-fp8"; - /// AMX-FP8 (Float8 Operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_movrs: "amx-movrs"; - /// AMX-MOVRS (Matrix MOVERS operations) - @FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32"; - /// AMX-TF32 (TensorFloat32 Operations) - @FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf"; - /// APX-F (Advanced Performance Extensions - Foundation) - @FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1"; - /// AVX10.1 - @FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_2: "avx10.2"; - /// AVX10.2 - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] f16c: "f16c"; - /// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma"; - /// FMA (Fused Multiply Add) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi1: "bmi1" ; - /// BMI1 (Bit Manipulation Instructions 1) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi2: "bmi2" ; - /// BMI2 (Bit Manipulation Instructions 2) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] lzcnt: "lzcnt"; - /// ABM (Advanced Bit Manipulation) / LZCNT (Leading Zero Count) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tbm: "tbm"; - /// TBM (Trailing Bit Manipulation) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] popcnt: "popcnt"; - /// POPCNT (Population Count) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fxsr: "fxsr"; - /// FXSR (Floating-point context fast save and restore) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsave: "xsave"; - /// XSAVE (Save Processor Extended States) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaveopt: "xsaveopt"; - /// XSAVEOPT (Save Processor Extended States Optimized) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaves: "xsaves"; - /// XSAVES (Save Processor Extended States Supervisor) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsavec: "xsavec"; - /// XSAVEC (Save Processor Extended States Compacted) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] cmpxchg16b: "cmpxchg16b"; - /// CMPXCH16B (16-byte compare-and-swap instruction) - @FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] kl: "kl"; - /// Intel Key Locker - @FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] widekl: "widekl"; - /// Intel Key Locker Wide - @FEATURE: #[stable(feature = "simd_x86_adx", since = "1.33.0")] adx: "adx"; - /// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rtm: "rtm"; - /// RTM, Intel (Restricted Transactional Memory) - @FEATURE: #[stable(feature = "movbe_target_feature", since = "1.67.0")] movbe: "movbe"; - /// MOVBE (Move Data After Swapping Bytes) - @FEATURE: #[unstable(feature = "movrs_target_feature", issue = "137976")] movrs: "movrs"; - /// MOVRS (Move data with the read-shared hint) - @FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ermsb: "ermsb"; - /// ERMSB, Enhanced REP MOVSB and STOSB - @FEATURE: #[unstable(feature = "xop_target_feature", issue = "127208")] xop: "xop"; - /// XOP: eXtended Operations (AMD) -} diff --git a/crates/std/crates/std_detect/src/detect/bit.rs b/crates/std/crates/std_detect/src/detect/bit.rs deleted file mode 100644 index 6f06c55..0000000 --- a/crates/std/crates/std_detect/src/detect/bit.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Bit manipulation utilities. - -/// Tests the `bit` of `x`. -#[allow(dead_code)] -#[inline] -pub(crate) fn test(x: usize, bit: u32) -> bool { - debug_assert!(bit < usize::BITS, "bit index out-of-bounds"); - x & (1 << bit) != 0 -} diff --git a/crates/std/crates/std_detect/src/detect/cache.rs b/crates/std/crates/std_detect/src/detect/cache.rs deleted file mode 100644 index c0c0b7b..0000000 --- a/crates/std/crates/std_detect/src/detect/cache.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Caches run-time feature detection so that it only needs to be computed -//! once. - -#![allow(dead_code)] // not used on all platforms - -use core::sync::atomic::{AtomicUsize, Ordering}; - -/// Sets the `bit` of `x`. -#[inline] -const fn set_bit(x: u128, bit: u32) -> u128 { - x | 1 << bit -} - -/// Tests the `bit` of `x`. -#[inline] -const fn test_bit(x: u128, bit: u32) -> bool { - x & (1 << bit) != 0 -} - -/// Unset the `bit of `x`. -#[inline] -const fn unset_bit(x: u128, bit: u32) -> u128 { - x & !(1 << bit) -} - -/// Maximum number of features that can be cached. -const CACHE_CAPACITY: u32 = 93; - -/// This type is used to initialize the cache -// The derived `Default` implementation will initialize the field to zero, -// which is what we want. -#[derive(Copy, Clone, Default, PartialEq, Eq)] -pub(crate) struct Initializer(u128); - -// NOTE: the `debug_assert!` would catch that we do not add more Features than -// the one fitting our cache. -impl Initializer { - /// Tests the `bit` of the cache. - #[inline] - pub(crate) fn test(self, bit: u32) -> bool { - debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!"); - test_bit(self.0, bit) - } - - /// Sets the `bit` of the cache. - #[inline] - pub(crate) fn set(&mut self, bit: u32) { - debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!"); - let v = self.0; - self.0 = set_bit(v, bit); - } - - /// Unsets the `bit` of the cache. - #[inline] - pub(crate) fn unset(&mut self, bit: u32) { - debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!"); - let v = self.0; - self.0 = unset_bit(v, bit); - } -} - -/// This global variable is a cache of the features supported by the CPU. -// Note: the third slot is only used in x86 -// Another Slot can be added if needed without any change to `Initializer` -static CACHE: [Cache; 3] = [Cache::uninitialized(), Cache::uninitialized(), Cache::uninitialized()]; - -/// Feature cache with capacity for `size_of::() * 8 - 1` features. -/// -/// Note: 0 is used to represent an uninitialized cache, and (at least) the most -/// significant bit is set on any cache which has been initialized. -/// -/// Note: we use `Relaxed` atomic operations, because we are only interested in -/// the effects of operations on a single memory location. That is, we only need -/// "modification order", and not the full-blown "happens before". -struct Cache(AtomicUsize); - -impl Cache { - const CAPACITY: u32 = (core::mem::size_of::() * 8 - 1) as u32; - const MASK: usize = (1 << Cache::CAPACITY) - 1; - const INITIALIZED_BIT: usize = 1usize << Cache::CAPACITY; - - /// Creates an uninitialized cache. - #[allow(clippy::declare_interior_mutable_const)] - const fn uninitialized() -> Self { - Cache(AtomicUsize::new(0)) - } - - /// Is the `bit` in the cache set? Returns `None` if the cache has not been initialized. - #[inline] - pub(crate) fn test(&self, bit: u32) -> Option { - let cached = self.0.load(Ordering::Relaxed); - if cached == 0 { None } else { Some(test_bit(cached as u128, bit)) } - } - - /// Initializes the cache. - #[inline] - fn initialize(&self, value: usize) -> usize { - debug_assert_eq!((value & !Cache::MASK), 0); - self.0.store(value | Cache::INITIALIZED_BIT, Ordering::Relaxed); - value - } -} - -cfg_select! { - feature = "std_detect_env_override" => { - #[inline] - fn disable_features(disable: &[u8], value: &mut Initializer) { - if let Ok(disable) = core::str::from_utf8(disable) { - for v in disable.split(" ") { - let _ = super::Feature::from_str(v).map(|v| value.unset(v as u32)); - } - } - } - - #[inline] - fn initialize(mut value: Initializer) -> Initializer { - use core::ffi::CStr; - const RUST_STD_DETECT_UNSTABLE: &CStr = c"RUST_STD_DETECT_UNSTABLE"; - cfg_select! { - windows => { - use alloc::vec; - #[link(name = "kernel32")] - unsafe extern "system" { - fn GetEnvironmentVariableA(name: *const u8, buffer: *mut u8, size: u32) -> u32; - } - let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::(), core::ptr::null_mut(), 0) }; - if len > 0 { - // +1 to include the null terminator. - let mut env = vec![0; len as usize + 1]; - let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::(), env.as_mut_ptr(), len + 1) }; - if len > 0 { - disable_features(&env[..len as usize], &mut value); - } - } - } - _ => { - let env = unsafe { - libc::getenv(RUST_STD_DETECT_UNSTABLE.as_ptr()) - }; - if !env.is_null() { - let len = unsafe { libc::strlen(env) }; - let env = unsafe { core::slice::from_raw_parts(env as *const u8, len) }; - disable_features(env, &mut value); - } - } - } - do_initialize(value); - value - } - } - _ => { - #[inline] - fn initialize(value: Initializer) -> Initializer { - do_initialize(value); - value - } - } -} - -#[inline] -fn do_initialize(value: Initializer) { - CACHE[0].initialize((value.0) as usize & Cache::MASK); - CACHE[1].initialize((value.0 >> Cache::CAPACITY) as usize & Cache::MASK); - CACHE[2].initialize((value.0 >> (2 * Cache::CAPACITY)) as usize & Cache::MASK); -} - -// We only have to detect features once, and it's fairly costly, so hint to LLVM -// that it should assume that cache hits are more common than misses (which is -// the point of caching). It's possibly unfortunate that this function needs to -// reach across modules like this to call `os::detect_features`, but it produces -// the best code out of several attempted variants. -// -// The `Initializer` that the cache was initialized with is returned, so that -// the caller can call `test()` on it without having to load the value from the -// cache again. -#[cold] -fn detect_and_initialize() -> Initializer { - initialize(super::os::detect_features()) -} - -/// Tests the `bit` of the storage. If the storage has not been initialized, -/// initializes it with the result of `os::detect_features()`. -/// -/// On its first invocation, it detects the CPU features and caches them in the -/// `CACHE` global variable as an `AtomicU64`. -/// -/// It uses the `Feature` variant to index into this variable as a bitset. If -/// the bit is set, the feature is enabled, and otherwise it is disabled. -/// -/// If the feature `std_detect_env_override` is enabled looks for the env -/// variable `RUST_STD_DETECT_UNSTABLE` and uses its content to disable -/// Features that would had been otherwise detected. -#[inline] -pub(crate) fn test(bit: u32) -> bool { - let (relative_bit, idx) = if bit < Cache::CAPACITY { - (bit, 0) - } else if bit < 2 * Cache::CAPACITY { - (bit - Cache::CAPACITY, 1) - } else { - (bit - 2 * Cache::CAPACITY, 2) - }; - CACHE[idx].test(relative_bit).unwrap_or_else(|| detect_and_initialize().test(bit)) -} diff --git a/crates/std/crates/std_detect/src/detect/macros.rs b/crates/std/crates/std_detect/src/detect/macros.rs deleted file mode 100644 index 17140e1..0000000 --- a/crates/std/crates/std_detect/src/detect/macros.rs +++ /dev/null @@ -1,203 +0,0 @@ -#[macro_export] -#[allow_internal_unstable(stdarch_internal)] -#[unstable(feature = "stdarch_internal", issue = "none")] -macro_rules! detect_feature { - ($feature:tt, $feature_lit:tt) => { - $crate::detect_feature!($feature, $feature_lit : $feature_lit) - }; - ($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => { - $(cfg!(target_feature = $target_feature_lit) ||)* - $crate::detect::__is_feature_detected::$feature() - }; - ($feature:tt, $feature_lit:tt, without cfg check: true) => { - $crate::detect::__is_feature_detected::$feature() - }; -} - -#[allow(unused_macros, reason = "it's used in the features! macro below")] -macro_rules! check_cfg_feature { - ($feature:tt, $feature_lit:tt) => { - check_cfg_feature!($feature, $feature_lit : $feature_lit) - }; - ($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => { - $(cfg!(target_feature = $target_feature_lit);)* - }; - ($feature:tt, $feature_lit:tt, without cfg check: $feature_cfg_check:literal) => { - #[allow(unexpected_cfgs, reason = $feature_lit)] - { cfg!(target_feature = $feature_lit) } - }; -} - -#[allow(unused)] -macro_rules! features { - ( - @TARGET: $target:ident; - @CFG: $cfg:meta; - @MACRO_NAME: $macro_name:ident; - @MACRO_ATTRS: $(#[$macro_attrs:meta])* - $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; $(#[$deprecate_attr:meta];)?)* - $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )* - $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt; - $(without cfg check: $feature_cfg_check:tt;)? - $(implied by target_features: [$($target_feature_lit:tt),*];)? - $(#[$feature_comment:meta])*)* - ) => { - #[macro_export] - $(#[$macro_attrs])* - #[allow_internal_unstable(stdarch_internal)] - #[cfg($cfg)] - #[doc(cfg($cfg))] - macro_rules! $macro_name { - $( - ($feature_lit) => { - $crate::detect_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?) - }; - )* - $( - ($bind_feature) => { - { - $( - #[$deprecate_attr] macro_rules! deprecated_feature { {} => {}; } - deprecated_feature! {}; - )? - $crate::$macro_name!($feature_impl) - } - }; - )* - $( - ($nort_feature) => { - compile_error!( - concat!( - stringify!($nort_feature), - " feature cannot be detected at run-time" - ) - ) - }; - )* - ($t:tt,) => { - $crate::$macro_name!($t); - }; - ($t:tt) => { - compile_error!( - concat!( - concat!("unknown ", stringify!($target)), - concat!(" target feature: ", $t) - ) - ) - }; - } - - $(#[$macro_attrs])* - #[macro_export] - #[cfg(not($cfg))] - #[doc(cfg($cfg))] - macro_rules! $macro_name { - $( - ($feature_lit) => { - compile_error!( - concat!( - r#"This macro cannot be used on the current target. - You can prevent it from being used in other architectures by - guarding it behind a cfg("#, - stringify!($cfg), - ")." - ) - ) - }; - )* - $( - ($bind_feature) => { $crate::$macro_name!($feature_impl) }; - )* - $( - ($nort_feature) => { - compile_error!( - concat!( - stringify!($nort_feature), - " feature cannot be detected at run-time" - ) - ) - }; - )* - ($t:tt,) => { - $crate::$macro_name!($t); - }; - ($t:tt) => { - compile_error!( - concat!( - concat!("unknown ", stringify!($target)), - concat!(" target feature: ", $t) - ) - ) - }; - } - - #[deny(unexpected_cfgs)] - #[deny(unfulfilled_lint_expectations)] - const _: () = { - $( - check_cfg_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?); - )* - }; - - /// Each variant denotes a position in a bitset for a particular feature. - /// - /// PLEASE: do not use this, it is an implementation detail subject - /// to change. - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[derive(Copy, Clone)] - #[repr(u8)] - #[unstable(feature = "stdarch_internal", issue = "none")] - #[cfg($cfg)] - pub(crate) enum Feature { - $( - $(#[$feature_comment])* - $feature, - )* - - // Do not add variants after last: - _last - } - - #[cfg($cfg)] - impl Feature { - pub(crate) fn to_str(self) -> &'static str { - match self { - $(Feature::$feature => $feature_lit,)* - Feature::_last => unreachable!(), - } - } - - #[cfg(feature = "std_detect_env_override")] - pub(crate) fn from_str(s: &str) -> Result { - match s { - $($feature_lit => Ok(Feature::$feature),)* - _ => Err(()) - } - } - } - - /// Each function performs run-time feature detection for a single - /// feature. This allow us to use stability attributes on a per feature - /// basis. - /// - /// PLEASE: do not use this, it is an implementation detail subject - /// to change. - #[doc(hidden)] - #[cfg($cfg)] - #[unstable(feature = "stdarch_internal", issue = "none")] - pub mod __is_feature_detected { - $( - - /// PLEASE: do not use this, it is an implementation detail - /// subject to change. - #[inline] - #[doc(hidden)] - #[$stability_attr] - pub fn $feature() -> bool { - $crate::detect::check_for($crate::detect::Feature::$feature) - } - )* - } - }; -} diff --git a/crates/std/crates/std_detect/src/detect/mod.rs b/crates/std/crates/std_detect/src/detect/mod.rs deleted file mode 100644 index c888dd3..0000000 --- a/crates/std/crates/std_detect/src/detect/mod.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! This module implements run-time feature detection. -//! -//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a -//! feature as a string-literal, and return a boolean indicating whether the -//! feature is enabled at run-time or not. -//! -//! These macros do two things: -//! * map the string-literal into an integer stored as a `Feature` enum, -//! * call a `os::check_for(x: Feature)` function that returns `true` if the -//! feature is enabled. -//! -//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs` -//! modules. -//! -//! The `check_for` functions are, in general, Operating System dependent. Most -//! architectures do not allow user-space programs to query the feature bits -//! due to security concerns (x86 is the big exception). These functions are -//! implemented in the `os/{target_os}.rs` modules. - -#[macro_use] -mod macros; - -mod arch; - -// This module needs to be public because the `is_{arch}_feature_detected!` -// macros expand calls to items within it in user crates. -#[doc(hidden)] -#[unstable(feature = "stdarch_internal", issue = "none")] -pub use self::arch::__is_feature_detected; -pub(crate) use self::arch::Feature; - -mod bit; -mod cache; - -cfg_select! { - miri => { - // When running under miri all target-features that are not enabled at - // compile-time are reported as disabled at run-time. - // - // For features for which `cfg(target_feature)` returns true, - // this run-time detection logic is never called. - #[path = "os/other.rs"] - mod os; - } - any(target_arch = "x86", target_arch = "x86_64") => { - // On x86/x86_64 no OS specific functionality is required. - #[path = "os/x86.rs"] - mod os; - } - any(target_os = "linux", target_os = "android") => { - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] - #[path = "os/riscv.rs"] - mod riscv; - #[path = "os/linux/mod.rs"] - mod os; - } - target_os = "freebsd" => { - #[cfg(target_arch = "aarch64")] - #[path = "os/aarch64.rs"] - mod aarch64; - #[path = "os/freebsd/mod.rs"] - mod os; - } - target_os = "openbsd" => { - #[allow(dead_code)] // we don't use code that calls the mrs instruction. - #[cfg(target_arch = "aarch64")] - #[path = "os/aarch64.rs"] - mod aarch64; - #[path = "os/openbsd/mod.rs"] - mod os; - } - all(target_os = "windows", any(target_arch = "aarch64", target_arch = "arm64ec")) => { - #[path = "os/windows/aarch64.rs"] - mod os; - } - all(target_vendor = "apple", target_arch = "aarch64") => { - #[path = "os/darwin/aarch64.rs"] - mod os; - } - _ => { - #[path = "os/other.rs"] - mod os; - } -} - -/// Performs run-time feature detection. -#[inline] -#[allow(dead_code)] -fn check_for(x: Feature) -> bool { - cache::test(x as u32) -} - -/// Returns an `Iterator` where -/// `Item.0` is the feature name, and `Item.1` is a `bool` which -/// is `true` if the feature is supported by the host and `false` otherwise. -#[unstable(feature = "stdarch_internal", issue = "none")] -pub fn features() -> impl Iterator { - cfg_select! { - any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "mips", - target_arch = "mips64", - target_arch = "loongarch32", - target_arch = "loongarch64", - target_arch = "s390x", - ) => { - (0_u8..Feature::_last as u8).map(|discriminant: u8| { - #[allow(bindings_with_variant_name)] // RISC-V has Feature::f - let f: Feature = unsafe { core::mem::transmute(discriminant) }; - let name: &'static str = f.to_str(); - let enabled: bool = check_for(f); - (name, enabled) - }) - } - _ => None.into_iter(), - } -} diff --git a/crates/std/crates/std_detect/src/detect/os/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/aarch64.rs deleted file mode 100644 index 3232e43..0000000 --- a/crates/std/crates/std_detect/src/detect/os/aarch64.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction. -//! -//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use -//! privileged system registers from userspace to check CPU feature support. -//! -//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1 -//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc. -//! Each part of the register indicates the level of support for a certain feature, e.g. -//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported. -//! -//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb), -//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947). -//! -//! References: -//! -//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp) -//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt) -//! - [ARM documentation](https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers?lang=en) - -use core::arch::asm; - -use crate::detect::{Feature, cache}; - -/// Try to read the features from the system registers. -/// -/// This will cause SIGILL if the current OS is not trapping the mrs instruction. -pub(crate) fn detect_features() -> cache::Initializer { - // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 - let aa64isar0: u64; - unsafe { - asm!( - "mrs {}, ID_AA64ISAR0_EL1", - out(reg) aa64isar0, - options(pure, nomem, preserves_flags, nostack) - ); - } - - // ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1 - let aa64isar1: u64; - unsafe { - asm!( - "mrs {}, ID_AA64ISAR1_EL1", - out(reg) aa64isar1, - options(pure, nomem, preserves_flags, nostack) - ); - } - - // ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2 - let aa64mmfr2: u64; - unsafe { - asm!( - "mrs {}, ID_AA64MMFR2_EL1", - out(reg) aa64mmfr2, - options(pure, nomem, preserves_flags, nostack) - ); - } - - // ID_AA64PFR0_EL1 - Processor Feature Register 0 - let aa64pfr0: u64; - unsafe { - asm!( - "mrs {}, ID_AA64PFR0_EL1", - out(reg) aa64pfr0, - options(pure, nomem, preserves_flags, nostack) - ); - } - - parse_system_registers(aa64isar0, aa64isar1, aa64mmfr2, Some(aa64pfr0)) -} - -pub(crate) fn parse_system_registers( - aa64isar0: u64, - aa64isar1: u64, - aa64mmfr2: u64, - aa64pfr0: Option, -) -> cache::Initializer { - let mut value = cache::Initializer::default(); - - let mut enable_feature = |f, enable| { - if enable { - value.set(f as u32); - } - }; - - // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0 - enable_feature(Feature::pmull, bits_shift(aa64isar0, 7, 4) >= 2); - enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 2); - enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1); - - // ID_AA64PFR0_EL1 - Processor Feature Register 0 - if let Some(aa64pfr0) = aa64pfr0 { - let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; - let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; - let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; - let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; - enable_feature(Feature::fp, fp); - enable_feature(Feature::fp16, fphp); - // SIMD support requires float support - if half-floats are - // supported, it also requires half-float support: - enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); - // SIMD extensions require SIMD support: - enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 2); - let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; - let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; - enable_feature(Feature::sha2, asimd && sha1 && sha2); - enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); - enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1); - enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); - } - - // ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1 - // Check for either APA or API field - enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1); - enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1); - // Check for either GPA or GPI field - enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1); - - // ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2 - enable_feature(Feature::lse2, bits_shift(aa64mmfr2, 35, 32) >= 1); - - value -} - -#[inline] -fn bits_shift(x: u64, high: usize, low: usize) -> u64 { - (x >> low) & ((1 << (high - low + 1)) - 1) -} diff --git a/crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs deleted file mode 100644 index a23d65a..0000000 --- a/crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! Run-time feature detection for aarch64 on Darwin (macOS/iOS/tvOS/watchOS/visionOS). -//! -//! - -use core::ffi::CStr; - -use crate::detect::{Feature, cache}; - -#[inline] -fn _sysctlbyname(name: &CStr) -> bool { - use libc; - - let mut enabled: i32 = 0; - let mut enabled_len: usize = 4; - let enabled_ptr = &mut enabled as *mut i32 as *mut libc::c_void; - - let ret = unsafe { - libc::sysctlbyname(name.as_ptr(), enabled_ptr, &mut enabled_len, core::ptr::null_mut(), 0) - }; - - match ret { - 0 => enabled != 0, - _ => false, - } -} - -/// Try to read the features using sysctlbyname. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - - let mut enable_feature = |f, enable| { - if enable { - value.set(f as u32); - } - }; - - // Armv8.0 features not using the standard identifiers - let fp = _sysctlbyname(c"hw.optional.floatingpoint"); - let asimd = _sysctlbyname(c"hw.optional.AdvSIMD"); - let crc_old = _sysctlbyname(c"hw.optional.armv8_crc32"); - - // Armv8 and Armv9 features using the standard identifiers - let aes = _sysctlbyname(c"hw.optional.arm.FEAT_AES"); - let bf16 = _sysctlbyname(c"hw.optional.arm.FEAT_BF16"); - let bti = _sysctlbyname(c"hw.optional.arm.FEAT_BTI"); - let crc = _sysctlbyname(c"hw.optional.arm.FEAT_CRC32"); - let cssc = _sysctlbyname(c"hw.optional.arm.FEAT_CSSC"); - let dit = _sysctlbyname(c"hw.optional.arm.FEAT_DIT"); - let dotprod = _sysctlbyname(c"hw.optional.arm.FEAT_DotProd"); - let dpb = _sysctlbyname(c"hw.optional.arm.FEAT_DPB"); - let dpb2 = _sysctlbyname(c"hw.optional.arm.FEAT_DPB2"); - let ecv = _sysctlbyname(c"hw.optional.arm.FEAT_ECV"); - let fcma = _sysctlbyname(c"hw.optional.arm.FEAT_FCMA"); - let fhm = _sysctlbyname(c"hw.optional.arm.FEAT_FHM"); - let flagm = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM"); - let flagm2 = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM2"); - let fp16 = _sysctlbyname(c"hw.optional.arm.FEAT_FP16"); - let frintts = _sysctlbyname(c"hw.optional.arm.FEAT_FRINTTS"); - let hbc = _sysctlbyname(c"hw.optional.arm.FEAT_HBC"); - let i8mm = _sysctlbyname(c"hw.optional.arm.FEAT_I8MM"); - let jsconv = _sysctlbyname(c"hw.optional.arm.FEAT_JSCVT"); - let rcpc = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC"); - let rcpc2 = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC2"); - let lse = _sysctlbyname(c"hw.optional.arm.FEAT_LSE"); - let lse2 = _sysctlbyname(c"hw.optional.arm.FEAT_LSE2"); - let mte = _sysctlbyname(c"hw.optional.arm.FEAT_MTE"); - let mte2 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE2"); - let pauth = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth"); - let pmull = _sysctlbyname(c"hw.optional.arm.FEAT_PMULL"); - let rdm = _sysctlbyname(c"hw.optional.arm.FEAT_RDM"); - let sb = _sysctlbyname(c"hw.optional.arm.FEAT_SB"); - let sha1 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA1"); - let sha256 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA256"); - let sha3 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA3"); - let sha512 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA512"); - let sme = _sysctlbyname(c"hw.optional.arm.FEAT_SME"); - let sme2 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2"); - let sme2p1 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2p1"); - let sme_b16b16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_B16B16"); - let sme_f16f16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F16F16"); - let sme_f64f64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F64F64"); - let sme_i16i64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_I16I64"); - let ssbs = _sysctlbyname(c"hw.optional.arm.FEAT_SSBS"); - let wfxt = _sysctlbyname(c"hw.optional.arm.FEAT_WFxT"); - - // The following features are not exposed by `is_aarch64_feature_detected`, - // but *are* reported by `sysctl`. They are here as documentation that they - // exist, and may potentially be exposed later. - /* - let afp = _sysctlbyname(c"hw.optional.arm.FEAT_AFP"); - let csv2 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV2"); - let csv3 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV3"); - let ebf16 = _sysctlbyname(c"hw.optional.arm.FEAT_EBF16"); - let fpac = _sysctlbyname(c"hw.optional.arm.FEAT_FPAC"); - let fpaccombine = _sysctlbyname(c"hw.optional.arm.FEAT_FPACCOMBINE"); - let mte_async = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_ASYNC"); - let mte_canonical_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_CANONICAL_TAGS"); - let mte_no_address_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_NO_ADDRESS_TAGS"); - let mte_store_only = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_STORE_ONLY"); - let mte3 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE3"); - let mte4 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE4"); - let pacimp = _sysctlbyname(c"hw.optional.arm.FEAT_PACIMP"); - let pauth2 = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth2"); - let rpres = _sysctlbyname(c"hw.optional.arm.FEAT_RPRES"); - let specres = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES"); - let specres2 = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES2"); - */ - - // The following "features" are reported by `sysctl` but are mandatory parts - // of SME or SME2, and so are not exposed separately by - // `is_aarch64_feature_detected`. They are here to document their - // existence, in case they're needed in the future. - /* - let sme_b16f32 = _sysctlbyname(c"hw.optional.arm.SME_B16F32"); - let sme_bi32i32 = _sysctlbyname(c"hw.optional.arm.SME_BI32I32"); - let sme_f16f32 = _sysctlbyname(c"hw.optional.arm.SME_F16F32"); - let sme_f32f32 = _sysctlbyname(c"hw.optional.arm.SME_F32F32"); - let sme_i16i32 = _sysctlbyname(c"hw.optional.arm.SME_I16I32"); - let sme_i8i32 = _sysctlbyname(c"hw.optional.arm.SME_I8I32"); - */ - - enable_feature(Feature::aes, aes && pmull); - enable_feature(Feature::asimd, asimd); - enable_feature(Feature::bf16, bf16); - enable_feature(Feature::bti, bti); - enable_feature(Feature::crc, crc_old || crc); - enable_feature(Feature::cssc, cssc); - enable_feature(Feature::dit, dit); - enable_feature(Feature::dotprod, dotprod); - enable_feature(Feature::dpb, dpb); - enable_feature(Feature::dpb2, dpb2); - enable_feature(Feature::ecv, ecv); - enable_feature(Feature::fcma, fcma); - enable_feature(Feature::fhm, fhm); - enable_feature(Feature::flagm, flagm); - enable_feature(Feature::flagm2, flagm2); - enable_feature(Feature::fp, fp); - enable_feature(Feature::fp16, fp16); - enable_feature(Feature::frintts, frintts); - enable_feature(Feature::hbc, hbc); - enable_feature(Feature::i8mm, i8mm); - enable_feature(Feature::jsconv, jsconv); - enable_feature(Feature::lse, lse); - enable_feature(Feature::lse2, lse2); - enable_feature(Feature::mte, mte && mte2); - enable_feature(Feature::paca, pauth); - enable_feature(Feature::pacg, pauth); - enable_feature(Feature::pmull, aes && pmull); - enable_feature(Feature::rcpc, rcpc); - enable_feature(Feature::rcpc2, rcpc2); - enable_feature(Feature::rdm, rdm); - enable_feature(Feature::sb, sb); - enable_feature(Feature::sha2, sha1 && sha256 && asimd); - enable_feature(Feature::sha3, sha512 && sha3 && asimd); - enable_feature(Feature::sme, sme); - enable_feature(Feature::sme2, sme2); - enable_feature(Feature::sme2p1, sme2p1); - enable_feature(Feature::sme_b16b16, sme_b16b16); - enable_feature(Feature::sme_f16f16, sme_f16f16); - enable_feature(Feature::sme_f64f64, sme_f64f64); - enable_feature(Feature::sme_i16i64, sme_i16i64); - enable_feature(Feature::ssbs, ssbs); - enable_feature(Feature::wfxt, wfxt); - - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/freebsd/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/freebsd/aarch64.rs deleted file mode 100644 index ccc48f5..0000000 --- a/crates/std/crates/std_detect/src/detect/os/freebsd/aarch64.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Run-time feature detection for Aarch64 on FreeBSD. - -pub(crate) use super::super::aarch64::detect_features; diff --git a/crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs b/crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs deleted file mode 100644 index 0a15156..0000000 --- a/crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Run-time feature detection for ARM on FreeBSD - -use super::auxvec; -use crate::detect::{Feature, cache}; - -// Defined in machine/elf.h. -// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/arm/include/elf.h -const HWCAP_NEON: usize = 0x00001000; -const HWCAP2_AES: usize = 0x00000001; -const HWCAP2_PMULL: usize = 0x00000002; -const HWCAP2_SHA1: usize = 0x00000004; -const HWCAP2_SHA2: usize = 0x00000008; -const HWCAP2_CRC32: usize = 0x00000010; - -/// Try to read the features from the auxiliary vector -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::neon, auxv.hwcap & HWCAP_NEON != 0); - enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & HWCAP2_PMULL != 0); - enable_feature(&mut value, Feature::crc, auxv.hwcap2 & HWCAP2_CRC32 != 0); - enable_feature(&mut value, Feature::aes, auxv.hwcap2 & HWCAP2_AES != 0); - // SHA2 requires SHA1 & SHA2 features - let sha1 = auxv.hwcap2 & HWCAP2_SHA1 != 0; - let sha2 = auxv.hwcap2 & HWCAP2_SHA2 != 0; - enable_feature(&mut value, Feature::sha2, sha1 && sha2); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs b/crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs deleted file mode 100644 index 2a7b87c..0000000 --- a/crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Parses ELF auxiliary vectors. -#![cfg_attr( - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "riscv64" - ), - allow(dead_code) -)] - -/// Cache HWCAP bitfields of the ELF Auxiliary Vector. -/// -/// If an entry cannot be read all the bits in the bitfield are set to zero. -/// This should be interpreted as all the features being disabled. -#[derive(Debug, Copy, Clone)] -pub(crate) struct AuxVec { - pub hwcap: usize, - pub hwcap2: usize, -} - -/// ELF Auxiliary Vector -/// -/// The auxiliary vector is a memory region in a running ELF program's stack -/// composed of (key: usize, value: usize) pairs. -/// -/// The keys used in the aux vector are platform dependent. For FreeBSD, they are -/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given -/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. -/// -/// Note that run-time feature detection is not invoked for features that can -/// be detected at compile-time. -/// -/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707 -pub(crate) fn auxv() -> Result { - let hwcap = archauxv(libc::AT_HWCAP); - let hwcap2 = archauxv(libc::AT_HWCAP2); - // Zero could indicate that no features were detected, but it's also used to - // indicate an error. In particular, on many platforms AT_HWCAP2 will be - // legitimately zero, since it contains the most recent feature flags. - if hwcap != 0 || hwcap2 != 0 { - return Ok(AuxVec { hwcap, hwcap2 }); - } - Err(()) -} - -/// Tries to read the `key` from the auxiliary vector. -fn archauxv(key: libc::c_int) -> usize { - const OUT_LEN: libc::c_int = core::mem::size_of::() as libc::c_int; - let mut out: libc::c_ulong = 0; - unsafe { - // elf_aux_info is available on FreeBSD 12.0+ and 11.4+: - // https://github.com/freebsd/freebsd-src/commit/0b08ae2120cdd08c20a2b806e2fcef4d0a36c470 - // https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/auxv.h - // FreeBSD 11 support in std has been removed in Rust 1.75 (https://github.com/rust-lang/rust/pull/114521), - // so we can safely use this function. - let res = - libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN); - // If elf_aux_info fails, `out` will be left at zero (which is the proper default value). - debug_assert!(res == 0 || out == 0); - } - out as usize -} diff --git a/crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs b/crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs deleted file mode 100644 index 7de9250..0000000 --- a/crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Run-time feature detection on FreeBSD - -mod auxvec; - -cfg_select! { - target_arch = "aarch64" => { - mod aarch64; - pub(crate) use self::aarch64::detect_features; - } - target_arch = "arm" => { - mod arm; - pub(crate) use self::arm::detect_features; - } - target_arch = "powerpc64" => { - mod powerpc; - pub(crate) use self::powerpc::detect_features; - } - _ => { - use crate::detect::cache; - /// Performs run-time feature detection. - pub(crate) fn detect_features() -> cache::Initializer { - cache::Initializer::default() - } - } -} diff --git a/crates/std/crates/std_detect/src/detect/os/freebsd/powerpc.rs b/crates/std/crates/std_detect/src/detect/os/freebsd/powerpc.rs deleted file mode 100644 index d03af68..0000000 --- a/crates/std/crates/std_detect/src/detect/os/freebsd/powerpc.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Run-time feature detection for PowerPC on FreeBSD. - -use super::auxvec; -use crate::detect::{Feature, cache}; - -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs deleted file mode 100644 index b733b8a..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs +++ /dev/null @@ -1,409 +0,0 @@ -//! Run-time feature detection for Aarch64 on Linux. - -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -/// Try to read the features from the auxiliary vector. -pub(crate) fn detect_features() -> cache::Initializer { - #[cfg(target_os = "android")] - let is_exynos9810 = { - // Samsung Exynos 9810 has a bug that big and little cores have different - // ISAs. And on older Android (pre-9), the kernel incorrectly reports - // that features available only on some cores are available on all cores. - // https://reviews.llvm.org/D114523 - let mut arch = [0_u8; libc::PROP_VALUE_MAX as usize]; - let len = unsafe { - libc::__system_property_get(c"ro.arch".as_ptr(), arch.as_mut_ptr() as *mut libc::c_char) - }; - // On Exynos, ro.arch is not available on Android 12+, but it is fine - // because Android 9+ includes the fix. - len > 0 && arch.starts_with(b"exynos9810") - }; - #[cfg(not(target_os = "android"))] - let is_exynos9810 = false; - - if let Ok(auxv) = auxvec::auxv() { - let hwcap: AtHwcap = auxv.into(); - return hwcap.cache(is_exynos9810); - } - cache::Initializer::default() -} - -/// These values are part of the platform-specific [asm/hwcap.h][hwcap] . -/// -/// The names match those used for cpuinfo. -/// -/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h -#[derive(Debug, Default, PartialEq)] -struct AtHwcap { - // AT_HWCAP - fp: bool, - asimd: bool, - // evtstrm: No LLVM support. - aes: bool, - pmull: bool, - sha1: bool, - sha2: bool, - crc32: bool, - atomics: bool, - fphp: bool, - asimdhp: bool, - // cpuid: No LLVM support. - asimdrdm: bool, - jscvt: bool, - fcma: bool, - lrcpc: bool, - dcpop: bool, - sha3: bool, - sm3: bool, - sm4: bool, - asimddp: bool, - sha512: bool, - sve: bool, - fhm: bool, - dit: bool, - uscat: bool, - ilrcpc: bool, - flagm: bool, - ssbs: bool, - sb: bool, - paca: bool, - pacg: bool, - - // AT_HWCAP2 - dcpodp: bool, - sve2: bool, - sveaes: bool, - svepmull: bool, - svebitperm: bool, - svesha3: bool, - svesm4: bool, - flagm2: bool, - frint: bool, - // svei8mm: See i8mm feature. - svef32mm: bool, - svef64mm: bool, - // svebf16: See bf16 feature. - i8mm: bool, - bf16: bool, - // dgh: No LLVM support. - rng: bool, - bti: bool, - mte: bool, - ecv: bool, - // afp: bool, - // rpres: bool, - // mte3: bool, - sme: bool, - smei16i64: bool, - smef64f64: bool, - // smei8i32: bool, - // smef16f32: bool, - // smeb16f32: bool, - // smef32f32: bool, - smefa64: bool, - wfxt: bool, - // ebf16: bool, - // sveebf16: bool, - cssc: bool, - // rprfm: bool, - sve2p1: bool, - sme2: bool, - sme2p1: bool, - // smei16i32: bool, - // smebi32i32: bool, - smeb16b16: bool, - smef16f16: bool, - mops: bool, - hbc: bool, - sveb16b16: bool, - lrcpc3: bool, - lse128: bool, - fpmr: bool, - lut: bool, - faminmax: bool, - f8cvt: bool, - f8fma: bool, - f8dp4: bool, - f8dp2: bool, - f8e4m3: bool, - f8e5m2: bool, - smelutv2: bool, - smef8f16: bool, - smef8f32: bool, - smesf8fma: bool, - smesf8dp4: bool, - smesf8dp2: bool, - // pauthlr: bool, -} - -impl From for AtHwcap { - /// Reads AtHwcap from the auxiliary vector. - fn from(auxv: auxvec::AuxVec) -> Self { - let mut cap = AtHwcap { - fp: bit::test(auxv.hwcap, 0), - asimd: bit::test(auxv.hwcap, 1), - // evtstrm: bit::test(auxv.hwcap, 2), - aes: bit::test(auxv.hwcap, 3), - pmull: bit::test(auxv.hwcap, 4), - sha1: bit::test(auxv.hwcap, 5), - sha2: bit::test(auxv.hwcap, 6), - crc32: bit::test(auxv.hwcap, 7), - atomics: bit::test(auxv.hwcap, 8), - fphp: bit::test(auxv.hwcap, 9), - asimdhp: bit::test(auxv.hwcap, 10), - // cpuid: bit::test(auxv.hwcap, 11), - asimdrdm: bit::test(auxv.hwcap, 12), - jscvt: bit::test(auxv.hwcap, 13), - fcma: bit::test(auxv.hwcap, 14), - lrcpc: bit::test(auxv.hwcap, 15), - dcpop: bit::test(auxv.hwcap, 16), - sha3: bit::test(auxv.hwcap, 17), - sm3: bit::test(auxv.hwcap, 18), - sm4: bit::test(auxv.hwcap, 19), - asimddp: bit::test(auxv.hwcap, 20), - sha512: bit::test(auxv.hwcap, 21), - sve: bit::test(auxv.hwcap, 22), - fhm: bit::test(auxv.hwcap, 23), - dit: bit::test(auxv.hwcap, 24), - uscat: bit::test(auxv.hwcap, 25), - ilrcpc: bit::test(auxv.hwcap, 26), - flagm: bit::test(auxv.hwcap, 27), - ssbs: bit::test(auxv.hwcap, 28), - sb: bit::test(auxv.hwcap, 29), - paca: bit::test(auxv.hwcap, 30), - pacg: bit::test(auxv.hwcap, 31), - - // AT_HWCAP2 - dcpodp: bit::test(auxv.hwcap2, 0), - sve2: bit::test(auxv.hwcap2, 1), - sveaes: bit::test(auxv.hwcap2, 2), - svepmull: bit::test(auxv.hwcap2, 3), - svebitperm: bit::test(auxv.hwcap2, 4), - svesha3: bit::test(auxv.hwcap2, 5), - svesm4: bit::test(auxv.hwcap2, 6), - flagm2: bit::test(auxv.hwcap2, 7), - frint: bit::test(auxv.hwcap2, 8), - // svei8mm: bit::test(auxv.hwcap2, 9), - svef32mm: bit::test(auxv.hwcap2, 10), - svef64mm: bit::test(auxv.hwcap2, 11), - // svebf16: bit::test(auxv.hwcap2, 12), - i8mm: bit::test(auxv.hwcap2, 13), - bf16: bit::test(auxv.hwcap2, 14), - // dgh: bit::test(auxv.hwcap2, 15), - rng: bit::test(auxv.hwcap2, 16), - bti: bit::test(auxv.hwcap2, 17), - mte: bit::test(auxv.hwcap2, 18), - ecv: bit::test(auxv.hwcap2, 19), - // afp: bit::test(auxv.hwcap2, 20), - // rpres: bit::test(auxv.hwcap2, 21), - // mte3: bit::test(auxv.hwcap2, 22), - sme: bit::test(auxv.hwcap2, 23), - smei16i64: bit::test(auxv.hwcap2, 24), - smef64f64: bit::test(auxv.hwcap2, 25), - // smei8i32: bit::test(auxv.hwcap2, 26), - // smef16f32: bit::test(auxv.hwcap2, 27), - // smeb16f32: bit::test(auxv.hwcap2, 28), - // smef32f32: bit::test(auxv.hwcap2, 29), - smefa64: bit::test(auxv.hwcap2, 30), - wfxt: bit::test(auxv.hwcap2, 31), - ..Default::default() - }; - - // Hardware capabilities from bits 32 to 63 should only - // be tested on LP64 targets with 64 bits `usize`. - // On ILP32 targets like `aarch64-unknown-linux-gnu_ilp32`, - // these hardware capabilities will default to `false`. - // https://github.com/rust-lang/rust/issues/146230 - #[cfg(target_pointer_width = "64")] - { - // cap.ebf16: bit::test(auxv.hwcap2, 32); - // cap.sveebf16: bit::test(auxv.hwcap2, 33); - cap.cssc = bit::test(auxv.hwcap2, 34); - // cap.rprfm: bit::test(auxv.hwcap2, 35); - cap.sve2p1 = bit::test(auxv.hwcap2, 36); - cap.sme2 = bit::test(auxv.hwcap2, 37); - cap.sme2p1 = bit::test(auxv.hwcap2, 38); - // cap.smei16i32 = bit::test(auxv.hwcap2, 39); - // cap.smebi32i32 = bit::test(auxv.hwcap2, 40); - cap.smeb16b16 = bit::test(auxv.hwcap2, 41); - cap.smef16f16 = bit::test(auxv.hwcap2, 42); - cap.mops = bit::test(auxv.hwcap2, 43); - cap.hbc = bit::test(auxv.hwcap2, 44); - cap.sveb16b16 = bit::test(auxv.hwcap2, 45); - cap.lrcpc3 = bit::test(auxv.hwcap2, 46); - cap.lse128 = bit::test(auxv.hwcap2, 47); - cap.fpmr = bit::test(auxv.hwcap2, 48); - cap.lut = bit::test(auxv.hwcap2, 49); - cap.faminmax = bit::test(auxv.hwcap2, 50); - cap.f8cvt = bit::test(auxv.hwcap2, 51); - cap.f8fma = bit::test(auxv.hwcap2, 52); - cap.f8dp4 = bit::test(auxv.hwcap2, 53); - cap.f8dp2 = bit::test(auxv.hwcap2, 54); - cap.f8e4m3 = bit::test(auxv.hwcap2, 55); - cap.f8e5m2 = bit::test(auxv.hwcap2, 56); - cap.smelutv2 = bit::test(auxv.hwcap2, 57); - cap.smef8f16 = bit::test(auxv.hwcap2, 58); - cap.smef8f32 = bit::test(auxv.hwcap2, 59); - cap.smesf8fma = bit::test(auxv.hwcap2, 60); - cap.smesf8dp4 = bit::test(auxv.hwcap2, 61); - cap.smesf8dp2 = bit::test(auxv.hwcap2, 62); - // cap.pauthlr = bit::test(auxv.hwcap2, ??); - } - cap - } -} - -impl AtHwcap { - /// Initializes the cache from the feature -bits. - /// - /// The feature dependencies here come directly from LLVM's feature definitions: - /// https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AArch64/AArch64.td - fn cache(self, is_exynos9810: bool) -> cache::Initializer { - let mut value = cache::Initializer::default(); - { - let mut enable_feature = |f, enable| { - if enable { - value.set(f as u32); - } - }; - - // Samsung Exynos 9810 has a bug that big and little cores have different - // ISAs. And on older Android (pre-9), the kernel incorrectly reports - // that features available only on some cores are available on all cores. - // So, only check features that are known to be available on exynos-m3: - // $ rustc --print cfg --target aarch64-linux-android -C target-cpu=exynos-m3 | grep target_feature - // See also https://github.com/rust-lang/stdarch/pull/1378#discussion_r1103748342. - if is_exynos9810 { - enable_feature(Feature::fp, self.fp); - enable_feature(Feature::crc, self.crc32); - // ASIMD support requires float support - if half-floats are - // supported, it also requires half-float support: - let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp); - enable_feature(Feature::asimd, asimd); - // Cryptographic extensions require ASIMD - // AES also covers FEAT_PMULL - enable_feature(Feature::aes, self.aes && self.pmull && asimd); - enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd); - return value; - } - - enable_feature(Feature::fp, self.fp); - // Half-float support requires float support - enable_feature(Feature::fp16, self.fp && self.fphp); - // FHM (fp16fml in LLVM) requires half float support - enable_feature(Feature::fhm, self.fphp && self.fhm); - enable_feature(Feature::pmull, self.pmull); - enable_feature(Feature::crc, self.crc32); - enable_feature(Feature::lse, self.atomics); - enable_feature(Feature::lse2, self.uscat); - enable_feature(Feature::lse128, self.lse128 && self.atomics); - enable_feature(Feature::rcpc, self.lrcpc); - // RCPC2 (rcpc-immo in LLVM) requires RCPC support - let rcpc2 = self.ilrcpc && self.lrcpc; - enable_feature(Feature::rcpc2, rcpc2); - enable_feature(Feature::rcpc3, self.lrcpc3 && rcpc2); - enable_feature(Feature::dit, self.dit); - enable_feature(Feature::flagm, self.flagm); - enable_feature(Feature::flagm2, self.flagm2); - enable_feature(Feature::ssbs, self.ssbs); - enable_feature(Feature::sb, self.sb); - enable_feature(Feature::paca, self.paca); - enable_feature(Feature::pacg, self.pacg); - // enable_feature(Feature::pauth_lr, self.pauthlr); - enable_feature(Feature::dpb, self.dcpop); - enable_feature(Feature::dpb2, self.dcpodp); - enable_feature(Feature::rand, self.rng); - enable_feature(Feature::bti, self.bti); - enable_feature(Feature::mte, self.mte); - // jsconv requires float support - enable_feature(Feature::jsconv, self.jscvt && self.fp); - enable_feature(Feature::rdm, self.asimdrdm); - enable_feature(Feature::dotprod, self.asimddp); - enable_feature(Feature::frintts, self.frint); - - // FEAT_I8MM & FEAT_BF16 also include optional SVE components which linux exposes - // separately. We ignore that distinction here. - enable_feature(Feature::i8mm, self.i8mm); - enable_feature(Feature::bf16, self.bf16); - - // ASIMD support requires float support - if half-floats are - // supported, it also requires half-float support: - let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp); - enable_feature(Feature::asimd, asimd); - // ASIMD extensions require ASIMD support: - enable_feature(Feature::fcma, self.fcma && asimd); - enable_feature(Feature::sve, self.sve && asimd); - - // SVE extensions require SVE & ASIMD - enable_feature(Feature::f32mm, self.svef32mm && self.sve && asimd); - enable_feature(Feature::f64mm, self.svef64mm && self.sve && asimd); - - // Cryptographic extensions require ASIMD - enable_feature(Feature::aes, self.aes && asimd); - enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd); - // SHA512/SHA3 require SHA1 & SHA256 - enable_feature( - Feature::sha3, - self.sha512 && self.sha3 && self.sha1 && self.sha2 && asimd, - ); - enable_feature(Feature::sm4, self.sm3 && self.sm4 && asimd); - - // SVE2 requires SVE - let sve2 = self.sve2 && self.sve && asimd; - enable_feature(Feature::sve2, sve2); - enable_feature(Feature::sve2p1, self.sve2p1 && sve2); - // SVE2 extensions require SVE2 and crypto features - enable_feature(Feature::sve2_aes, self.sveaes && self.svepmull && sve2 && self.aes); - enable_feature(Feature::sve2_sm4, self.svesm4 && sve2 && self.sm3 && self.sm4); - enable_feature( - Feature::sve2_sha3, - self.svesha3 && sve2 && self.sha512 && self.sha3 && self.sha1 && self.sha2, - ); - enable_feature(Feature::sve2_bitperm, self.svebitperm && self.sve2); - enable_feature(Feature::sve_b16b16, self.bf16 && self.sveb16b16); - enable_feature(Feature::hbc, self.hbc); - enable_feature(Feature::mops, self.mops); - enable_feature(Feature::ecv, self.ecv); - enable_feature(Feature::lut, self.lut); - enable_feature(Feature::cssc, self.cssc); - enable_feature(Feature::fpmr, self.fpmr); - enable_feature(Feature::faminmax, self.faminmax); - let fp8 = self.f8cvt && self.faminmax && self.lut && self.bf16; - enable_feature(Feature::fp8, fp8); - let fp8fma = self.f8fma && fp8; - enable_feature(Feature::fp8fma, fp8fma); - let fp8dot4 = self.f8dp4 && fp8fma; - enable_feature(Feature::fp8dot4, fp8dot4); - enable_feature(Feature::fp8dot2, self.f8dp2 && fp8dot4); - enable_feature(Feature::wfxt, self.wfxt); - let sme = self.sme && self.bf16; - enable_feature(Feature::sme, sme); - enable_feature(Feature::sme_i16i64, self.smei16i64 && sme); - enable_feature(Feature::sme_f64f64, self.smef64f64 && sme); - enable_feature(Feature::sme_fa64, self.smefa64 && sme && sve2); - let sme2 = self.sme2 && sme; - enable_feature(Feature::sme2, sme2); - enable_feature(Feature::sme2p1, self.sme2p1 && sme2); - enable_feature( - Feature::sme_b16b16, - sme2 && self.bf16 && self.sveb16b16 && self.smeb16b16, - ); - enable_feature(Feature::sme_f16f16, self.smef16f16 && sme2); - enable_feature(Feature::sme_lutv2, self.smelutv2); - let sme_f8f32 = self.smef8f32 && sme2 && fp8; - enable_feature(Feature::sme_f8f32, sme_f8f32); - enable_feature(Feature::sme_f8f16, self.smef8f16 && sme_f8f32); - let ssve_fp8fma = self.smesf8fma && sme2 && fp8; - enable_feature(Feature::ssve_fp8fma, ssve_fp8fma); - let ssve_fp8dot4 = self.smesf8dp4 && ssve_fp8fma; - enable_feature(Feature::ssve_fp8dot4, ssve_fp8dot4); - enable_feature(Feature::ssve_fp8dot2, self.smesf8dp2 && ssve_fp8dot4); - } - value - } -} - -#[cfg(target_endian = "little")] -#[cfg(test)] -mod tests; diff --git a/crates/std/crates/std_detect/src/detect/os/linux/aarch64/tests.rs b/crates/std/crates/std_detect/src/detect/os/linux/aarch64/tests.rs deleted file mode 100644 index 4d7c9a4..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/aarch64/tests.rs +++ /dev/null @@ -1,70 +0,0 @@ -use super::auxvec::auxv_from_file; -use super::*; -// The baseline hwcaps used in the (artificial) auxv test files. -fn baseline_hwcaps() -> AtHwcap { - AtHwcap { - fp: true, - asimd: true, - aes: true, - pmull: true, - sha1: true, - sha2: true, - crc32: true, - atomics: true, - fphp: true, - asimdhp: true, - asimdrdm: true, - lrcpc: true, - dcpop: true, - asimddp: true, - ssbs: true, - ..AtHwcap::default() - } -} - -#[test] -fn linux_empty_hwcap2_aarch64() { - let file = concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv" - ); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!(AtHwcap::from(v), baseline_hwcaps()); -} -#[test] -fn linux_no_hwcap2_aarch64() { - let file = - concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv"); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!(AtHwcap::from(v), baseline_hwcaps()); -} -#[test] -fn linux_hwcap2_aarch64() { - let file = - concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-hwcap2-aarch64.auxv"); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!( - AtHwcap::from(v), - AtHwcap { - // Some other HWCAP bits. - paca: true, - pacg: true, - // HWCAP2-only bits. - dcpodp: true, - frint: true, - rng: true, - bti: true, - mte: true, - ..baseline_hwcaps() - } - ); -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/arm.rs b/crates/std/crates/std_detect/src/detect/os/linux/arm.rs deleted file mode 100644 index bbb1732..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/arm.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Run-time feature detection for ARM on Linux. - -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -/// Try to read the features from the auxiliary vector. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - // The values are part of the platform-specific [asm/hwcap.h][hwcap] - // - // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm/include/uapi/asm/hwcap.h - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::i8mm, bit::test(auxv.hwcap, 27)); - enable_feature(&mut value, Feature::dotprod, bit::test(auxv.hwcap, 24)); - enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12)); - enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1)); - enable_feature(&mut value, Feature::crc, bit::test(auxv.hwcap2, 4)); - enable_feature(&mut value, Feature::aes, bit::test(auxv.hwcap2, 0)); - // SHA2 requires SHA1 & SHA2 features - enable_feature( - &mut value, - Feature::sha2, - bit::test(auxv.hwcap2, 2) && bit::test(auxv.hwcap2, 3), - ); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs b/crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs deleted file mode 100644 index c0bbc7d..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! Parses ELF auxiliary vectors. -#![allow(dead_code)] - -pub(crate) const AT_NULL: usize = 0; - -/// Key to access the CPU Hardware capabilities bitfield. -pub(crate) const AT_HWCAP: usize = 16; -/// Key to access the CPU Hardware capabilities 2 bitfield. -#[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", -))] -pub(crate) const AT_HWCAP2: usize = 26; - -/// Cache HWCAP bitfields of the ELF Auxiliary Vector. -/// -/// If an entry cannot be read all the bits in the bitfield are set to zero. -/// This should be interpreted as all the features being disabled. -#[derive(Debug, Copy, Clone)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) struct AuxVec { - pub hwcap: usize, - #[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - ))] - pub hwcap2: usize, -} - -/// ELF Auxiliary Vector -/// -/// The auxiliary vector is a memory region in a running ELF program's stack -/// composed of (key: usize, value: usize) pairs. -/// -/// The keys used in the aux vector are platform dependent. For Linux, they are -/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given -/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. -/// -/// There is no perfect way of reading the auxiliary vector. -/// -/// - If [`getauxval`] is linked to the binary we use it, and otherwise it will -/// try to read `/proc/self/auxv`. -/// - If that fails, this function returns an error. -/// -/// Note that run-time feature detection is not invoked for features that can -/// be detected at compile-time. -/// -/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and -/// `*-android*` targets rather than `dlsym` it because we can safely assume -/// `getauxval` is linked to the binary. -/// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) -/// have glibc requirements higher than [glibc 2.16 that added `getauxval`](https://sourceware.org/legacy-ml/libc-announce/2012/msg00000.html). -/// - `*-linux-musl*` targets ([at least since Rust 1.15](https://github.com/rust-lang/rust/blob/1.15.0/src/ci/docker/x86_64-musl/build-musl.sh#L15)) -/// use musl newer than [musl 1.1.0 that added `getauxval`](https://git.musl-libc.org/cgit/musl/tree/WHATSNEW?h=v1.1.0#n1197) -/// - `*-linux-ohos*` targets use a [fork of musl 1.2](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/native-lib/musl.md) -/// - `*-android*` targets ([since Rust 1.68](https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html)) -/// have the minimum supported API level higher than [Android 4.3 (API level 18) that added `getauxval`](https://github.com/aosp-mirror/platform_bionic/blob/d3ebc2f7c49a9893b114124d4a6b315f3a328764/libc/include/sys/auxv.h#L49). -/// -/// For more information about when `getauxval` is available check the great -/// [`auxv` crate documentation][auxv_docs]. -/// -/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h -/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/ -/// [`getauxval`]: https://man7.org/linux/man-pages/man3/getauxval.3.html -pub(crate) fn auxv() -> Result { - // Try to call a getauxval function. - if let Ok(hwcap) = getauxval(AT_HWCAP) { - // Targets with only AT_HWCAP: - #[cfg(any( - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "mips", - target_arch = "mips64", - target_arch = "loongarch32", - target_arch = "loongarch64", - ))] - { - // Zero could indicate that no features were detected, but it's also used to indicate - // an error. In either case, try the fallback. - if hwcap != 0 { - return Ok(AuxVec { hwcap }); - } - } - - // Targets with AT_HWCAP and AT_HWCAP2: - #[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - ))] - { - if let Ok(hwcap2) = getauxval(AT_HWCAP2) { - // Zero could indicate that no features were detected, but it's also used to indicate - // an error. In particular, on many platforms AT_HWCAP2 will be legitimately zero, - // since it contains the most recent feature flags. Use the fallback only if no - // features were detected at all. - if hwcap != 0 || hwcap2 != 0 { - return Ok(AuxVec { hwcap, hwcap2 }); - } - } - } - - // Intentionnaly not used - let _ = hwcap; - } - - // If calling getauxval fails, try to read the auxiliary vector from - // its file: - auxv_from_file("/proc/self/auxv").map_err(|_| ()) -} - -/// Tries to read the `key` from the auxiliary vector by calling the -/// `getauxval` function. If the function is not linked, this function return `Err`. -fn getauxval(key: usize) -> Result { - type F = unsafe extern "C" fn(libc::c_ulong) -> libc::c_ulong; - cfg_select! { - any( - all( - target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos"), - ), - target_os = "android", - ) => { - let ffi_getauxval: F = libc::getauxval; - } - _ => { - let ffi_getauxval: F = unsafe { - let ptr = libc::dlsym(libc::RTLD_DEFAULT, c"getauxval".as_ptr()); - if ptr.is_null() { - return Err(()); - } - core::mem::transmute(ptr) - }; - } - } - Ok(unsafe { ffi_getauxval(key as libc::c_ulong) as usize }) -} - -/// Tries to read the auxiliary vector from the `file`. If this fails, this -/// function returns `Err`. -pub(super) fn auxv_from_file(file: &str) -> Result { - let file = super::read_file(file)?; - auxv_from_file_bytes(&file) -} - -/// Read auxiliary vector from a slice of bytes. -pub(super) fn auxv_from_file_bytes(bytes: &[u8]) -> Result { - // See . - // - // The auxiliary vector contains at most 34 (key,value) fields: from - // `AT_MINSIGSTKSZ` to `AT_NULL`, but its number may increase. - let len = bytes.len(); - let mut buf = alloc::vec![0_usize; 1 + len / core::mem::size_of::()]; - unsafe { - core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr() as *mut u8, len); - } - - auxv_from_buf(&buf) -} - -/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this -/// function returns `Err`. -fn auxv_from_buf(buf: &[usize]) -> Result { - // Targets with only AT_HWCAP: - #[cfg(any( - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "mips", - target_arch = "mips64", - target_arch = "loongarch32", - target_arch = "loongarch64", - ))] - { - for el in buf.chunks(2) { - match el[0] { - AT_NULL => break, - AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }), - _ => (), - } - } - } - // Targets with AT_HWCAP and AT_HWCAP2: - #[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - ))] - { - let mut hwcap = None; - // For some platforms, AT_HWCAP2 was added recently, so let it default to zero. - let mut hwcap2 = 0; - for el in buf.chunks(2) { - match el[0] { - AT_NULL => break, - AT_HWCAP => hwcap = Some(el[1]), - AT_HWCAP2 => hwcap2 = el[1], - _ => (), - } - } - - if let Some(hwcap) = hwcap { - return Ok(AuxVec { hwcap, hwcap2 }); - } - } - // Suppress unused variable - let _ = buf; - Err(alloc::string::String::from("hwcap not found")) -} - -#[cfg(test)] -mod tests; diff --git a/crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs b/crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs deleted file mode 100644 index 88f0d6d..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs +++ /dev/null @@ -1,108 +0,0 @@ -use super::*; - -// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv -// does not always contain the AT_HWCAP key under qemu. -#[cfg(any( - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", -))] -#[test] -fn auxv_crate() { - let v = auxv(); - if let Ok(hwcap) = getauxval(AT_HWCAP) { - let rt_hwcap = v.expect("failed to find hwcap key").hwcap; - assert_eq!(rt_hwcap, hwcap); - } - - // Targets with AT_HWCAP and AT_HWCAP2: - #[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - ))] - { - if let Ok(hwcap2) = getauxval(AT_HWCAP2) { - let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2; - assert_eq!(rt_hwcap2, hwcap2); - } - } -} - -#[test] -fn auxv_dump() { - if let Ok(auxvec) = auxv() { - println!("{:?}", auxvec); - } else { - println!("both getauxval() and reading /proc/self/auxv failed!"); - } -} - -cfg_select! { - target_arch = "arm" => { - // The tests below can be executed under qemu, where we do not have access to the test - // files on disk, so we need to embed them with `include_bytes!`. - #[test] - fn linux_rpi3() { - let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv")); - let v = auxv_from_file_bytes(auxv).unwrap(); - assert_eq!(v.hwcap, 4174038); - assert_eq!(v.hwcap2, 16); - } - - #[test] - fn linux_macos_vb() { - let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv")); - // The file contains HWCAP but not HWCAP2. In that case, we treat HWCAP2 as zero. - let v = auxv_from_file_bytes(auxv).unwrap(); - assert_eq!(v.hwcap, 126614527); - assert_eq!(v.hwcap2, 0); - } - } - target_arch = "aarch64" => { - #[cfg(target_endian = "little")] - #[test] - fn linux_artificial_aarch64() { - let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-artificial-aarch64.auxv")); - let v = auxv_from_file_bytes(auxv).unwrap(); - assert_eq!(v.hwcap, 0x0123456789abcdef); - assert_eq!(v.hwcap2, 0x02468ace13579bdf); - } - #[cfg(target_endian = "little")] - #[test] - fn linux_no_hwcap2_aarch64() { - let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv")); - let v = auxv_from_file_bytes(auxv).unwrap(); - // An absent HWCAP2 is treated as zero, and does not prevent acceptance of HWCAP. - assert_ne!(v.hwcap, 0); - assert_eq!(v.hwcap2, 0); - } - } - _ => {} -} - -#[test] -fn auxv_dump_procfs() { - if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") { - println!("{:?}", auxvec); - } else { - println!("reading /proc/self/auxv failed!"); - } -} - -#[cfg(any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", -))] -#[test] -fn auxv_crate_procfs() { - if let Ok(procfs_auxv) = auxv_from_file("/proc/self/auxv") { - assert_eq!(auxv().unwrap(), procfs_auxv); - } -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/loongarch.rs b/crates/std/crates/std_detect/src/detect/os/linux/loongarch.rs deleted file mode 100644 index 7441526..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/loongarch.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! Run-time feature detection for LoongArch on Linux. - -use core::arch::asm; - -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -/// Try to read the features from the auxiliary vector. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, feature, enable| { - if enable { - value.set(feature as u32); - } - }; - - // The values are part of the platform-specific [cpucfg] - // - // [cpucfg]: LoongArch Reference Manual Volume 1: Basic Architecture v1.1 - let cpucfg1: usize; - let cpucfg2: usize; - let cpucfg3: usize; - unsafe { - asm!( - "cpucfg {}, {}", - "cpucfg {}, {}", - "cpucfg {}, {}", - out(reg) cpucfg1, in(reg) 1, - out(reg) cpucfg2, in(reg) 2, - out(reg) cpucfg3, in(reg) 3, - options(pure, nomem, preserves_flags, nostack) - ); - } - enable_feature(&mut value, Feature::_32s, bit::test(cpucfg1, 0) || bit::test(cpucfg1, 1)); - enable_feature(&mut value, Feature::frecipe, bit::test(cpucfg2, 25)); - enable_feature(&mut value, Feature::div32, bit::test(cpucfg2, 26)); - enable_feature(&mut value, Feature::lam_bh, bit::test(cpucfg2, 27)); - enable_feature(&mut value, Feature::lamcas, bit::test(cpucfg2, 28)); - enable_feature(&mut value, Feature::scq, bit::test(cpucfg2, 30)); - enable_feature(&mut value, Feature::ld_seq_sa, bit::test(cpucfg3, 23)); - - // The values are part of the platform-specific [asm/hwcap.h][hwcap] - // - // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/loongarch/include/uapi/asm/hwcap.h - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::f, bit::test(cpucfg2, 1) && bit::test(auxv.hwcap, 3)); - enable_feature(&mut value, Feature::d, bit::test(cpucfg2, 2) && bit::test(auxv.hwcap, 3)); - enable_feature(&mut value, Feature::lsx, bit::test(auxv.hwcap, 4)); - enable_feature(&mut value, Feature::lasx, bit::test(auxv.hwcap, 5)); - enable_feature( - &mut value, - Feature::lbt, - bit::test(auxv.hwcap, 10) && bit::test(auxv.hwcap, 11) && bit::test(auxv.hwcap, 12), - ); - enable_feature(&mut value, Feature::lvz, bit::test(auxv.hwcap, 9)); - enable_feature(&mut value, Feature::ual, bit::test(auxv.hwcap, 2)); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/mips.rs b/crates/std/crates/std_detect/src/detect/os/linux/mips.rs deleted file mode 100644 index 0cfa886..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/mips.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Run-time feature detection for MIPS on Linux. - -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -/// Try to read the features from the auxiliary vector. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - // The values are part of the platform-specific [asm/hwcap.h][hwcap] - // - // [hwcap]: https://github.com/torvalds/linux/blob/master/arch/mips/include/uapi/asm/hwcap.h - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1)); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/mod.rs b/crates/std/crates/std_detect/src/detect/os/linux/mod.rs deleted file mode 100644 index aec94f9..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/mod.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! Run-time feature detection on Linux - -use alloc::vec::Vec; - -mod auxvec; - -fn read_file(orig_path: &str) -> Result, alloc::string::String> { - use alloc::format; - - let mut path = Vec::from(orig_path.as_bytes()); - path.push(0); - - unsafe { - let file = libc::open(path.as_ptr() as *const libc::c_char, libc::O_RDONLY); - if file == -1 { - return Err(format!("Cannot open file at {orig_path}")); - } - - let mut data = Vec::new(); - loop { - data.reserve(4096); - let spare = data.spare_capacity_mut(); - match libc::read(file, spare.as_mut_ptr() as *mut _, spare.len()) { - -1 => { - libc::close(file); - return Err(format!("Error while reading from file at {orig_path}")); - } - 0 => break, - n => data.set_len(data.len() + n as usize), - } - } - - libc::close(file); - Ok(data) - } -} - -cfg_select! { - target_arch = "aarch64" => { - mod aarch64; - pub(crate) use self::aarch64::detect_features; - } - target_arch = "arm" => { - mod arm; - pub(crate) use self::arm::detect_features; - } - any(target_arch = "riscv32", target_arch = "riscv64") => { - mod riscv; - pub(crate) use self::riscv::detect_features; - } - any(target_arch = "mips", target_arch = "mips64") => { - mod mips; - pub(crate) use self::mips::detect_features; - } - any(target_arch = "powerpc", target_arch = "powerpc64") => { - mod powerpc; - pub(crate) use self::powerpc::detect_features; - } - any(target_arch = "loongarch32", target_arch = "loongarch64") => { - mod loongarch; - pub(crate) use self::loongarch::detect_features; - } - target_arch = "s390x" => { - mod s390x; - pub(crate) use self::s390x::detect_features; - } - _ => { - use crate::detect::cache; - /// Performs run-time feature detection. - pub(crate) fn detect_features() -> cache::Initializer { - cache::Initializer::default() - } - } -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs b/crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs deleted file mode 100644 index 6a4f7e7..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Run-time feature detection for PowerPC on Linux. - -use super::auxvec; -use crate::detect::{Feature, cache}; - -/// Try to read the features from the auxiliary vector. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - // The values are part of the platform-specific [asm/cputable.h][cputable] - // - // [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h - if let Ok(auxv) = auxvec::auxv() { - // note: the PowerPC values are the mask to do the test (instead of the - // index of the bit to test like in ARM and Aarch64) - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - let power8_features = auxv.hwcap2 & 0x80000000 != 0; - enable_feature(&mut value, Feature::power8, power8_features); - enable_feature(&mut value, Feature::power8_altivec, power8_features); - enable_feature(&mut value, Feature::power8_crypto, power8_features); - enable_feature(&mut value, Feature::power8_vector, power8_features); - let power9_features = auxv.hwcap2 & 0x00800000 != 0; - enable_feature(&mut value, Feature::power9, power9_features); - enable_feature(&mut value, Feature::power9_altivec, power9_features); - enable_feature(&mut value, Feature::power9_vector, power9_features); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/riscv.rs b/crates/std/crates/std_detect/src/detect/os/linux/riscv.rs deleted file mode 100644 index 18f9f68..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/riscv.rs +++ /dev/null @@ -1,323 +0,0 @@ -//! Run-time feature detection for RISC-V on Linux. -//! -//! On RISC-V, detection using auxv only supports single-letter extensions. -//! So, we use riscv_hwprobe that supports multi-letter extensions if available. -//! - -use core::ptr; - -use super::super::riscv::imply_features; -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -// See -// for runtime status query constants. -const PR_RISCV_V_GET_CONTROL: libc::c_int = 70; -const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2; -const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3; - -// See -// for riscv_hwprobe struct and hardware probing constants. - -#[repr(C)] -struct riscv_hwprobe { - key: i64, - value: u64, -} - -impl riscv_hwprobe { - // key is overwritten to -1 if not supported by riscv_hwprobe syscall. - pub fn get(&self) -> Option { - (self.key != -1).then_some(self.value) - } -} - -#[allow(non_upper_case_globals)] -const __NR_riscv_hwprobe: libc::c_long = 258; - -const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3; -const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0; - -const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4; -const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0; -const RISCV_HWPROBE_IMA_C: u64 = 1 << 1; -const RISCV_HWPROBE_IMA_V: u64 = 1 << 2; -const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3; -const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4; -const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5; -const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6; -const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7; -const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8; -const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9; -const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10; -const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11; -const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12; -const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13; -const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14; -const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15; -const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16; -const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17; -const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18; -const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19; -const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20; -const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21; -const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22; -const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23; -const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24; -const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25; -const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26; -const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27; -const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28; -const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29; -const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30; -const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31; -const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32; -const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33; -const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34; -const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35; -const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36; -const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37; -const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38; -const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39; -const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40; -const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41; -const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42; -const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43; -const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44; -const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45; -const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46; -const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47; -const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48; -// Excluded because it only reports the existence of `prctl`-based pointer masking control. -// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49; -const RISCV_HWPROBE_EXT_ZICNTR: u64 = 1 << 50; -const RISCV_HWPROBE_EXT_ZIHPM: u64 = 1 << 51; -const RISCV_HWPROBE_EXT_ZFBFMIN: u64 = 1 << 52; -const RISCV_HWPROBE_EXT_ZVFBFMIN: u64 = 1 << 53; -const RISCV_HWPROBE_EXT_ZVFBFWMA: u64 = 1 << 54; -const RISCV_HWPROBE_EXT_ZICBOM: u64 = 1 << 55; -const RISCV_HWPROBE_EXT_ZAAMO: u64 = 1 << 56; -const RISCV_HWPROBE_EXT_ZALRSC: u64 = 1 << 57; -const RISCV_HWPROBE_EXT_ZABHA: u64 = 1 << 58; - -const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5; -const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3; -const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7; - -const RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF: i64 = 9; -const RISCV_HWPROBE_MISALIGNED_SCALAR_FAST: u64 = 3; - -const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10; -const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3; - -// syscall returns an unsupported error if riscv_hwprobe is not supported, -// so we can safely use this function on older versions of Linux. -fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool { - unsafe fn __riscv_hwprobe( - pairs: *mut riscv_hwprobe, - pair_count: libc::size_t, - cpu_set_size: libc::size_t, - cpus: *mut libc::c_ulong, - flags: libc::c_uint, - ) -> libc::c_long { - unsafe { libc::syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_set_size, cpus, flags) } - } - - unsafe { __riscv_hwprobe(out.as_mut_ptr(), out.len(), 0, ptr::null_mut(), 0) == 0 } -} - -/// Read list of supported features from (1) the auxiliary vector -/// and (2) the results of `riscv_hwprobe` and `prctl` system calls. -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let mut enable_feature = |feature, enable| { - if enable { - value.set(feature as u32); - } - }; - - // Use auxiliary vector to enable single-letter ISA extensions. - // The values are part of the platform-specific [asm/hwcap.h][hwcap] - // - // [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.16 - let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform - let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into()); - #[allow(clippy::eq_op)] - enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into())); - enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into())); - enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into())); - enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into())); - enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into())); - let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into()); - let mut is_v_set = false; - - // Use riscv_hwprobe syscall to query more extensions and - // performance-related capabilities. - 'hwprobe: { - macro_rules! init { - { $($name: ident : $key: expr),* $(,)? } => { - #[repr(usize)] - enum Indices { $($name),* } - let mut t = [$(riscv_hwprobe { key: $key, value: 0 }),*]; - macro_rules! data_mut { () => { &mut t } } - macro_rules! query { [$idx: ident] => { t[Indices::$idx as usize].get() } } - } - } - init! { - BaseBehavior: RISCV_HWPROBE_KEY_BASE_BEHAVIOR, - Extensions: RISCV_HWPROBE_KEY_IMA_EXT_0, - MisalignedScalarPerf: RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, - MisalignedVectorPerf: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, - MisalignedScalarPerfFallback: RISCV_HWPROBE_KEY_CPUPERF_0, - }; - if !_riscv_hwprobe(data_mut!()) { - break 'hwprobe; - } - - // Query scalar misaligned behavior. - if let Some(value) = query![MisalignedScalarPerf] { - enable_feature( - Feature::unaligned_scalar_mem, - value == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST, - ); - } else if let Some(value) = query![MisalignedScalarPerfFallback] { - // Deprecated method for fallback - enable_feature( - Feature::unaligned_scalar_mem, - value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST, - ); - } - - // Query vector misaligned behavior. - if let Some(value) = query![MisalignedVectorPerf] { - enable_feature( - Feature::unaligned_vector_mem, - value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST, - ); - } - - // Query whether "I" base and extensions "M" and "A" (as in the ISA - // manual version 2.2) are enabled. "I" base at that time corresponds - // to "I", "Zicsr", "Zicntr" and "Zifencei" (as in the ISA manual version - // 20240411). - // This is a current requirement of - // `RISCV_HWPROBE_KEY_IMA_EXT_0`-based tests. - if query![BaseBehavior].is_none_or(|value| value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA == 0) { - break 'hwprobe; - } - has_i = true; - enable_feature(Feature::zicsr, true); - enable_feature(Feature::zicntr, true); - enable_feature(Feature::zifencei, true); - enable_feature(Feature::m, true); - enable_feature(Feature::a, true); - - // Enable features based on `RISCV_HWPROBE_KEY_IMA_EXT_0`. - let Some(ima_ext_0) = query![Extensions] else { - break 'hwprobe; - }; - let test = |mask| (ima_ext_0 & mask) != 0; - - enable_feature(Feature::d, test(RISCV_HWPROBE_IMA_FD)); // F is implied. - enable_feature(Feature::c, test(RISCV_HWPROBE_IMA_C)); - - enable_feature(Feature::zicntr, test(RISCV_HWPROBE_EXT_ZICNTR)); - enable_feature(Feature::zihpm, test(RISCV_HWPROBE_EXT_ZIHPM)); - - enable_feature(Feature::zihintntl, test(RISCV_HWPROBE_EXT_ZIHINTNTL)); - enable_feature(Feature::zihintpause, test(RISCV_HWPROBE_EXT_ZIHINTPAUSE)); - enable_feature(Feature::zimop, test(RISCV_HWPROBE_EXT_ZIMOP)); - enable_feature(Feature::zicbom, test(RISCV_HWPROBE_EXT_ZICBOM)); - enable_feature(Feature::zicboz, test(RISCV_HWPROBE_EXT_ZICBOZ)); - enable_feature(Feature::zicond, test(RISCV_HWPROBE_EXT_ZICOND)); - - enable_feature(Feature::zalrsc, test(RISCV_HWPROBE_EXT_ZALRSC)); - enable_feature(Feature::zaamo, test(RISCV_HWPROBE_EXT_ZAAMO)); - enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS)); - enable_feature(Feature::zabha, test(RISCV_HWPROBE_EXT_ZABHA)); - enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS)); - enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO)); - - enable_feature(Feature::zba, test(RISCV_HWPROBE_EXT_ZBA)); - enable_feature(Feature::zbb, test(RISCV_HWPROBE_EXT_ZBB)); - enable_feature(Feature::zbs, test(RISCV_HWPROBE_EXT_ZBS)); - enable_feature(Feature::zbc, test(RISCV_HWPROBE_EXT_ZBC)); - - enable_feature(Feature::zbkb, test(RISCV_HWPROBE_EXT_ZBKB)); - enable_feature(Feature::zbkc, test(RISCV_HWPROBE_EXT_ZBKC)); - enable_feature(Feature::zbkx, test(RISCV_HWPROBE_EXT_ZBKX)); - enable_feature(Feature::zknd, test(RISCV_HWPROBE_EXT_ZKND)); - enable_feature(Feature::zkne, test(RISCV_HWPROBE_EXT_ZKNE)); - enable_feature(Feature::zknh, test(RISCV_HWPROBE_EXT_ZKNH)); - enable_feature(Feature::zksed, test(RISCV_HWPROBE_EXT_ZKSED)); - enable_feature(Feature::zksh, test(RISCV_HWPROBE_EXT_ZKSH)); - enable_feature(Feature::zkt, test(RISCV_HWPROBE_EXT_ZKT)); - - enable_feature(Feature::zcmop, test(RISCV_HWPROBE_EXT_ZCMOP)); - enable_feature(Feature::zca, test(RISCV_HWPROBE_EXT_ZCA)); - enable_feature(Feature::zcf, test(RISCV_HWPROBE_EXT_ZCF)); - enable_feature(Feature::zcd, test(RISCV_HWPROBE_EXT_ZCD)); - enable_feature(Feature::zcb, test(RISCV_HWPROBE_EXT_ZCB)); - - enable_feature(Feature::zfh, test(RISCV_HWPROBE_EXT_ZFH)); - enable_feature(Feature::zfhmin, test(RISCV_HWPROBE_EXT_ZFHMIN)); - enable_feature(Feature::zfa, test(RISCV_HWPROBE_EXT_ZFA)); - enable_feature(Feature::zfbfmin, test(RISCV_HWPROBE_EXT_ZFBFMIN)); - - // Use prctl (if any) to determine whether the vector extension - // is enabled on the current thread (assuming the entire process - // share the same status). If prctl fails (e.g. QEMU userland emulator - // as of version 9.2.3), use auxiliary vector to retrieve the default - // vector status on the process startup. - let has_vectors = { - let v_status = unsafe { libc::prctl(PR_RISCV_V_GET_CONTROL) }; - if v_status >= 0 { - (v_status & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) == PR_RISCV_V_VSTATE_CTRL_ON - } else { - has_v - } - }; - if has_vectors { - enable_feature(Feature::v, test(RISCV_HWPROBE_IMA_V)); - enable_feature(Feature::zve32x, test(RISCV_HWPROBE_EXT_ZVE32X)); - enable_feature(Feature::zve32f, test(RISCV_HWPROBE_EXT_ZVE32F)); - enable_feature(Feature::zve64x, test(RISCV_HWPROBE_EXT_ZVE64X)); - enable_feature(Feature::zve64f, test(RISCV_HWPROBE_EXT_ZVE64F)); - enable_feature(Feature::zve64d, test(RISCV_HWPROBE_EXT_ZVE64D)); - - enable_feature(Feature::zvbb, test(RISCV_HWPROBE_EXT_ZVBB)); - enable_feature(Feature::zvbc, test(RISCV_HWPROBE_EXT_ZVBC)); - enable_feature(Feature::zvkb, test(RISCV_HWPROBE_EXT_ZVKB)); - enable_feature(Feature::zvkg, test(RISCV_HWPROBE_EXT_ZVKG)); - enable_feature(Feature::zvkned, test(RISCV_HWPROBE_EXT_ZVKNED)); - enable_feature(Feature::zvknha, test(RISCV_HWPROBE_EXT_ZVKNHA)); - enable_feature(Feature::zvknhb, test(RISCV_HWPROBE_EXT_ZVKNHB)); - enable_feature(Feature::zvksed, test(RISCV_HWPROBE_EXT_ZVKSED)); - enable_feature(Feature::zvksh, test(RISCV_HWPROBE_EXT_ZVKSH)); - enable_feature(Feature::zvkt, test(RISCV_HWPROBE_EXT_ZVKT)); - - enable_feature(Feature::zvfh, test(RISCV_HWPROBE_EXT_ZVFH)); - enable_feature(Feature::zvfhmin, test(RISCV_HWPROBE_EXT_ZVFHMIN)); - enable_feature(Feature::zvfbfmin, test(RISCV_HWPROBE_EXT_ZVFBFMIN)); - enable_feature(Feature::zvfbfwma, test(RISCV_HWPROBE_EXT_ZVFBFWMA)); - } - is_v_set = true; - }; - - // Set V purely depending on the auxiliary vector - // only if no fine-grained vector extension detection is available. - if !is_v_set { - enable_feature(Feature::v, has_v); - } - - // Handle base ISA. - // If future RV128I is supported, implement with `enable_feature` here. - // Note that we should use `target_arch` instead of `target_pointer_width` - // to avoid misdetection caused by experimental ABIs such as RV64ILP32. - #[cfg(target_arch = "riscv64")] - enable_feature(Feature::rv64i, has_i); - #[cfg(target_arch = "riscv32")] - enable_feature(Feature::rv32i, has_i); - - imply_features(value) -} diff --git a/crates/std/crates/std_detect/src/detect/os/linux/s390x.rs b/crates/std/crates/std_detect/src/detect/os/linux/s390x.rs deleted file mode 100644 index 9b53f52..0000000 --- a/crates/std/crates/std_detect/src/detect/os/linux/s390x.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Run-time feature detection for s390x on Linux. - -use super::auxvec; -use crate::detect::{Feature, bit, cache}; - -/// Try to read the features from the auxiliary vector -pub(crate) fn detect_features() -> cache::Initializer { - let opt_hwcap: Option = auxvec::auxv().ok().map(Into::into); - let facilities = ExtendedFacilityList::new(); - cache(opt_hwcap, facilities) -} - -#[derive(Debug, Default, PartialEq)] -struct AtHwcap { - esan3: bool, - zarch: bool, - stfle: bool, - msa: bool, - ldisp: bool, - eimm: bool, - dfp: bool, - hpage: bool, - etf3eh: bool, - high_gprs: bool, - te: bool, - vxrs: bool, - vxrs_bcd: bool, - vxrs_ext: bool, - gs: bool, - vxrs_ext2: bool, - vxrs_pde: bool, - sort: bool, - dflt: bool, - vxrs_pde2: bool, - nnpa: bool, - pci_mio: bool, - sie: bool, -} - -impl From for AtHwcap { - /// Reads AtHwcap from the auxiliary vector. - fn from(auxv: auxvec::AuxVec) -> Self { - AtHwcap { - esan3: bit::test(auxv.hwcap, 0), - zarch: bit::test(auxv.hwcap, 1), - stfle: bit::test(auxv.hwcap, 2), - msa: bit::test(auxv.hwcap, 3), - ldisp: bit::test(auxv.hwcap, 4), - eimm: bit::test(auxv.hwcap, 5), - dfp: bit::test(auxv.hwcap, 6), - hpage: bit::test(auxv.hwcap, 7), - etf3eh: bit::test(auxv.hwcap, 8), - high_gprs: bit::test(auxv.hwcap, 9), - te: bit::test(auxv.hwcap, 10), - vxrs: bit::test(auxv.hwcap, 11), - vxrs_bcd: bit::test(auxv.hwcap, 12), - vxrs_ext: bit::test(auxv.hwcap, 13), - gs: bit::test(auxv.hwcap, 14), - vxrs_ext2: bit::test(auxv.hwcap, 15), - vxrs_pde: bit::test(auxv.hwcap, 16), - sort: bit::test(auxv.hwcap, 17), - dflt: bit::test(auxv.hwcap, 18), - vxrs_pde2: bit::test(auxv.hwcap, 19), - nnpa: bit::test(auxv.hwcap, 20), - pci_mio: bit::test(auxv.hwcap, 21), - sie: bit::test(auxv.hwcap, 22), - } - } -} - -struct ExtendedFacilityList([u64; 4]); - -impl ExtendedFacilityList { - fn new() -> Self { - let mut result: [u64; 4] = [0; 4]; - // SAFETY: rust/llvm only support s390x version with the `stfle` instruction. - unsafe { - core::arch::asm!( - // equivalently ".insn s, 0xb2b00000, 0({1})", - "stfle 0({})", - in(reg_addr) result.as_mut_ptr() , - inout("r0") result.len() as u64 - 1 => _, - options(nostack) - ); - } - Self(result) - } - - const fn get_bit(&self, n: usize) -> bool { - // NOTE: bits are numbered from the left. - self.0[n / 64] & (1 << (63 - (n % 64))) != 0 - } -} - -/// Initializes the cache from the feature bits. -/// -/// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the -/// fields found in the [Facility Indications]. -/// -/// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63 -/// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129 -fn cache(hwcap: Option, facilities: ExtendedFacilityList) -> cache::Initializer { - let mut value = cache::Initializer::default(); - - { - let mut enable_if_set = |bit_index, f| { - if facilities.get_bit(bit_index) { - value.set(f as u32); - } - }; - - // We use HWCAP for `vector` because it requires both hardware and kernel support. - if let Some(AtHwcap { vxrs: true, .. }) = hwcap { - // vector and related - - enable_if_set(129, Feature::vector); - - enable_if_set(135, Feature::vector_enhancements_1); - enable_if_set(148, Feature::vector_enhancements_2); - enable_if_set(198, Feature::vector_enhancements_3); - - enable_if_set(134, Feature::vector_packed_decimal); - enable_if_set(152, Feature::vector_packed_decimal_enhancement); - enable_if_set(192, Feature::vector_packed_decimal_enhancement_2); - enable_if_set(199, Feature::vector_packed_decimal_enhancement_3); - - enable_if_set(165, Feature::nnp_assist); - } - - // others - - enable_if_set(76, Feature::message_security_assist_extension3); - enable_if_set(77, Feature::message_security_assist_extension4); - enable_if_set(57, Feature::message_security_assist_extension5); - enable_if_set(146, Feature::message_security_assist_extension8); - enable_if_set(155, Feature::message_security_assist_extension9); - enable_if_set(86, Feature::message_security_assist_extension12); - - enable_if_set(58, Feature::miscellaneous_extensions_2); - enable_if_set(61, Feature::miscellaneous_extensions_3); - enable_if_set(84, Feature::miscellaneous_extensions_4); - - enable_if_set(45, Feature::high_word); - enable_if_set(73, Feature::transactional_execution); - enable_if_set(133, Feature::guarded_storage); - enable_if_set(150, Feature::enhanced_sort); - enable_if_set(151, Feature::deflate_conversion); - enable_if_set(201, Feature::concurrent_functions); - } - - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/openbsd/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/openbsd/aarch64.rs deleted file mode 100644 index 6825a37..0000000 --- a/crates/std/crates/std_detect/src/detect/os/openbsd/aarch64.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! Run-time feature detection for Aarch64 on OpenBSD. -//! -//! OpenBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl. -//! https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8 -//! https://github.com/golang/go/commit/cd54ef1f61945459486e9eea2f016d99ef1da925 - -use core::mem::MaybeUninit; -use core::ptr; - -use crate::detect::cache; - -// Defined in machine/cpu.h. -// https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/arch/arm64/include/cpu.h#L25-L40 -const CPU_ID_AA64ISAR0: libc::c_int = 2; -const CPU_ID_AA64ISAR1: libc::c_int = 3; -const CPU_ID_AA64MMFR2: libc::c_int = 7; -const CPU_ID_AA64PFR0: libc::c_int = 8; - -/// Try to read the features from the system registers. -pub(crate) fn detect_features() -> cache::Initializer { - // ID_AA64ISAR0_EL1 and ID_AA64ISAR1_EL1 are supported on OpenBSD 7.1+. - // https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8 - // Others are supported on OpenBSD 7.3+. - // https://github.com/openbsd/src/commit/c7654cd65262d532212f65123ee3905ba200365c - // sysctl returns an unsupported error if operation is not supported, - // so we can safely use this function on older versions of OpenBSD. - let aa64isar0 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64ISAR0]).unwrap_or(0); - let aa64isar1 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64ISAR1]).unwrap_or(0); - let aa64mmfr2 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64MMFR2]).unwrap_or(0); - // Do not use unwrap_or(0) because in fp and asimd fields, 0 indicates that - // the feature is available. - let aa64pfr0 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64PFR0]); - - crate::detect::aarch64::parse_system_registers(aa64isar0, aa64isar1, aa64mmfr2, aa64pfr0) -} - -#[inline] -fn sysctl64(mib: &[libc::c_int]) -> Option { - const OUT_LEN: libc::size_t = core::mem::size_of::(); - let mut out = MaybeUninit::::uninit(); - let mut out_len = OUT_LEN; - let res = unsafe { - libc::sysctl( - mib.as_ptr(), - mib.len() as libc::c_uint, - out.as_mut_ptr() as *mut libc::c_void, - &mut out_len, - ptr::null_mut(), - 0, - ) - }; - if res == -1 || out_len != OUT_LEN { - return None; - } - // SAFETY: we've checked that sysctl was successful and `out` was filled. - Some(unsafe { out.assume_init() }) -} diff --git a/crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs b/crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs deleted file mode 100644 index 7a1efb2..0000000 --- a/crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Parses ELF auxiliary vectors. -#![cfg_attr( - any(target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64"), - allow(dead_code) -)] - -/// Cache HWCAP bitfields of the ELF Auxiliary Vector. -/// -/// If an entry cannot be read all the bits in the bitfield are set to zero. -/// This should be interpreted as all the features being disabled. -#[derive(Debug, Copy, Clone)] -pub(crate) struct AuxVec { - pub hwcap: usize, - pub hwcap2: usize, -} - -/// ELF Auxiliary Vector -/// -/// The auxiliary vector is a memory region in a running ELF program's stack -/// composed of (key: usize, value: usize) pairs. -/// -/// The keys used in the aux vector are platform dependent. For OpenBSD, they are -/// defined in [machine/elf.h][elfh]. The hardware capabilities of a given CPU -/// can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys. -/// -/// Note that run-time feature detection is not invoked for features that can -/// be detected at compile-time. -/// -/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/arm64/include/elf.h -/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/powerpc64/include/elf.h -pub(crate) fn auxv() -> Result { - let hwcap = archauxv(libc::AT_HWCAP); - let hwcap2 = archauxv(libc::AT_HWCAP2); - // Zero could indicate that no features were detected, but it's also used to - // indicate an error. In particular, on many platforms AT_HWCAP2 will be - // legitimately zero, since it contains the most recent feature flags. - if hwcap != 0 || hwcap2 != 0 { - return Ok(AuxVec { hwcap, hwcap2 }); - } - Err(()) -} - -/// Tries to read the `key` from the auxiliary vector. -fn archauxv(key: libc::c_int) -> usize { - const OUT_LEN: libc::c_int = core::mem::size_of::() as libc::c_int; - let mut out: libc::c_ulong = 0; - unsafe { - let res = - libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN); - // If elf_aux_info fails, `out` will be left at zero (which is the proper default value). - debug_assert!(res == 0 || out == 0); - } - out as usize -} diff --git a/crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs b/crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs deleted file mode 100644 index ebfdbd5..0000000 --- a/crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Run-time feature detection on OpenBSD - -mod auxvec; - -cfg_select! { - target_arch = "aarch64" => { - mod aarch64; - pub(crate) use self::aarch64::detect_features; - } - target_arch = "powerpc64" => { - mod powerpc; - pub(crate) use self::powerpc::detect_features; - } - _ => { - use crate::detect::cache; - /// Performs run-time feature detection. - pub(crate) fn detect_features() -> cache::Initializer { - cache::Initializer::default() - } - } -} diff --git a/crates/std/crates/std_detect/src/detect/os/openbsd/powerpc.rs b/crates/std/crates/std_detect/src/detect/os/openbsd/powerpc.rs deleted file mode 100644 index dd98ab2..0000000 --- a/crates/std/crates/std_detect/src/detect/os/openbsd/powerpc.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Run-time feature detection for PowerPC on OpenBSD. - -use super::auxvec; -use crate::detect::{Feature, cache}; - -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - let enable_feature = |value: &mut cache::Initializer, f, enable| { - if enable { - value.set(f as u32); - } - }; - - if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); - return value; - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/other.rs b/crates/std/crates/std_detect/src/detect/os/other.rs deleted file mode 100644 index 091fafc..0000000 --- a/crates/std/crates/std_detect/src/detect/os/other.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Other operating systems - -use crate::detect::cache; - -#[allow(dead_code)] -pub(crate) fn detect_features() -> cache::Initializer { - cache::Initializer::default() -} diff --git a/crates/std/crates/std_detect/src/detect/os/riscv.rs b/crates/std/crates/std_detect/src/detect/os/riscv.rs deleted file mode 100644 index 9b9e0cb..0000000 --- a/crates/std/crates/std_detect/src/detect/os/riscv.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! Run-time feature detection utility for RISC-V. -//! -//! On RISC-V, full feature detection needs a help of one or more -//! feature detection mechanisms (usually provided by the operating system). -//! -//! RISC-V architecture defines many extensions and some have dependency to others. -//! More importantly, some of them cannot be enabled without resolving such -//! dependencies due to limited set of features that such mechanisms provide. -//! -//! This module provides an OS-independent utility to process such relations -//! between RISC-V extensions. - -use crate::detect::{Feature, cache}; - -/// Imply features by the given set of enabled features. -/// -/// Note that it does not perform any consistency checks including existence of -/// conflicting extensions and/or complicated requirements. Eliminating such -/// inconsistencies is the responsibility of the feature detection logic and -/// its provider(s). -pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initializer { - loop { - // Check convergence of the feature flags later. - let prev = value; - - // Expect that the optimizer turns repeated operations into - // a fewer number of bit-manipulation operations. - macro_rules! imply { - // Regular implication: - // A1 => (B1[, B2...]), A2 => (B1[, B2...]) and so on. - ($($from: ident)|+ => $($to: ident)&+) => { - if [$(Feature::$from as u32),+].iter().any(|&x| value.test(x)) { - $( - value.set(Feature::$to as u32); - )+ - } - }; - // Implication with multiple requirements: - // A1 && A2 ... => (B1[, B2...]). - ($($from: ident)&+ => $($to: ident)&+) => { - if [$(Feature::$from as u32),+].iter().all(|&x| value.test(x)) { - $( - value.set(Feature::$to as u32); - )+ - } - }; - } - macro_rules! group { - ($group: ident == $($member: ident)&+) => { - // Forward implication as defined in the specifications. - imply!($group => $($member)&+); - // Reverse implication to "group extension" from its members. - // This is not a part of specifications but convenient for - // feature detection and implemented in e.g. LLVM. - imply!($($member)&+ => $group); - }; - } - - /* - If a dependency/implication is not explicitly stated in the - specification, it is denoted as a comment as follows: - "defined as subset": - The latter extension is described as a subset of the former - (but the evidence is weak). - "functional": - The former extension is functionally a superset of the latter - (no direct references though). - */ - - imply!(zvbb => zvkb); - - // Certain set of vector cryptography extensions form a group. - group!(zvkn == zvkned & zvknhb & zvkb & zvkt); - group!(zvknc == zvkn & zvbc); - group!(zvkng == zvkn & zvkg); - group!(zvks == zvksed & zvksh & zvkb & zvkt); - group!(zvksc == zvks & zvbc); - group!(zvksg == zvks & zvkg); - - imply!(zvknhb => zvknha); // functional - - // For vector cryptography, Zvknhb and Zvbc require integer arithmetic - // with EEW=64 (Zve64x) while others not depending on them - // require EEW=32 (Zve32x). - imply!(zvknhb | zvbc => zve64x); - imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x); - - imply!(zbc => zbkc); // defined as subset - group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh); - group!(zks == zbkb & zbkc & zbkx & zksed & zksh); - group!(zk == zkn & zkr & zkt); - - imply!(zabha | zacas => zaamo); - group!(a == zalrsc & zaamo); - - group!(b == zba & zbb & zbs); - - imply!(zcf => zca & f); - imply!(zcd => zca & d); - imply!(zcmop | zcb => zca); - - imply!(zhinx => zhinxmin); - imply!(zdinx | zhinxmin => zfinx); - - imply!(zvfh => zvfhmin); // functional - imply!(zvfh => zve32f & zfhmin); - imply!(zvfhmin => zve32f); - imply!(zvfbfwma => zvfbfmin & zfbfmin); - imply!(zvfbfmin => zve32f); - - imply!(v => zve64d); - imply!(zve64d => zve64f & d); - imply!(zve64f => zve64x & zve32f); - imply!(zve64x => zve32x); - imply!(zve32f => zve32x & f); - - imply!(zfh => zfhmin); - imply!(q => d); - imply!(d | zfhmin | zfa => f); - imply!(zfbfmin => f); // and some of (not all) "Zfh" instructions. - - // Relatively complex implication rules around the "C" extension. - // (from "C" and some others) - imply!(c => zca); - imply!(c & d => zcd); - #[cfg(target_arch = "riscv32")] - imply!(c & f => zcf); - // (to "C"; defined as superset) - cfg_select! { - target_arch = "riscv32" => { - if value.test(Feature::d as u32) { - imply!(zcf & zcd => c); - } else if value.test(Feature::f as u32) { - imply!(zcf => c); - } else { - imply!(zca => c); - } - } - _ => { - if value.test(Feature::d as u32) { - imply!(zcd => c); - } else { - imply!(zca => c); - } - } - } - - imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr); - - // Loop until the feature flags converge. - if prev == value { - return value; - } - } -} - -#[cfg(test)] -#[path = "riscv/tests.rs"] -mod tests; diff --git a/crates/std/crates/std_detect/src/detect/os/riscv/tests.rs b/crates/std/crates/std_detect/src/detect/os/riscv/tests.rs deleted file mode 100644 index 99a81de..0000000 --- a/crates/std/crates/std_detect/src/detect/os/riscv/tests.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::*; - -#[test] -fn simple_direct() { - let mut value = cache::Initializer::default(); - value.set(Feature::f as u32); - // F (and other extensions with CSRs) -> Zicsr - assert!(imply_features(value).test(Feature::zicsr as u32)); -} - -#[test] -fn simple_indirect() { - let mut value = cache::Initializer::default(); - value.set(Feature::q as u32); - // Q -> D, D -> F, F -> Zicsr - assert!(imply_features(value).test(Feature::zicsr as u32)); -} - -#[test] -fn complex_zcd() { - let mut value = cache::Initializer::default(); - // C & D -> Zcd - value.set(Feature::c as u32); - assert!(!imply_features(value).test(Feature::zcd as u32)); - value.set(Feature::d as u32); - assert!(imply_features(value).test(Feature::zcd as u32)); -} - -#[test] -fn group_simple_forward() { - let mut value = cache::Initializer::default(); - // A -> Zalrsc & Zaamo (forward implication) - value.set(Feature::a as u32); - let value = imply_features(value); - assert!(value.test(Feature::zalrsc as u32)); - assert!(value.test(Feature::zaamo as u32)); -} - -#[test] -fn group_simple_backward() { - let mut value = cache::Initializer::default(); - // Zalrsc & Zaamo -> A (reverse implication) - value.set(Feature::zalrsc as u32); - value.set(Feature::zaamo as u32); - assert!(imply_features(value).test(Feature::a as u32)); -} - -#[test] -fn group_complex_convergence() { - let mut value = cache::Initializer::default(); - // Needs 3 iterations to converge - // (and 4th iteration for convergence checking): - // 1. [Zvksc] -> Zvks & Zvbc - // 2. Zvks -> Zvksed & Zvksh & Zvkb & Zvkt - // 3a. [Zvkned] & [Zvknhb] & [Zvkb] & Zvkt -> {Zvkn} - // 3b. Zvkn & Zvbc -> {Zvknc} - value.set(Feature::zvksc as u32); - value.set(Feature::zvkned as u32); - value.set(Feature::zvknhb as u32); - value.set(Feature::zvkb as u32); - let value = imply_features(value); - assert!(value.test(Feature::zvkn as u32)); - assert!(value.test(Feature::zvknc as u32)); -} diff --git a/crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs b/crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs deleted file mode 100644 index 937f9f2..0000000 --- a/crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! Run-time feature detection for Aarch64 on Windows. - -use crate::detect::{Feature, cache}; - -/// Try to read the features using IsProcessorFeaturePresent. -pub(crate) fn detect_features() -> cache::Initializer { - type DWORD = u32; - type BOOL = i32; - - const FALSE: BOOL = 0; - // The following Microsoft documents isn't updated for aarch64. - // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent - // These are defined in winnt.h of Windows SDK - const PF_ARM_VFP_32_REGISTERS_AVAILABLE: u32 = 18; - const PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: u32 = 19; - const PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE: u32 = 30; - const PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE: u32 = 31; - const PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE: u32 = 34; - const PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE: u32 = 43; - const PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE: u32 = 44; - const PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE: u32 = 45; - const PF_ARM_SVE_INSTRUCTIONS_AVAILABLE: u32 = 46; - const PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE: u32 = 47; - const PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE: u32 = 48; - const PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE: u32 = 49; - const PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE: u32 = 50; - const PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE: u32 = 51; - // const PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE: u32 = 52; - // const PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE: u32 = 53; - const PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE: u32 = 54; - const PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE: u32 = 55; - const PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE: u32 = 56; - // const PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE: u32 = 57; - // const PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE: u32 = 58; - // const PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE: u32 = 59; - - unsafe extern "system" { - fn IsProcessorFeaturePresent(ProcessorFeature: DWORD) -> BOOL; - } - - let mut value = cache::Initializer::default(); - { - let mut enable_feature = |f, enable| { - if enable { - value.set(f as u32); - } - }; - - // Some features may be supported on current CPU, - // but no way to detect it by OS API. - // Also, we require unsafe block for the extern "system" calls. - unsafe { - enable_feature( - Feature::fp, - IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::asimd, - IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::crc, - IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::lse, - IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::dotprod, - IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::jsconv, - IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::rcpc, - IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve, - IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve2, - IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve2p1, - IsProcessorFeaturePresent(PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve2_aes, - IsProcessorFeaturePresent(PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE) != FALSE - && IsProcessorFeaturePresent(PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE) - != FALSE, - ); - enable_feature( - Feature::sve2_bitperm, - IsProcessorFeaturePresent(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve_b16b16, - IsProcessorFeaturePresent(PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve2_sha3, - IsProcessorFeaturePresent(PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - enable_feature( - Feature::sve2_sm4, - IsProcessorFeaturePresent(PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE) != FALSE, - ); - // PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE means aes, sha1, sha2 and - // pmull support - let crypto = - IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != FALSE; - enable_feature(Feature::aes, crypto); - enable_feature(Feature::pmull, crypto); - enable_feature(Feature::sha2, crypto); - } - } - value -} diff --git a/crates/std/crates/std_detect/src/detect/os/x86.rs b/crates/std/crates/std_detect/src/detect/os/x86.rs deleted file mode 100644 index f2205ba..0000000 --- a/crates/std/crates/std_detect/src/detect/os/x86.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! x86 run-time feature detection is OS independent. - -#[cfg(target_arch = "x86")] -use core::arch::x86::*; -#[cfg(target_arch = "x86_64")] -use core::arch::x86_64::*; -use core::mem; - -use crate::detect::{Feature, bit, cache}; - -/// Run-time feature detection on x86 works by using the CPUID instruction. -/// -/// The [CPUID Wikipedia page][wiki_cpuid] contains -/// all the information about which flags to set to query which values, and in -/// which registers these are reported. -/// -/// The definitive references are: -/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2: -/// Instruction Set Reference, A-Z][intel64_ref]. -/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and -/// System Instructions][amd64_ref]. -/// -/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID -/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf -/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf -#[allow(clippy::similar_names)] -pub(crate) fn detect_features() -> cache::Initializer { - let mut value = cache::Initializer::default(); - - if cfg!(target_env = "sgx") { - // doesn't support this because it is untrusted data - return value; - } - - // Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU - // has `cpuid` support. - - // 0. EAX = 0: Basic Information: - // - EAX returns the "Highest Function Parameter", that is, the maximum - // leaf value for subsequent calls of `cpuinfo` in range [0, - // 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars, - // returned in EBX, EDX, and ECX (in that order): - let (max_basic_leaf, vendor_id) = { - let CpuidResult { eax: max_basic_leaf, ebx, ecx, edx } = __cpuid(0); - let vendor_id: [[u8; 4]; 3] = [ebx.to_ne_bytes(), edx.to_ne_bytes(), ecx.to_ne_bytes()]; - let vendor_id: [u8; 12] = unsafe { mem::transmute(vendor_id) }; - (max_basic_leaf, vendor_id) - }; - - if max_basic_leaf < 1 { - // Earlier Intel 486, CPUID not implemented - return value; - } - - // EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits"; - // Contains information about most x86 features. - let CpuidResult { ecx: proc_info_ecx, edx: proc_info_edx, .. } = __cpuid(0x0000_0001_u32); - - // EAX = 7: Queries "Extended Features"; - // Contains information about bmi,bmi2, and avx2 support. - let ( - extended_features_ebx, - extended_features_ecx, - extended_features_edx, - extended_features_eax_leaf_1, - extended_features_edx_leaf_1, - ) = if max_basic_leaf >= 7 { - let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0x0000_0007_u32); - let CpuidResult { eax: eax_1, edx: edx_1, .. } = - __cpuid_count(0x0000_0007_u32, 0x0000_0001_u32); - (ebx, ecx, edx, eax_1, edx_1) - } else { - (0, 0, 0, 0, 0) // CPUID does not support "Extended Features" - }; - - // EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported - // - EAX returns the max leaf value for extended information, that is, - // `cpuid` calls in range [0x8000_0000; u32::MAX]: - let CpuidResult { eax: extended_max_basic_leaf, .. } = __cpuid(0x8000_0000_u32); - - // EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature - // Bits" - let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 { - let CpuidResult { ecx, .. } = __cpuid(0x8000_0001_u32); - ecx - } else { - 0 - }; - - { - // borrows value till the end of this scope: - let mut enable = |r, rb, f| { - let present = bit::test(r as usize, rb); - if present { - value.set(f as u32); - } - present - }; - - enable(proc_info_ecx, 0, Feature::sse3); - enable(proc_info_ecx, 1, Feature::pclmulqdq); - enable(proc_info_ecx, 9, Feature::ssse3); - enable(proc_info_ecx, 13, Feature::cmpxchg16b); - enable(proc_info_ecx, 19, Feature::sse4_1); - enable(proc_info_ecx, 20, Feature::sse4_2); - enable(proc_info_ecx, 22, Feature::movbe); - enable(proc_info_ecx, 23, Feature::popcnt); - enable(proc_info_ecx, 25, Feature::aes); - let f16c = enable(proc_info_ecx, 29, Feature::f16c); - enable(proc_info_ecx, 30, Feature::rdrand); - enable(extended_features_ebx, 18, Feature::rdseed); - enable(extended_features_ebx, 19, Feature::adx); - enable(extended_features_ebx, 11, Feature::rtm); - enable(proc_info_edx, 4, Feature::tsc); - enable(proc_info_edx, 23, Feature::mmx); - enable(proc_info_edx, 24, Feature::fxsr); - enable(proc_info_edx, 25, Feature::sse); - enable(proc_info_edx, 26, Feature::sse2); - enable(extended_features_ebx, 29, Feature::sha); - - enable(extended_features_ecx, 8, Feature::gfni); - enable(extended_features_ecx, 9, Feature::vaes); - enable(extended_features_ecx, 10, Feature::vpclmulqdq); - - enable(extended_features_ebx, 3, Feature::bmi1); - enable(extended_features_ebx, 8, Feature::bmi2); - - enable(extended_features_ebx, 9, Feature::ermsb); - - enable(extended_features_eax_leaf_1, 31, Feature::movrs); - - // Detect if CPUID.19h available - if bit::test(extended_features_ecx as usize, 23) { - let CpuidResult { ebx, .. } = __cpuid(0x19); - enable(ebx, 0, Feature::kl); - enable(ebx, 2, Feature::widekl); - } - - // This detects ABM on AMD CPUs and LZCNT on Intel CPUs. - // On intel CPUs with popcnt, lzcnt implements the - // "missing part" of ABM, so we map both to the same - // internal feature. - // - // The `is_x86_feature_detected!("lzcnt")` macro then - // internally maps to Feature::abm. - enable(extended_proc_info_ecx, 5, Feature::lzcnt); - - // As Hygon Dhyana originates from AMD technology and shares most of the architecture with - // AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series - // number(Family 18h). - // - // For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD - // family 17h. - // - // Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf. - // Related Hygon kernel patch can be found on - // http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn - if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" { - // These features are available on AMD arch CPUs: - enable(extended_proc_info_ecx, 6, Feature::sse4a); - enable(extended_proc_info_ecx, 21, Feature::tbm); - enable(extended_proc_info_ecx, 11, Feature::xop); - } - - // `XSAVE` and `AVX` support: - let cpu_xsave = bit::test(proc_info_ecx as usize, 26); - if cpu_xsave { - // 0. Here the CPU supports `XSAVE`. - - // 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and - // supports saving the state of the AVX/AVX2 vector registers on - // context-switches, see: - // - // - [intel: is avx enabled?][is_avx_enabled], - // - [mozilla: sse.cpp][mozilla_sse_cpp]. - // - // [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled - // [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190 - let cpu_osxsave = bit::test(proc_info_ecx as usize, 27); - - if cpu_osxsave { - // 2. The OS must have signaled the CPU that it supports saving and - // restoring the: - // - // * SSE -> `XCR0.SSE[1]` - // * AVX -> `XCR0.AVX[2]` - // * AVX-512 -> `XCR0.AVX-512[7:5]`. - // * AMX -> `XCR0.AMX[18:17]` - // * APX -> `XCR0.APX[19]` - // - // by setting the corresponding bits of `XCR0` to `1`. - // - // This is safe because the CPU supports `xsave` - // and the OS has set `osxsave`. - let xcr0 = unsafe { _xgetbv(0) }; - // Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`: - let os_avx_support = xcr0 & 6 == 6; - // Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 0xe0`: - let os_avx512_support = xcr0 & 0xe0 == 0xe0; - // Test `XCR0.AMX[18:17]` with the mask `0b110_0000_0000_0000_0000 == 0x60000` - let os_amx_support = xcr0 & 0x60000 == 0x60000; - // Test `XCR0.APX[19]` with the mask `0b1000_0000_0000_0000_0000 == 0x80000` - let os_apx_support = xcr0 & 0x80000 == 0x80000; - - // Only if the OS and the CPU support saving/restoring the AVX - // registers we enable `xsave` support: - if os_avx_support { - // See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED - // FEATURES" in the "Intel® 64 and IA-32 Architectures Software - // Developer’s Manual, Volume 1: Basic Architecture": - // - // "Software enables the XSAVE feature set by setting - // CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4 - // instruction). If this bit is 0, execution of any of XGETBV, - // XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV - // causes an invalid-opcode exception (#UD)" - // - enable(proc_info_ecx, 26, Feature::xsave); - - // For `xsaveopt`, `xsavec`, and `xsaves` we need to query: - // Processor Extended State Enumeration Sub-leaf (EAX = 0DH, - // ECX = 1): - if max_basic_leaf >= 0xd { - let CpuidResult { eax: proc_extended_state1_eax, .. } = - __cpuid_count(0xd_u32, 1); - enable(proc_extended_state1_eax, 0, Feature::xsaveopt); - enable(proc_extended_state1_eax, 1, Feature::xsavec); - enable(proc_extended_state1_eax, 3, Feature::xsaves); - } - - // FMA (uses 256-bit wide registers): - let fma = enable(proc_info_ecx, 12, Feature::fma); - - // And AVX/AVX2: - enable(proc_info_ecx, 28, Feature::avx); - enable(extended_features_ebx, 5, Feature::avx2); - - // "Short" versions of AVX512 instructions - enable(extended_features_eax_leaf_1, 4, Feature::avxvnni); - enable(extended_features_eax_leaf_1, 23, Feature::avxifma); - enable(extended_features_edx_leaf_1, 4, Feature::avxvnniint8); - enable(extended_features_edx_leaf_1, 5, Feature::avxneconvert); - enable(extended_features_edx_leaf_1, 10, Feature::avxvnniint16); - - enable(extended_features_eax_leaf_1, 0, Feature::sha512); - enable(extended_features_eax_leaf_1, 1, Feature::sm3); - enable(extended_features_eax_leaf_1, 2, Feature::sm4); - - // For AVX-512 the OS also needs to support saving/restoring - // the extended state, only then we enable AVX-512 support: - // Also, Rust makes `avx512f` imply `fma` and `f16c`, because - // otherwise the assembler is broken. But Intel doesn't guarantee - // that `fma` and `f16c` are available with `avx512f`, so we - // need to check for them separately. - if os_avx512_support && f16c && fma { - enable(extended_features_ebx, 16, Feature::avx512f); - enable(extended_features_ebx, 17, Feature::avx512dq); - enable(extended_features_ebx, 21, Feature::avx512ifma); - enable(extended_features_ebx, 26, Feature::avx512pf); - enable(extended_features_ebx, 27, Feature::avx512er); - enable(extended_features_ebx, 28, Feature::avx512cd); - enable(extended_features_ebx, 30, Feature::avx512bw); - enable(extended_features_ebx, 31, Feature::avx512vl); - enable(extended_features_ecx, 1, Feature::avx512vbmi); - enable(extended_features_ecx, 6, Feature::avx512vbmi2); - enable(extended_features_ecx, 11, Feature::avx512vnni); - enable(extended_features_ecx, 12, Feature::avx512bitalg); - enable(extended_features_ecx, 14, Feature::avx512vpopcntdq); - enable(extended_features_edx, 8, Feature::avx512vp2intersect); - enable(extended_features_edx, 23, Feature::avx512fp16); - enable(extended_features_eax_leaf_1, 5, Feature::avx512bf16); - } - } - - if os_amx_support { - enable(extended_features_edx, 24, Feature::amx_tile); - enable(extended_features_edx, 25, Feature::amx_int8); - enable(extended_features_edx, 22, Feature::amx_bf16); - enable(extended_features_eax_leaf_1, 21, Feature::amx_fp16); - enable(extended_features_edx_leaf_1, 8, Feature::amx_complex); - - if max_basic_leaf >= 0x1e { - let CpuidResult { eax: amx_feature_flags_eax, .. } = - __cpuid_count(0x1e_u32, 1); - - enable(amx_feature_flags_eax, 4, Feature::amx_fp8); - enable(amx_feature_flags_eax, 6, Feature::amx_tf32); - enable(amx_feature_flags_eax, 7, Feature::amx_avx512); - enable(amx_feature_flags_eax, 8, Feature::amx_movrs); - } - } - - if os_apx_support { - enable(extended_features_edx_leaf_1, 21, Feature::apxf); - } - - let avx10_1 = enable(extended_features_edx_leaf_1, 19, Feature::avx10_1); - if avx10_1 { - let CpuidResult { ebx, .. } = __cpuid(0x24); - let avx10_version = ebx & 0xff; - if avx10_version >= 2 { - value.set(Feature::avx10_2 as u32); - } - } - } - } - } - - // Unfortunately, some Skylake chips erroneously report support for BMI1 and - // BMI2 without actual support. These chips don't support AVX, and it seems - // that all Intel chips with non-erroneous support BMI do (I didn't check - // other vendors), so we can disable these flags for chips that don't also - // report support for AVX. - // - // It's possible this will pessimize future chips that do support BMI and - // not AVX, but this seems minor compared to a hard crash you get when - // executing an unsupported instruction (to put it another way, it's safe - // for us to under-report CPU features, but not to over-report them). Still, - // to limit any impact this may have in the future, we only do this for - // Intel chips, as it's a bug only present in their chips. - // - // This bug is documented as `SKL052` in the errata section of this document: - // http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/desktop-6th-gen-core-family-spec-update.pdf - if vendor_id == *b"GenuineIntel" && !value.test(Feature::avx as u32) { - value.unset(Feature::bmi1 as u32); - value.unset(Feature::bmi2 as u32); - } - - value -} diff --git a/crates/std/crates/std_detect/src/detect/test_data/linux-artificial-aarch64.auxv b/crates/std/crates/std_detect/src/detect/test_data/linux-artificial-aarch64.auxv deleted file mode 100644 index ec826afcf38179904c8b4312842dc474687d254c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmY#nKn2Dyt`vkJ0Oh|wySg*oRhf|uCJ&Vmgz}-v8JJ=6P?`me&x*!pN8@v#@j21> r+)zHuJ{~kaFB+c@jV})6!zf7@15Mri+2O+Hy4;xL(d7Bj_~={!cy$wI|)_SNs gSJrR?cW@66@F@G<1kdmSC)u&PV9(#hMY+P{19AETwEzGB diff --git a/crates/std/crates/std_detect/src/detect/test_data/linux-hwcap2-aarch64.auxv b/crates/std/crates/std_detect/src/detect/test_data/linux-hwcap2-aarch64.auxv deleted file mode 100644 index 1d87264b221901c140c379792e7e14c7e74cbf5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmZ9`OA3H6420nn-}vmR=tew)2lh(dNRdt!{@r9gCeR%Af2VWHVUeFJ-e0U`QxRpY kTj9NC30H6fw{Qpdvi~>0BRs)L_OW`vUI_WJ9sLZGFHf=regFUf diff --git a/crates/std/crates/std_detect/src/detect/test_data/linux-no-hwcap2-aarch64.auxv b/crates/std/crates/std_detect/src/detect/test_data/linux-no-hwcap2-aarch64.auxv deleted file mode 100644 index 35f01cc767c507ed0d28a1d349fa84ba33dc7899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 zcmZ9_OA3H66hqO1fBbe-?4-B`7wl3MG#Pk1Ne+3?BmCcK=C5q>eZ{x7-Zey7=b`eh ctlYO+n*aa+ diff --git a/crates/std/crates/std_detect/src/detect/test_data/linux-rpi3.auxv b/crates/std/crates/std_detect/src/detect/test_data/linux-rpi3.auxv deleted file mode 100644 index 0538e661f63ad0676ced42f9ac4cd57787555c20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmY#n0D=qcbpk-@+6H?DHV_}k5d=~xK+FupCJc-WEI?WTh*^P{6NuS?m|?-&G7b<8 z1R$|WX+{QaApHWUi3dvaLTNrIEe@nXKoW>&`?b|c0cnuk@<9CAzpaiRNUsKp11T^7 E07AJE2LJ#7 diff --git a/crates/std/crates/std_detect/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv b/crates/std/crates/std_detect/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv deleted file mode 100644 index 75abc02d17813074bf947fa0725e8509e21109ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmY#jU|{%DQ@&jhNHY|aZx;Yk|9^M0vw`?Pjv$aq0b*t#HfdntU;)w!K+FonoIuPD z#0(S4w{w7KAOMMF%wyr;2J;vgc%U>dl;(rd;!s)=NN0P!*e?&HKgYk=&kv-lfqbYm F0{~-J6Wag) diff --git a/crates/std/crates/std_detect/src/lib.rs b/crates/std/crates/std_detect/src/lib.rs deleted file mode 100644 index f9d79df..0000000 --- a/crates/std/crates/std_detect/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Run-time feature detection for the Rust standard library. -//! -//! To detect whether a feature is enabled in the system running the binary -//! use one of the appropriate macro for the target: -//! -//! * `x86` and `x86_64`: [`is_x86_feature_detected`] -//! * `arm`: [`is_arm_feature_detected`] -//! * `aarch64`: [`is_aarch64_feature_detected`] -//! * `riscv`: [`is_riscv_feature_detected`] -//! * `mips`: [`is_mips_feature_detected`] -//! * `mips64`: [`is_mips64_feature_detected`] -//! * `powerpc`: [`is_powerpc_feature_detected`] -//! * `powerpc64`: [`is_powerpc64_feature_detected`] -//! * `loongarch`: [`is_loongarch_feature_detected`] -//! * `s390x`: [`is_s390x_feature_detected`] - -#![unstable(feature = "stdarch_internal", issue = "none")] -#![feature(staged_api, doc_cfg, allow_internal_unstable)] -#![deny(rust_2018_idioms)] -#![allow(clippy::shadow_reuse)] -#![cfg_attr(test, allow(unused_imports))] -#![no_std] -#![allow(internal_features)] - -#[cfg(test)] -#[macro_use] -extern crate std; - -// rust-lang/rust#83888: removing `extern crate` gives an error that `vec_spare> -#[allow(unused_extern_crates)] -extern crate alloc; - -#[doc(hidden)] -#[unstable(feature = "stdarch_internal", issue = "none")] -pub mod detect; diff --git a/crates/std/crates/std_detect/tests/cpu-detection.rs b/crates/std/crates/std_detect/tests/cpu-detection.rs deleted file mode 100644 index 0aad088..0000000 --- a/crates/std/crates/std_detect/tests/cpu-detection.rs +++ /dev/null @@ -1,335 +0,0 @@ -#![allow(internal_features, unused_features)] -#![feature(stdarch_internal)] -#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))] -#![cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - feature(stdarch_aarch64_feature_detection) -)] -#![cfg_attr( - any(target_arch = "riscv32", target_arch = "riscv64"), - feature(stdarch_riscv_feature_detection) -)] -#![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))] -#![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))] -#![allow(clippy::unwrap_used, clippy::use_debug, clippy::print_stdout)] - -#[cfg_attr( - any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - ), - macro_use -)] -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", -))] -extern crate std_detect; - -#[test] -fn all() { - for (f, e) in std_detect::detect::features() { - println!("{f}: {e}"); - } -} - -#[test] -#[cfg(all(target_arch = "arm", target_os = "freebsd"))] -fn arm_freebsd() { - println!("neon: {}", is_arm_feature_detected!("neon")); - println!("pmull: {}", is_arm_feature_detected!("pmull")); - println!("crc: {}", is_arm_feature_detected!("crc")); - println!("aes: {}", is_arm_feature_detected!("aes")); - println!("sha2: {}", is_arm_feature_detected!("sha2")); -} - -#[test] -#[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))] -fn arm_linux() { - println!("neon: {}", is_arm_feature_detected!("neon")); - println!("pmull: {}", is_arm_feature_detected!("pmull")); - println!("crc: {}", is_arm_feature_detected!("crc")); - println!("aes: {}", is_arm_feature_detected!("aes")); - println!("sha2: {}", is_arm_feature_detected!("sha2")); - println!("dotprod: {}", is_arm_feature_detected!("dotprod")); - println!("i8mm: {}", is_arm_feature_detected!("i8mm")); -} - -#[test] -#[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))] -fn aarch64_linux() { - println!("asimd: {}", is_aarch64_feature_detected!("asimd")); - println!("neon: {}", is_aarch64_feature_detected!("neon")); - println!("pmull: {}", is_aarch64_feature_detected!("pmull")); - println!("fp: {}", is_aarch64_feature_detected!("fp")); - println!("fp16: {}", is_aarch64_feature_detected!("fp16")); - println!("sve: {}", is_aarch64_feature_detected!("sve")); - println!("crc: {}", is_aarch64_feature_detected!("crc")); - println!("lse: {}", is_aarch64_feature_detected!("lse")); - println!("lse2: {}", is_aarch64_feature_detected!("lse2")); - println!("lse128: {}", is_aarch64_feature_detected!("lse128")); - println!("rdm: {}", is_aarch64_feature_detected!("rdm")); - println!("rcpc: {}", is_aarch64_feature_detected!("rcpc")); - println!("rcpc2: {}", is_aarch64_feature_detected!("rcpc2")); - println!("rcpc3: {}", is_aarch64_feature_detected!("rcpc3")); - println!("dotprod: {}", is_aarch64_feature_detected!("dotprod")); - println!("fhm: {}", is_aarch64_feature_detected!("fhm")); - println!("dit: {}", is_aarch64_feature_detected!("dit")); - println!("flagm: {}", is_aarch64_feature_detected!("flagm")); - println!("flagm2: {}", is_aarch64_feature_detected!("flagm2")); - println!("ssbs: {}", is_aarch64_feature_detected!("ssbs")); - println!("sb: {}", is_aarch64_feature_detected!("sb")); - println!("paca: {}", is_aarch64_feature_detected!("paca")); - println!("pacg: {}", is_aarch64_feature_detected!("pacg")); - // println!("pauth-lr: {}", is_aarch64_feature_detected!("pauth-lr")); - println!("dpb: {}", is_aarch64_feature_detected!("dpb")); - println!("dpb2: {}", is_aarch64_feature_detected!("dpb2")); - println!("sve-b16b16: {}", is_aarch64_feature_detected!("sve-b16b16")); - println!("sve2: {}", is_aarch64_feature_detected!("sve2")); - println!("sve2p1: {}", is_aarch64_feature_detected!("sve2p1")); - println!("sve2-aes: {}", is_aarch64_feature_detected!("sve2-aes")); - println!("sve2-sm4: {}", is_aarch64_feature_detected!("sve2-sm4")); - println!("sve2-sha3: {}", is_aarch64_feature_detected!("sve2-sha3")); - println!("sve2-bitperm: {}", is_aarch64_feature_detected!("sve2-bitperm")); - println!("frintts: {}", is_aarch64_feature_detected!("frintts")); - println!("i8mm: {}", is_aarch64_feature_detected!("i8mm")); - println!("f32mm: {}", is_aarch64_feature_detected!("f32mm")); - println!("f64mm: {}", is_aarch64_feature_detected!("f64mm")); - println!("bf16: {}", is_aarch64_feature_detected!("bf16")); - println!("rand: {}", is_aarch64_feature_detected!("rand")); - println!("bti: {}", is_aarch64_feature_detected!("bti")); - println!("mte: {}", is_aarch64_feature_detected!("mte")); - println!("jsconv: {}", is_aarch64_feature_detected!("jsconv")); - println!("fcma: {}", is_aarch64_feature_detected!("fcma")); - println!("aes: {}", is_aarch64_feature_detected!("aes")); - println!("sha2: {}", is_aarch64_feature_detected!("sha2")); - println!("sha3: {}", is_aarch64_feature_detected!("sha3")); - println!("sm4: {}", is_aarch64_feature_detected!("sm4")); - println!("hbc: {}", is_aarch64_feature_detected!("hbc")); - println!("mops: {}", is_aarch64_feature_detected!("mops")); - println!("ecv: {}", is_aarch64_feature_detected!("ecv")); - println!("cssc: {}", is_aarch64_feature_detected!("cssc")); - println!("fpmr: {}", is_aarch64_feature_detected!("fpmr")); - println!("lut: {}", is_aarch64_feature_detected!("lut")); - println!("faminmax: {}", is_aarch64_feature_detected!("faminmax")); - println!("fp8: {}", is_aarch64_feature_detected!("fp8")); - println!("fp8fma: {}", is_aarch64_feature_detected!("fp8fma")); - println!("fp8dot4: {}", is_aarch64_feature_detected!("fp8dot4")); - println!("fp8dot2: {}", is_aarch64_feature_detected!("fp8dot2")); - println!("wfxt: {}", is_aarch64_feature_detected!("wfxt")); - println!("sme: {}", is_aarch64_feature_detected!("sme")); - println!("sme-b16b16: {}", is_aarch64_feature_detected!("sme-b16b16")); - println!("sme-i16i64: {}", is_aarch64_feature_detected!("sme-i16i64")); - println!("sme-f64f64: {}", is_aarch64_feature_detected!("sme-f64f64")); - println!("sme-fa64: {}", is_aarch64_feature_detected!("sme-fa64")); - println!("sme2: {}", is_aarch64_feature_detected!("sme2")); - println!("sme2p1: {}", is_aarch64_feature_detected!("sme2p1")); - println!("sme-f16f16: {}", is_aarch64_feature_detected!("sme-f16f16")); - println!("sme-lutv2: {}", is_aarch64_feature_detected!("sme-lutv2")); - println!("sme-f8f16: {}", is_aarch64_feature_detected!("sme-f8f16")); - println!("sme-f8f32: {}", is_aarch64_feature_detected!("sme-f8f32")); - println!("ssve-fp8fma: {}", is_aarch64_feature_detected!("ssve-fp8fma")); - println!("ssve-fp8dot4: {}", is_aarch64_feature_detected!("ssve-fp8dot4")); - println!("ssve-fp8dot2: {}", is_aarch64_feature_detected!("ssve-fp8dot2")); -} - -#[test] -#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_os = "windows"))] -fn aarch64_windows() { - println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); - println!("fp: {:?}", is_aarch64_feature_detected!("fp")); - println!("crc: {:?}", is_aarch64_feature_detected!("crc")); - println!("lse: {:?}", is_aarch64_feature_detected!("lse")); - println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); - println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv")); - println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); - println!("aes: {:?}", is_aarch64_feature_detected!("aes")); - println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); - println!("sha2: {:?}", is_aarch64_feature_detected!("sha2")); -} - -#[test] -#[cfg(all(target_arch = "aarch64", any(target_os = "freebsd", target_os = "openbsd")))] -fn aarch64_bsd() { - println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); - println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); - println!("fp: {:?}", is_aarch64_feature_detected!("fp")); - println!("fp16: {:?}", is_aarch64_feature_detected!("fp16")); - println!("sve: {:?}", is_aarch64_feature_detected!("sve")); - println!("crc: {:?}", is_aarch64_feature_detected!("crc")); - println!("lse: {:?}", is_aarch64_feature_detected!("lse")); - println!("lse2: {:?}", is_aarch64_feature_detected!("lse2")); - println!("rdm: {:?}", is_aarch64_feature_detected!("rdm")); - println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); - println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); - println!("paca: {:?}", is_aarch64_feature_detected!("paca")); - println!("pacg: {:?}", is_aarch64_feature_detected!("pacg")); - println!("aes: {:?}", is_aarch64_feature_detected!("aes")); - println!("sha2: {:?}", is_aarch64_feature_detected!("sha2")); -} - -#[test] -#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))] -fn aarch64_darwin() { - println!("asimd: {:?}", is_aarch64_feature_detected!("asimd")); - println!("fp: {:?}", is_aarch64_feature_detected!("fp")); - println!("fp16: {:?}", is_aarch64_feature_detected!("fp16")); - println!("pmull: {:?}", is_aarch64_feature_detected!("pmull")); - println!("crc: {:?}", is_aarch64_feature_detected!("crc")); - println!("lse: {:?}", is_aarch64_feature_detected!("lse")); - println!("lse2: {:?}", is_aarch64_feature_detected!("lse2")); - println!("rdm: {:?}", is_aarch64_feature_detected!("rdm")); - println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc")); - println!("rcpc2: {:?}", is_aarch64_feature_detected!("rcpc2")); - println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod")); - println!("fhm: {:?}", is_aarch64_feature_detected!("fhm")); - println!("flagm: {:?}", is_aarch64_feature_detected!("flagm")); - println!("ssbs: {:?}", is_aarch64_feature_detected!("ssbs")); - println!("sb: {:?}", is_aarch64_feature_detected!("sb")); - println!("paca: {:?}", is_aarch64_feature_detected!("paca")); - println!("dpb: {:?}", is_aarch64_feature_detected!("dpb")); - println!("dpb2: {:?}", is_aarch64_feature_detected!("dpb2")); - println!("frintts: {:?}", is_aarch64_feature_detected!("frintts")); - println!("i8mm: {:?}", is_aarch64_feature_detected!("i8mm")); - println!("bf16: {:?}", is_aarch64_feature_detected!("bf16")); - println!("bti: {:?}", is_aarch64_feature_detected!("bti")); - println!("fcma: {:?}", is_aarch64_feature_detected!("fcma")); - println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv")); - println!("aes: {:?}", is_aarch64_feature_detected!("aes")); - println!("sha2: {:?}", is_aarch64_feature_detected!("sha2")); - println!("sha3: {:?}", is_aarch64_feature_detected!("sha3")); -} - -#[test] -#[cfg(all( - any(target_arch = "riscv32", target_arch = "riscv64"), - any(target_os = "linux", target_os = "android") -))] -fn riscv_linux() { - println!("rv32i: {}", is_riscv_feature_detected!("rv32i")); - println!("rv32e: {}", is_riscv_feature_detected!("rv32e")); - println!("rv64i: {}", is_riscv_feature_detected!("rv64i")); - println!("rv128i: {}", is_riscv_feature_detected!("rv128i")); - println!("unaligned-scalar-mem: {}", is_riscv_feature_detected!("unaligned-scalar-mem")); - println!("unaligned-vector-mem: {}", is_riscv_feature_detected!("unaligned-vector-mem")); - println!("zicsr: {}", is_riscv_feature_detected!("zicsr")); - println!("zicntr: {}", is_riscv_feature_detected!("zicntr")); - println!("zihpm: {}", is_riscv_feature_detected!("zihpm")); - println!("zifencei: {}", is_riscv_feature_detected!("zifencei")); - println!("zihintntl: {}", is_riscv_feature_detected!("zihintntl")); - println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause")); - println!("zimop: {}", is_riscv_feature_detected!("zimop")); - println!("zicbom: {}", is_riscv_feature_detected!("zicbom")); - println!("zicboz: {}", is_riscv_feature_detected!("zicboz")); - println!("zicond: {}", is_riscv_feature_detected!("zicond")); - println!("m: {}", is_riscv_feature_detected!("m")); - println!("a: {}", is_riscv_feature_detected!("a")); - println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc")); - println!("zaamo: {}", is_riscv_feature_detected!("zaamo")); - println!("zawrs: {}", is_riscv_feature_detected!("zawrs")); - println!("zabha: {}", is_riscv_feature_detected!("zabha")); - println!("zacas: {}", is_riscv_feature_detected!("zacas")); - println!("zam: {}", is_riscv_feature_detected!("zam")); - println!("ztso: {}", is_riscv_feature_detected!("ztso")); - println!("f: {}", is_riscv_feature_detected!("f")); - println!("d: {}", is_riscv_feature_detected!("d")); - println!("q: {}", is_riscv_feature_detected!("q")); - println!("zfh: {}", is_riscv_feature_detected!("zfh")); - println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin")); - println!("zfa: {}", is_riscv_feature_detected!("zfa")); - println!("zfbfmin: {}", is_riscv_feature_detected!("zfbfmin")); - println!("zfinx: {}", is_riscv_feature_detected!("zfinx")); - println!("zdinx: {}", is_riscv_feature_detected!("zdinx")); - println!("zhinx: {}", is_riscv_feature_detected!("zhinx")); - println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin")); - println!("c: {}", is_riscv_feature_detected!("c")); - println!("zca: {}", is_riscv_feature_detected!("zca")); - println!("zcf: {}", is_riscv_feature_detected!("zcf")); - println!("zcd: {}", is_riscv_feature_detected!("zcd")); - println!("zcb: {}", is_riscv_feature_detected!("zcb")); - println!("zcmop: {}", is_riscv_feature_detected!("zcmop")); - println!("b: {}", is_riscv_feature_detected!("b")); - println!("zba: {}", is_riscv_feature_detected!("zba")); - println!("zbb: {}", is_riscv_feature_detected!("zbb")); - println!("zbc: {}", is_riscv_feature_detected!("zbc")); - println!("zbs: {}", is_riscv_feature_detected!("zbs")); - println!("zbkb: {}", is_riscv_feature_detected!("zbkb")); - println!("zbkc: {}", is_riscv_feature_detected!("zbkc")); - println!("zbkx: {}", is_riscv_feature_detected!("zbkx")); - println!("zknd: {}", is_riscv_feature_detected!("zknd")); - println!("zkne: {}", is_riscv_feature_detected!("zkne")); - println!("zknh: {}", is_riscv_feature_detected!("zknh")); - println!("zksed: {}", is_riscv_feature_detected!("zksed")); - println!("zksh: {}", is_riscv_feature_detected!("zksh")); - println!("zkr: {}", is_riscv_feature_detected!("zkr")); - println!("zkn: {}", is_riscv_feature_detected!("zkn")); - println!("zks: {}", is_riscv_feature_detected!("zks")); - println!("zk: {}", is_riscv_feature_detected!("zk")); - println!("zkt: {}", is_riscv_feature_detected!("zkt")); - println!("v: {}", is_riscv_feature_detected!("v")); - println!("zve32x: {}", is_riscv_feature_detected!("zve32x")); - println!("zve32f: {}", is_riscv_feature_detected!("zve32f")); - println!("zve64x: {}", is_riscv_feature_detected!("zve64x")); - println!("zve64f: {}", is_riscv_feature_detected!("zve64f")); - println!("zve64d: {}", is_riscv_feature_detected!("zve64d")); - println!("zvfh: {}", is_riscv_feature_detected!("zvfh")); - println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin")); - println!("zvfbfmin: {}", is_riscv_feature_detected!("zvfbfmin")); - println!("zvfbfwma: {}", is_riscv_feature_detected!("zvfbfwma")); - println!("zvbb: {}", is_riscv_feature_detected!("zvbb")); - println!("zvbc: {}", is_riscv_feature_detected!("zvbc")); - println!("zvkb: {}", is_riscv_feature_detected!("zvkb")); - println!("zvkg: {}", is_riscv_feature_detected!("zvkg")); - println!("zvkned: {}", is_riscv_feature_detected!("zvkned")); - println!("zvknha: {}", is_riscv_feature_detected!("zvknha")); - println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb")); - println!("zvksed: {}", is_riscv_feature_detected!("zvksed")); - println!("zvksh: {}", is_riscv_feature_detected!("zvksh")); - println!("zvkn: {}", is_riscv_feature_detected!("zvkn")); - println!("zvknc: {}", is_riscv_feature_detected!("zvknc")); - println!("zvkng: {}", is_riscv_feature_detected!("zvkng")); - println!("zvks: {}", is_riscv_feature_detected!("zvks")); - println!("zvksc: {}", is_riscv_feature_detected!("zvksc")); - println!("zvksg: {}", is_riscv_feature_detected!("zvksg")); - println!("zvkt: {}", is_riscv_feature_detected!("zvkt")); - println!("j: {}", is_riscv_feature_detected!("j")); - println!("p: {}", is_riscv_feature_detected!("p")); -} - -#[test] -#[cfg(all(target_arch = "powerpc", target_os = "linux"))] -fn powerpc_linux() { - println!("altivec: {}", is_powerpc_feature_detected!("altivec")); - println!("vsx: {}", is_powerpc_feature_detected!("vsx")); - println!("power8: {}", is_powerpc_feature_detected!("power8")); -} - -#[test] -#[cfg(all( - target_arch = "powerpc64", - any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"), -))] -fn powerpc64_linux_or_bsd() { - println!("altivec: {}", is_powerpc64_feature_detected!("altivec")); - println!("vsx: {}", is_powerpc64_feature_detected!("vsx")); - println!("power8: {}", is_powerpc64_feature_detected!("power8")); - println!("power9: {}", is_powerpc64_feature_detected!("power9")); -} - -#[test] -#[cfg(all(target_arch = "s390x", target_os = "linux",))] -fn s390x_linux() { - println!("vector: {}", is_s390x_feature_detected!("vector")); -} diff --git a/crates/std/crates/std_detect/tests/macro_trailing_commas.rs b/crates/std/crates/std_detect/tests/macro_trailing_commas.rs deleted file mode 100644 index a60b34a..0000000 --- a/crates/std/crates/std_detect/tests/macro_trailing_commas.rs +++ /dev/null @@ -1,110 +0,0 @@ -#![allow(internal_features, unused_features)] -#![cfg_attr( - any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "x86", - target_arch = "x86_64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch32", - target_arch = "loongarch64" - ), - feature(stdarch_internal) -)] -#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))] -#![cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - feature(stdarch_aarch64_feature_detection) -)] -#![cfg_attr( - any(target_arch = "powerpc", target_arch = "powerpc64"), - feature(stdarch_powerpc_feature_detection) -)] -#![cfg_attr( - any(target_arch = "riscv32", target_arch = "riscv64"), - feature(stdarch_riscv_feature_detection) -)] -#![cfg_attr( - any(target_arch = "loongarch32", target_arch = "loongarch64"), - feature(stdarch_loongarch_feature_detection) -)] - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "x86", - target_arch = "x86_64", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch32", - target_arch = "loongarch64" -))] -#[macro_use] -extern crate std_detect; - -#[test] -#[cfg(target_arch = "arm")] -fn arm() { - let _ = is_arm_feature_detected!("neon"); - let _ = is_arm_feature_detected!("neon",); -} - -#[test] -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] -fn aarch64() { - let _ = is_aarch64_feature_detected!("fp"); - let _ = is_aarch64_feature_detected!("fp",); -} - -#[test] -#[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] -fn loongarch() { - let _ = is_loongarch_feature_detected!("32s"); - let _ = is_loongarch_feature_detected!("32s",); - let _ = is_loongarch_feature_detected!("lsx"); - let _ = is_loongarch_feature_detected!("lsx",); -} - -#[test] -#[cfg(target_arch = "powerpc")] -fn powerpc() { - let _ = is_powerpc_feature_detected!("altivec"); - let _ = is_powerpc_feature_detected!("altivec",); -} - -#[test] -#[cfg(target_arch = "powerpc64")] -fn powerpc64() { - let _ = is_powerpc64_feature_detected!("altivec"); - let _ = is_powerpc64_feature_detected!("altivec",); -} - -#[test] -#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] -fn riscv() { - let _ = is_riscv_feature_detected!("zk"); - let _ = is_riscv_feature_detected!("zk",); -} - -#[test] -#[cfg(target_arch = "s390x")] -fn s390x() { - let _ = is_s390x_feature_detected!("vector"); - let _ = is_s390x_feature_detected!("vector",); -} - -#[test] -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn x86() { - let _ = is_x86_feature_detected!("sse"); - let _ = is_x86_feature_detected!("sse",); -} diff --git a/crates/std/crates/std_detect/tests/x86-specific.rs b/crates/std/crates/std_detect/tests/x86-specific.rs deleted file mode 100644 index 90ca322..0000000 --- a/crates/std/crates/std_detect/tests/x86-specific.rs +++ /dev/null @@ -1,90 +0,0 @@ -#![cfg(any(target_arch = "x86", target_arch = "x86_64"))] -#![allow(internal_features)] -#![feature(stdarch_internal, x86_amx_intrinsics, xop_target_feature, movrs_target_feature)] - -#[macro_use] -extern crate std_detect; - -#[test] -fn dump() { - println!("aes: {:?}", is_x86_feature_detected!("aes")); - println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq")); - println!("rdrand: {:?}", is_x86_feature_detected!("rdrand")); - println!("rdseed: {:?}", is_x86_feature_detected!("rdseed")); - println!("tsc: {:?}", is_x86_feature_detected!("tsc")); - println!("sse: {:?}", is_x86_feature_detected!("sse")); - println!("sse2: {:?}", is_x86_feature_detected!("sse2")); - println!("sse3: {:?}", is_x86_feature_detected!("sse3")); - println!("ssse3: {:?}", is_x86_feature_detected!("ssse3")); - println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1")); - println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2")); - println!("sse4a: {:?}", is_x86_feature_detected!("sse4a")); - println!("sha: {:?}", is_x86_feature_detected!("sha")); - println!("f16c: {:?}", is_x86_feature_detected!("f16c")); - println!("avx: {:?}", is_x86_feature_detected!("avx")); - println!("avx2: {:?}", is_x86_feature_detected!("avx2")); - println!("sha512: {:?}", is_x86_feature_detected!("sha512")); - println!("sm3: {:?}", is_x86_feature_detected!("sm3")); - println!("sm4: {:?}", is_x86_feature_detected!("sm4")); - println!("avx512f: {:?}", is_x86_feature_detected!("avx512f")); - println!("avx512cd: {:?}", is_x86_feature_detected!("avx512cd")); - println!("avx512er: {:?}", is_x86_feature_detected!("avx512er")); - println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf")); - println!("avx512bw: {:?}", is_x86_feature_detected!("avx512bw")); - println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq")); - println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl")); - println!("avx512_ifma: {:?}", is_x86_feature_detected!("avx512ifma")); - println!("avx512vbmi {:?}", is_x86_feature_detected!("avx512vbmi")); - println!("avx512_vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq")); - println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2")); - println!("gfni: {:?}", is_x86_feature_detected!("gfni")); - println!("vaes: {:?}", is_x86_feature_detected!("vaes")); - println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq")); - println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni")); - println!("avx512bitalg: {:?}", is_x86_feature_detected!("avx512bitalg")); - println!("avx512bf16: {:?}", is_x86_feature_detected!("avx512bf16")); - println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect")); - println!("avx512fp16: {:?}", is_x86_feature_detected!("avx512fp16")); - println!("fma: {:?}", is_x86_feature_detected!("fma")); - println!("abm: {:?}", is_x86_feature_detected!("abm")); - println!("bmi: {:?}", is_x86_feature_detected!("bmi1")); - println!("bmi2: {:?}", is_x86_feature_detected!("bmi2")); - println!("tbm: {:?}", is_x86_feature_detected!("tbm")); - println!("popcnt: {:?}", is_x86_feature_detected!("popcnt")); - println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt")); - println!("fxsr: {:?}", is_x86_feature_detected!("fxsr")); - println!("xsave: {:?}", is_x86_feature_detected!("xsave")); - println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt")); - println!("xsaves: {:?}", is_x86_feature_detected!("xsaves")); - println!("xsavec: {:?}", is_x86_feature_detected!("xsavec")); - println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b")); - println!("adx: {:?}", is_x86_feature_detected!("adx")); - println!("rtm: {:?}", is_x86_feature_detected!("rtm")); - println!("movbe: {:?}", is_x86_feature_detected!("movbe")); - println!("avxvnni: {:?}", is_x86_feature_detected!("avxvnni")); - println!("avxvnniint8: {:?}", is_x86_feature_detected!("avxvnniint8")); - println!("avxneconvert: {:?}", is_x86_feature_detected!("avxneconvert")); - println!("avxifma: {:?}", is_x86_feature_detected!("avxifma")); - println!("avxvnniint16: {:?}", is_x86_feature_detected!("avxvnniint16")); - println!("amx-bf16: {:?}", is_x86_feature_detected!("amx-bf16")); - println!("amx-tile: {:?}", is_x86_feature_detected!("amx-tile")); - println!("amx-int8: {:?}", is_x86_feature_detected!("amx-int8")); - println!("amx-fp16: {:?}", is_x86_feature_detected!("amx-fp16")); - println!("amx-complex: {:?}", is_x86_feature_detected!("amx-complex")); - println!("xop: {:?}", is_x86_feature_detected!("xop")); - println!("kl: {:?}", is_x86_feature_detected!("kl")); - println!("widekl: {:?}", is_x86_feature_detected!("widekl")); - println!("movrs: {:?}", is_x86_feature_detected!("movrs")); - println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8")); - println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32")); - println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512")); - println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs")); -} - -#[test] -#[allow(deprecated)] -fn x86_deprecated() { - println!("avx512gfni {:?}", is_x86_feature_detected!("avx512gfni")); - println!("avx512vaes {:?}", is_x86_feature_detected!("avx512vaes")); - println!("avx512vpclmulqdq {:?}", is_x86_feature_detected!("avx512vpclmulqdq")); -} diff --git a/crates/std/patches.sed b/crates/std/patches.sed deleted file mode 100644 index 45a91dd..0000000 --- a/crates/std/patches.sed +++ /dev/null @@ -1,6 +0,0 @@ -s|alloc::ffi|alloc_crate::ffi|g -s|alloc::slice::Join|alloc_crate::slice::Join|g -s|alloc::bstr|alloc_crate::bstr|g -s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g -s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g -/\[doc = include_str!/c \ // todo retreive docs diff --git a/crates/std/patches/collections/hash/map.sed b/crates/std/patches/collections/hash/map.sed deleted file mode 100644 index c5f05ff..0000000 --- a/crates/std/patches/collections/hash/map.sed +++ /dev/null @@ -1,2 +0,0 @@ -362d -361a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")] diff --git a/crates/std/patches/collections/hash/set.sed b/crates/std/patches/collections/hash/set.sed deleted file mode 100644 index 9a38ec1..0000000 --- a/crates/std/patches/collections/hash/set.sed +++ /dev/null @@ -1,2 +0,0 @@ -234d -233a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")] diff --git a/crates/std/patches/sys/alloc/mod.sed b/crates/std/patches/sys/alloc/mod.sed deleted file mode 100644 index 22d2e31..0000000 --- a/crates/std/patches/sys/alloc/mod.sed +++ /dev/null @@ -1,3 +0,0 @@ -109a \ target_os = "survos" => { \ - mod survos; \ - } diff --git a/crates/std/src/backtrace/tests.rs b/crates/std/src/backtrace/tests.rs deleted file mode 100644 index b68b528..0000000 --- a/crates/std/src/backtrace/tests.rs +++ /dev/null @@ -1,98 +0,0 @@ -use super::*; -use crate::panic::RefUnwindSafe; - -fn generate_fake_frames() -> Vec { - vec![ - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"std::backtrace::Backtrace::create".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), - lineno: Some(100), - colno: None, - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"__rust_maybe_catch_panic".to_vec()), - filename: None, - lineno: None, - colno: None, - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![ - BacktraceSymbol { - name: Some(b"std::rt::lang_start_internal".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(300), - colno: Some(5), - }, - BacktraceSymbol { - name: Some(b"std::rt::lang_start".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(400), - colno: None, - }, - ], - }, - ] -} - -#[test] -fn test_debug() { - let backtrace = Backtrace { - inner: Inner::Captured( - (Capture { actual_start: 1, frames: generate_fake_frames() }).into(), - ), - }; - - #[rustfmt::skip] - let expected = "Backtrace [\ - \n { fn: \"__rust_maybe_catch_panic\" },\ - \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\ - \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\ - \n]"; - - assert_eq!(format!("{backtrace:#?}"), expected); - - // Format the backtrace a second time, just to make sure lazily resolved state is stable - assert_eq!(format!("{backtrace:#?}"), expected); -} - -#[test] -fn test_frames() { - let backtrace = Backtrace { - inner: Inner::Captured( - (Capture { actual_start: 1, frames: generate_fake_frames() }).into(), - ), - }; - - let frames = backtrace.frames(); - - #[rustfmt::skip] - let expected = vec![ - "[ - { fn: \"std::backtrace::Backtrace::create\", file: \"rust/backtrace.rs\", line: 100 }, -]", - "[ - { fn: \"__rust_maybe_catch_panic\" }, -]", - "[ - { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 }, - { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 }, -]" - ]; - - let mut iter = frames.iter().zip(expected.iter()); - - assert!(iter.all(|(f, e)| format!("{f:#?}") == *e)); -} - -#[test] -fn backtrace_unwind_safe() { - fn assert_unwind_safe() {} - assert_unwind_safe::(); -} diff --git a/crates/std/src/collections/hash/map.rs b/crates/std/src/collections/hash/map.rs deleted file mode 100644 index cf83a29..0000000 --- a/crates/std/src/collections/hash/map.rs +++ /dev/null @@ -1,3038 +0,0 @@ -#[cfg(test)] -mod tests; - -use hashbrown::hash_map as base; - -use self::Entry::*; -use crate::alloc::{Allocator, Global}; -use crate::borrow::Borrow; -use crate::collections::{TryReserveError, TryReserveErrorKind}; -use crate::error::Error; -use crate::fmt::{self, Debug}; -use crate::hash::{BuildHasher, Hash, RandomState}; -use crate::iter::FusedIterator; -use crate::ops::Index; - -/// A [hash map] implemented with quadratic probing and SIMD lookup. -/// -/// By default, `HashMap` uses a hashing algorithm selected to provide -/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a -/// reasonable best-effort is made to generate this seed from a high quality, -/// secure source of randomness provided by the host without blocking the -/// program. Because of this, the randomness of the seed depends on the output -/// quality of the system's random number coroutine when the seed is created. -/// In particular, seeds generated when the system's entropy pool is abnormally -/// low such as during system boot may be of a lower quality. -/// -/// The default hashing algorithm is currently SipHash 1-3, though this is -/// subject to change at any point in the future. While its performance is very -/// competitive for medium sized keys, other hashing algorithms will outperform -/// it for small keys such as integers as well as large keys such as long -/// strings, though those algorithms will typically *not* protect against -/// attacks such as HashDoS. -/// -/// The hashing algorithm can be replaced on a per-`HashMap` basis using the -/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods. -/// There are many alternative [hashing algorithms available on crates.io]. -/// -/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although -/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. -/// If you implement these yourself, it is important that the following -/// property holds: -/// -/// ```text -/// k1 == k2 -> hash(k1) == hash(k2) -/// ``` -/// -/// In other words, if two keys are equal, their hashes must be equal. -/// Violating this property is a logic error. -/// -/// It is also a logic error for a key to be modified in such a way that the key's -/// hash, as determined by the [`Hash`] trait, or its equality, as determined by -/// the [`Eq`] trait, changes while it is in the map. This is normally only -/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// The behavior resulting from either logic error is not specified, but will -/// be encapsulated to the `HashMap` that observed the logic error and not -/// result in undefined behavior. This could include panics, incorrect results, -/// aborts, memory leaks, and non-termination. -/// -/// The hash table implementation is a Rust port of Google's [SwissTable]. -/// The original C++ version of SwissTable can be found [here], and this -/// [CppCon talk] gives an overview of how the algorithm works. -/// -/// [hash map]: crate::collections#use-a-hashmap-when -/// [hashing algorithms available on crates.io]: https://crates.io/keywords/hasher -/// [SwissTable]: https://abseil.io/blog/20180927-swisstables -/// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h -/// [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4 -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `HashMap` in this example). -/// let mut book_reviews = HashMap::new(); -/// -/// // Review some books. -/// book_reviews.insert( -/// "Adventures of Huckleberry Finn".to_string(), -/// "My favorite book.".to_string(), -/// ); -/// book_reviews.insert( -/// "Grimms' Fairy Tales".to_string(), -/// "Masterpiece.".to_string(), -/// ); -/// book_reviews.insert( -/// "Pride and Prejudice".to_string(), -/// "Very enjoyable.".to_string(), -/// ); -/// book_reviews.insert( -/// "The Adventures of Sherlock Holmes".to_string(), -/// "Eye lyked it alot.".to_string(), -/// ); -/// -/// // Check for a specific one. -/// // When collections store owned values (String), they can still be -/// // queried using references (&str). -/// if !book_reviews.contains_key("Les Misérables") { -/// println!("We've got {} reviews, but Les Misérables ain't one.", -/// book_reviews.len()); -/// } -/// -/// // oops, this review has a lot of spelling mistakes, let's delete it. -/// book_reviews.remove("The Adventures of Sherlock Holmes"); -/// -/// // Look up the values associated with some keys. -/// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; -/// for &book in &to_find { -/// match book_reviews.get(book) { -/// Some(review) => println!("{book}: {review}"), -/// None => println!("{book} is unreviewed.") -/// } -/// } -/// -/// // Look up the value for a key (will panic if the key is not found). -/// println!("Review for Jane: {}", book_reviews["Pride and Prejudice"]); -/// -/// // Iterate over everything. -/// for (book, review) in &book_reviews { -/// println!("{book}: \"{review}\""); -/// } -/// ``` -/// -/// A `HashMap` with a known list of items can be initialized from an array: -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let solar_distance = HashMap::from([ -/// ("Mercury", 0.4), -/// ("Venus", 0.7), -/// ("Earth", 1.0), -/// ("Mars", 1.5), -/// ]); -/// ``` -/// -/// ## `Entry` API -/// -/// `HashMap` implements an [`Entry` API](#method.entry), which allows -/// for complex methods of getting, setting, updating and removing keys and -/// their values: -/// -/// ``` -/// use std::collections::HashMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `HashMap<&str, u8>` in this example). -/// let mut player_stats = HashMap::new(); -/// -/// fn random_stat_buff() -> u8 { -/// // could actually return some random value here - let's just return -/// // some fixed value for now -/// 42 -/// } -/// -/// // insert a key only if it doesn't already exist -/// player_stats.entry("health").or_insert(100); -/// -/// // insert a key using a function that provides a new value only if it -/// // doesn't already exist -/// player_stats.entry("defence").or_insert_with(random_stat_buff); -/// -/// // update a key, guarding against the key possibly not being set -/// let stat = player_stats.entry("attack").or_insert(100); -/// *stat += random_stat_buff(); -/// -/// // modify an entry before an insert with in-place mutation -/// player_stats.entry("mana").and_modify(|mana| *mana += 200).or_insert(100); -/// ``` -/// -/// ## Usage with custom key types -/// -/// The easiest way to use `HashMap` with a custom key type is to derive [`Eq`] and [`Hash`]. -/// We must also derive [`PartialEq`]. -/// -/// [`RefCell`]: crate::cell::RefCell -/// [`Cell`]: crate::cell::Cell -/// [`default`]: Default::default -/// [`with_hasher`]: Self::with_hasher -/// [`with_capacity_and_hasher`]: Self::with_capacity_and_hasher -/// -/// ``` -/// use std::collections::HashMap; -/// -/// #[derive(Hash, Eq, PartialEq, Debug)] -/// struct Viking { -/// name: String, -/// country: String, -/// } -/// -/// impl Viking { -/// /// Creates a new Viking. -/// fn new(name: &str, country: &str) -> Viking { -/// Viking { name: name.to_string(), country: country.to_string() } -/// } -/// } -/// -/// // Use a HashMap to store the vikings' health points. -/// let vikings = HashMap::from([ -/// (Viking::new("Einar", "Norway"), 25), -/// (Viking::new("Olaf", "Denmark"), 24), -/// (Viking::new("Harald", "Iceland"), 12), -/// ]); -/// -/// // Use derived implementation to print the status of the vikings. -/// for (viking, health) in &vikings { -/// println!("{viking:?} has {health} hp"); -/// } -/// ``` -/// -/// # Usage in `const` and `static` -/// -/// As explained above, `HashMap` is randomly seeded: each `HashMap` instance uses a different seed, -/// which means that `HashMap::new` normally cannot be used in a `const` or `static` initializer. -/// -/// However, if you need to use a `HashMap` in a `const` or `static` initializer while retaining -/// random seed generation, you can wrap the `HashMap` in [`LazyLock`]. -/// -/// Alternatively, you can construct a `HashMap` in a `const` or `static` initializer using a different -/// hasher that does not rely on a random seed. **Be aware that a `HashMap` created this way is not -/// resistant to HashDoS attacks!** -/// -/// [`LazyLock`]: crate::sync::LazyLock -/// ```rust -/// use std::collections::HashMap; -/// use std::hash::{BuildHasherDefault, DefaultHasher}; -/// use std::sync::{LazyLock, Mutex}; -/// -/// // HashMaps with a fixed, non-random hasher -/// const NONRANDOM_EMPTY_MAP: HashMap, BuildHasherDefault> = -/// HashMap::with_hasher(BuildHasherDefault::new()); -/// static NONRANDOM_MAP: Mutex, BuildHasherDefault>> = -/// Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); -/// -/// // HashMaps using LazyLock to retain random seeding -/// const RANDOM_EMPTY_MAP: LazyLock>> = -/// LazyLock::new(HashMap::new); -/// static RANDOM_MAP: LazyLock>>> = -/// LazyLock::new(|| Mutex::new(HashMap::new())); -/// ``` - -#[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_insignificant_dtor] -pub struct HashMap< - K, - V, - S = RandomState, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::HashMap, -} - -impl HashMap { - /// Creates an empty `HashMap`. - /// - /// The hash map is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> HashMap { - Default::default() - } - - /// Creates an empty `HashMap` with at least the specified capacity. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> HashMap { - HashMap::with_capacity_and_hasher(capacity, Default::default()) - } -} - -impl HashMap { - /// Creates an empty `HashMap` using the given allocator. - /// - /// The hash map is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// ``` - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_in(alloc: A) -> Self { - HashMap::with_hasher_in(Default::default(), alloc) - } - - /// Creates an empty `HashMap` with at least the specified capacity using - /// the given allocator. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); - /// ``` - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - HashMap::with_capacity_and_hasher_in(capacity, Default::default(), alloc) - } -} - -impl HashMap { - /// Creates an empty `HashMap` which will use the given hash builder to hash - /// keys. - /// - /// The created map has the default initial capacity. - /// - /// Warning: `hash_builder` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashMap` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut map = HashMap::with_hasher(s); - /// map.insert(1, 2); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_unstable(feature = "custom_std", issue = "none")] - pub const fn with_hasher(hash_builder: S) -> HashMap { - HashMap { base: base::HashMap::with_hasher(hash_builder) } - } - - /// Creates an empty `HashMap` with at least the specified capacity, using - /// `hasher` to hash the keys. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the `HashMap` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut map = HashMap::with_capacity_and_hasher(10, s); - /// map.insert(1, 2); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap { - HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) } - } -} - -impl HashMap { - /// Creates an empty `HashMap` which will use the given hash builder and - /// allocator. - /// - /// The created map has the default initial capacity. - /// - /// Warning: `hash_builder` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashMap` to be useful, see its documentation for details. - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { - HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } - } - - /// Creates an empty `HashMap` with at least the specified capacity, using - /// `hasher` to hash the keys and `alloc` to allocate memory. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow HashMaps to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the `HashMap` to be useful, see its documentation for details. - /// - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { - HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) } - } - - /// Returns the number of elements the map can hold without reallocating. - /// - /// This number is a lower bound; the `HashMap` might be able to hold - /// more, but is guaranteed to be able to hold at least this many. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let map: HashMap = HashMap::with_capacity(100); - /// assert!(map.capacity() >= 100); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.base.capacity() - } - - /// An iterator visiting all keys in arbitrary order. - /// The iterator element type is `&'a K`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// for key in map.keys() { - /// println!("{key}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over keys takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn keys(&self) -> Keys<'_, K, V> { - Keys { inner: self.iter() } - } - - /// Creates a consuming iterator visiting all the keys in arbitrary order. - /// The map cannot be used after calling this. - /// The iterator element type is `K`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// let mut vec: Vec<&str> = map.into_keys().collect(); - /// // The `IntoKeys` iterator produces keys in arbitrary order, so the - /// // keys must be sorted to test them against a sorted array. - /// vec.sort_unstable(); - /// assert_eq!(vec, ["a", "b", "c"]); - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over keys takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_keys(self) -> IntoKeys { - IntoKeys { inner: self.into_iter() } - } - - /// An iterator visiting all values in arbitrary order. - /// The iterator element type is `&'a V`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// for val in map.values() { - /// println!("{val}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over values takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn values(&self) -> Values<'_, K, V> { - Values { inner: self.iter() } - } - - /// An iterator visiting all values mutably in arbitrary order. - /// The iterator element type is `&'a mut V`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// for val in map.values_mut() { - /// *val = *val + 10; - /// } - /// - /// for val in map.values() { - /// println!("{val}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over values takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "map_values_mut", since = "1.10.0")] - pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { - ValuesMut { inner: self.iter_mut() } - } - - /// Creates a consuming iterator visiting all the values in arbitrary order. - /// The map cannot be used after calling this. - /// The iterator element type is `V`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// let mut vec: Vec = map.into_values().collect(); - /// // The `IntoValues` iterator produces values in arbitrary order, so - /// // the values must be sorted to test them against a sorted array. - /// vec.sort_unstable(); - /// assert_eq!(vec, [1, 2, 3]); - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over values takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_values(self) -> IntoValues { - IntoValues { inner: self.into_iter() } - } - - /// An iterator visiting all key-value pairs in arbitrary order. - /// The iterator element type is `(&'a K, &'a V)`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// for (key, val) in map.iter() { - /// println!("key: {key} val: {val}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over map takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.iter() } - } - - /// An iterator visiting all key-value pairs in arbitrary order, - /// with mutable references to the values. - /// The iterator element type is `(&'a K, &'a mut V)`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// // Update all values - /// for (_, val) in map.iter_mut() { - /// *val *= 2; - /// } - /// - /// for (key, val) in &map { - /// println!("key: {key} val: {val}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over map takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut { base: self.base.iter_mut() } - } - - /// Returns the number of elements in the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// assert_eq!(a.len(), 0); - /// a.insert(1, "a"); - /// assert_eq!(a.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.base.len() - } - - /// Returns `true` if the map contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// assert!(a.is_empty()); - /// a.insert(1, "a"); - /// assert!(!a.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.base.is_empty() - } - - /// Clears the map, returning all key-value pairs as an iterator. Keeps the - /// allocated memory for reuse. - /// - /// If the returned iterator is dropped before being fully consumed, it - /// drops the remaining key-value pairs. The returned iterator keeps a - /// mutable borrow on the map to optimize its implementation. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// - /// for (k, v) in a.drain().take(1) { - /// assert!(k == 1 || k == 2); - /// assert!(v == "a" || v == "b"); - /// } - /// - /// assert!(a.is_empty()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, K, V, A> { - Drain { base: self.base.drain() } - } - - /// Creates an iterator which uses a closure to determine if an element (key-value pair) should be removed. - /// - /// If the closure returns `true`, the element is removed from the map and - /// yielded. If the closure returns `false`, or panics, the element remains - /// in the map and will not be yielded. - /// - /// The iterator also lets you mutate the value of each element in the - /// closure, regardless of whether you choose to keep or remove it. - /// - /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating - /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain`] with a negated predicate if you do not need the returned iterator. - /// - /// [`retain`]: HashMap::retain - /// - /// # Examples - /// - /// Splitting a map into even and odd keys, reusing the original map: - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = (0..8).map(|x| (x, x)).collect(); - /// let extracted: HashMap = map.extract_if(|k, _v| k % 2 == 0).collect(); - /// - /// let mut evens = extracted.keys().copied().collect::>(); - /// let mut odds = map.keys().copied().collect::>(); - /// evens.sort(); - /// odds.sort(); - /// - /// assert_eq!(evens, vec![0, 2, 4, 6]); - /// assert_eq!(odds, vec![1, 3, 5, 7]); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> - where - F: FnMut(&K, &mut V) -> bool, - { - ExtractIf { base: self.base.extract_if(pred) } - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`. - /// The elements are visited in unsorted (and unspecified) order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = (0..8).map(|x| (x, x*10)).collect(); - /// map.retain(|&k, _| k % 2 == 0); - /// assert_eq!(map.len(), 4); - /// ``` - /// - /// # Performance - /// - /// In the current implementation, this operation takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "retain_hash_collection", since = "1.18.0")] - pub fn retain(&mut self, f: F) - where - F: FnMut(&K, &mut V) -> bool, - { - self.base.retain(f) - } - - /// Clears the map, removing all key-value pairs. Keeps the allocated memory - /// for reuse. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut a = HashMap::new(); - /// a.insert(1, "a"); - /// a.clear(); - /// assert!(a.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.base.clear(); - } - - /// Returns a reference to the map's [`BuildHasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::hash::RandomState; - /// - /// let hasher = RandomState::new(); - /// let map: HashMap = HashMap::with_hasher(hasher); - /// let hasher: &RandomState = map.hasher(); - /// ``` - #[inline] - #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] - pub fn hasher(&self) -> &S { - self.base.hasher() - } -} - -impl HashMap -where - K: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the `HashMap`. The collection may reserve more space to speculatively - /// avoid frequent reallocations. After calling `reserve`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if capacity is already sufficient. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows [`usize`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// map.reserve(10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.base.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the `HashMap`. The collection may reserve more space to speculatively - /// avoid frequent reallocations. After calling `try_reserve`, - /// capacity will be greater than or equal to `self.len() + additional` if - /// it returns `Ok(())`. - /// Does nothing if capacity is already sufficient. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, isize> = HashMap::new(); - /// map.try_reserve(10).expect("why is the test harness OOMing on a handful of bytes?"); - /// ``` - #[inline] - #[stable(feature = "try_reserve", since = "1.57.0")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.base.try_reserve(additional).map_err(map_try_reserve_error) - } - - /// Shrinks the capacity of the map as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = HashMap::with_capacity(100); - /// map.insert(1, 2); - /// map.insert(3, 4); - /// assert!(map.capacity() >= 100); - /// map.shrink_to_fit(); - /// assert!(map.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.base.shrink_to_fit(); - } - - /// Shrinks the capacity of the map with a lower limit. It will drop - /// down no lower than the supplied limit while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// If the current capacity is less than the lower limit, this is a no-op. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap = HashMap::with_capacity(100); - /// map.insert(1, 2); - /// map.insert(3, 4); - /// assert!(map.capacity() >= 100); - /// map.shrink_to(10); - /// assert!(map.capacity() >= 10); - /// map.shrink_to(0); - /// assert!(map.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "shrink_to", since = "1.56.0")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.base.shrink_to(min_capacity); - } - - /// Gets the given key's corresponding entry in the map for in-place manipulation. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut letters = HashMap::new(); - /// - /// for ch in "a short treatise on fungi".chars() { - /// letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1); - /// } - /// - /// assert_eq!(letters[&'s'], 2); - /// assert_eq!(letters[&'t'], 3); - /// assert_eq!(letters[&'u'], 1); - /// assert_eq!(letters.get(&'y'), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V, A> { - map_entry(self.base.rustc_entry(key)) - } - - /// Returns a reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get(&1), Some(&"a")); - /// assert_eq!(map.get(&2), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get(&self, k: &Q) -> Option<&V> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get(k) - } - - /// Returns the key-value pair corresponding to the supplied key. This is - /// potentially useful: - /// - for key types where non-identical keys can be considered equal; - /// - for getting the `&K` stored key value from a borrowed `&Q` lookup key; or - /// - for getting a reference to a key with the same lifetime as the collection. - /// - /// The supplied key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::hash::{Hash, Hasher}; - /// - /// #[derive(Clone, Copy, Debug)] - /// struct S { - /// id: u32, - /// # #[allow(unused)] // prevents a "field `name` is never read" error - /// name: &'static str, // ignored by equality and hashing operations - /// } - /// - /// impl PartialEq for S { - /// fn eq(&self, other: &S) -> bool { - /// self.id == other.id - /// } - /// } - /// - /// impl Eq for S {} - /// - /// impl Hash for S { - /// fn hash(&self, state: &mut H) { - /// self.id.hash(state); - /// } - /// } - /// - /// let j_a = S { id: 1, name: "Jessica" }; - /// let j_b = S { id: 1, name: "Jess" }; - /// let p = S { id: 2, name: "Paul" }; - /// assert_eq!(j_a, j_b); - /// - /// let mut map = HashMap::new(); - /// map.insert(j_a, "Paris"); - /// assert_eq!(map.get_key_value(&j_a), Some((&j_a, &"Paris"))); - /// assert_eq!(map.get_key_value(&j_b), Some((&j_a, &"Paris"))); // the notable case - /// assert_eq!(map.get_key_value(&p), None); - /// ``` - #[inline] - #[stable(feature = "map_get_key_value", since = "1.40.0")] - pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get_key_value(k) - } - - /// Attempts to get mutable references to `N` values in the map at once. - /// - /// Returns an array of length `N` with the results of each query. For soundness, at most one - /// mutable reference will be returned to any value. `None` will be used if the key is missing. - /// - /// This method performs a check to ensure there are no duplicate keys, which currently has a time-complexity of O(n^2), - /// so be careful when passing many keys. - /// - /// # Panics - /// - /// Panics if any keys are overlapping. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut libraries = HashMap::new(); - /// libraries.insert("Bodleian Library".to_string(), 1602); - /// libraries.insert("Athenæum".to_string(), 1807); - /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); - /// libraries.insert("Library of Congress".to_string(), 1800); - /// - /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ - /// "Athenæum", - /// "Bodleian Library", - /// ]) else { panic!() }; - /// - /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_disjoint_mut([ - /// "Athenæum", - /// "Library of Congress", - /// ]); - /// assert_eq!( - /// got, - /// [ - /// Some(&mut 1807), - /// Some(&mut 1800), - /// ], - /// ); - /// - /// // Missing keys result in None - /// let got = libraries.get_disjoint_mut([ - /// "Athenæum", - /// "New York Public Library", - /// ]); - /// assert_eq!( - /// got, - /// [ - /// Some(&mut 1807), - /// None - /// ] - /// ); - /// ``` - /// - /// ```should_panic - /// use std::collections::HashMap; - /// - /// let mut libraries = HashMap::new(); - /// libraries.insert("Athenæum".to_string(), 1807); - /// - /// // Duplicate keys panic! - /// let got = libraries.get_disjoint_mut([ - /// "Athenæum", - /// "Athenæum", - /// ]); - /// ``` - #[inline] - #[doc(alias = "get_many_mut")] - #[stable(feature = "map_many_mut", since = "1.86.0")] - pub fn get_disjoint_mut( - &mut self, - ks: [&Q; N], - ) -> [Option<&'_ mut V>; N] - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get_disjoint_mut(ks) - } - - /// Attempts to get mutable references to `N` values in the map at once, without validating that - /// the values are unique. - /// - /// Returns an array of length `N` with the results of each query. `None` will be used if - /// the key is missing. - /// - /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). - /// - /// # Safety - /// - /// Calling this method with overlapping keys is *[undefined behavior]* even if the resulting - /// references are not used. - /// - /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut libraries = HashMap::new(); - /// libraries.insert("Bodleian Library".to_string(), 1602); - /// libraries.insert("Athenæum".to_string(), 1807); - /// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691); - /// libraries.insert("Library of Congress".to_string(), 1800); - /// - /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ - /// "Athenæum", - /// "Bodleian Library", - /// ]) }) else { panic!() }; - /// - /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ - /// "Athenæum", - /// "Library of Congress", - /// ]) }; - /// assert_eq!( - /// got, - /// [ - /// Some(&mut 1807), - /// Some(&mut 1800), - /// ], - /// ); - /// - /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ - /// "Athenæum", - /// "New York Public Library", - /// ]) }; - /// // Missing keys result in None - /// assert_eq!(got, [Some(&mut 1807), None]); - /// ``` - #[inline] - #[doc(alias = "get_many_unchecked_mut")] - #[stable(feature = "map_many_mut", since = "1.86.0")] - pub unsafe fn get_disjoint_unchecked_mut( - &mut self, - ks: [&Q; N], - ) -> [Option<&'_ mut V>; N] - where - K: Borrow, - Q: Hash + Eq, - { - unsafe { self.base.get_disjoint_unchecked_mut(ks) } - } - - /// Returns `true` if the map contains a value for the specified key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.contains_key(&1), true); - /// assert_eq!(map.contains_key(&2), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_contains_key")] - pub fn contains_key(&self, k: &Q) -> bool - where - K: Borrow, - Q: Hash + Eq, - { - self.base.contains_key(k) - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// if let Some(x) = map.get_mut(&1) { - /// *x = "b"; - /// } - /// assert_eq!(map[&1], "b"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.get_mut(k) - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not have this key present, [`None`] is returned. - /// - /// If the map did have this key present, the value is updated, and the old - /// value is returned. The key is not updated, though; this matters for - /// types that can be `==` without being identical. See the [module-level - /// documentation] for more. - /// - /// [module-level documentation]: crate::collections#insert-and-complex-keys - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// assert_eq!(map.insert(37, "a"), None); - /// assert_eq!(map.is_empty(), false); - /// - /// map.insert(37, "b"); - /// assert_eq!(map.insert(37, "c"), Some("b")); - /// assert_eq!(map[&37], "c"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("push", "append", "put")] - #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_insert")] - pub fn insert(&mut self, k: K, v: V) -> Option { - self.base.insert(k, v) - } - - /// Tries to insert a key-value pair into the map, and returns - /// a mutable reference to the value in the entry. - /// - /// If the map already had this key present, nothing is updated, and - /// an error containing the occupied entry and the value is returned. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(map_try_insert)] - /// - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a"); - /// - /// let err = map.try_insert(37, "b").unwrap_err(); - /// assert_eq!(err.entry.key(), &37); - /// assert_eq!(err.entry.get(), &"a"); - /// assert_eq!(err.value, "b"); - /// ``` - #[unstable(feature = "map_try_insert", issue = "82766")] - pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V, A>> { - match self.entry(key) { - Occupied(entry) => Err(OccupiedError { entry, value }), - Vacant(entry) => Ok(entry.insert(value)), - } - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove(&1), Some("a")); - /// assert_eq!(map.remove(&1), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("delete", "take")] - pub fn remove(&mut self, k: &Q) -> Option - where - K: Borrow, - Q: Hash + Eq, - { - self.base.remove(k) - } - - /// Removes a key from the map, returning the stored key and value if the - /// key was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the key type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// # fn main() { - /// let mut map = HashMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove_entry(&1), Some((1, "a"))); - /// assert_eq!(map.remove(&1), None); - /// # } - /// ``` - #[inline] - #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] - pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> - where - K: Borrow, - Q: Hash + Eq, - { - self.base.remove_entry(k) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashMap -where - K: Clone, - V: Clone, - S: Clone, - A: Allocator + Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self { base: self.base.clone() } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - self.base.clone_from(&source.base); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashMap -where - K: Eq + Hash, - V: PartialEq, - S: BuildHasher, - A: Allocator, -{ - fn eq(&self, other: &HashMap) -> bool { - if self.len() != other.len() { - return false; - } - - self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashMap -where - K: Eq + Hash, - V: Eq, - S: BuildHasher, - A: Allocator, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for HashMap -where - K: Debug, - V: Debug, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_default", issue = "143894")] -impl const Default for HashMap -where - S: [const] Default, -{ - /// Creates an empty `HashMap`, with the `Default` value for the hasher. - #[inline] - fn default() -> HashMap { - HashMap::with_hasher(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for HashMap -where - K: Eq + Hash + Borrow, - Q: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - type Output = V; - - /// Returns a reference to the value corresponding to the supplied key. - /// - /// # Panics - /// - /// Panics if the key is not present in the `HashMap`. - #[inline] - fn index(&self, key: &Q) -> &V { - self.get(key).expect("no entry found for key") - } -} - -#[stable(feature = "std_collections_from_array", since = "1.56.0")] -// Note: as what is currently the most convenient built-in way to construct -// a HashMap, a simple usage of this function must not *require* the user -// to provide a type annotation in order to infer the third type parameter -// (the hasher parameter, conventionally "S"). -// To that end, this impl is defined using RandomState as the concrete -// type of S, rather than being generic over `S: BuildHasher + Default`. -// It is expected that users who want to specify a hasher will manually use -// `with_capacity_and_hasher`. -// If type parameter defaults worked on impls, and if type parameter -// defaults could be mixed with const generics, then perhaps -// this could be generalized. -// See also the equivalent impl on HashSet. -impl From<[(K, V); N]> for HashMap -where - K: Eq + Hash, -{ - /// Converts a `[(K, V); N]` into a `HashMap`. - /// - /// If any entries in the array have equal keys, - /// all but one of the corresponding values will be dropped. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map1 = HashMap::from([(1, 2), (3, 4)]); - /// let map2: HashMap<_, _> = [(1, 2), (3, 4)].into(); - /// assert_eq!(map1, map2); - /// ``` - fn from(arr: [(K, V); N]) -> Self { - Self::from_iter(arr) - } -} - -/// An iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`iter`]: HashMap::iter -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter = map.iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_ty")] -pub struct Iter<'a, K: 'a, V: 'a> { - base: base::Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Iter { base: self.base.clone() } - } -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for Iter<'_, K, V> { - #[inline] - fn default() -> Self { - Iter { base: Default::default() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Iter<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: HashMap::iter_mut -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let mut map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter = map.iter_mut(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_mut_ty")] -pub struct IterMut<'a, K: 'a, V: 'a> { - base: base::IterMut<'a, K, V>, -} - -impl<'a, K, V> IterMut<'a, K, V> { - /// Returns an iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for IterMut<'_, K, V> { - #[inline] - fn default() -> Self { - IterMut { base: Default::default() } - } -} - -/// An owning iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`HashMap`] -/// (provided by the [`IntoIterator`] trait). See its documentation for more. -/// -/// [`into_iter`]: IntoIterator::into_iter -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter = map.into_iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter< - K, - V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::IntoIter, -} - -impl IntoIter { - /// Returns an iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for IntoIter { - #[inline] - fn default() -> Self { - IntoIter { base: Default::default() } - } -} - -/// An iterator over the keys of a `HashMap`. -/// -/// This `struct` is created by the [`keys`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`keys`]: HashMap::keys -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter_keys = map.keys(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_keys_ty")] -pub struct Keys<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Keys<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Keys { inner: self.inner.clone() } - } -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for Keys<'_, K, V> { - #[inline] - fn default() -> Self { - Keys { inner: Default::default() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Keys<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// An iterator over the values of a `HashMap`. -/// -/// This `struct` is created by the [`values`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`values`]: HashMap::values -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter_values = map.values(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_ty")] -pub struct Values<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Values<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Values { inner: self.inner.clone() } - } -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for Values<'_, K, V> { - #[inline] - fn default() -> Self { - Values { inner: Default::default() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Values<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A draining iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`drain`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`drain`]: HashMap::drain -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let mut map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter = map.drain(); -/// ``` -#[stable(feature = "drain", since = "1.6.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_drain_ty")] -pub struct Drain< - 'a, - K: 'a, - V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::Drain<'a, K, V, A>, -} - -impl<'a, K, V, A: Allocator> Drain<'a, K, V, A> { - /// Returns an iterator of references over the remaining items. - #[inline] - pub(super) fn iter(&self) -> Iter<'_, K, V> { - Iter { base: self.base.rustc_iter() } - } -} - -/// A draining, filtering iterator over the entries of a `HashMap`. -/// -/// This `struct` is created by the [`extract_if`] method on [`HashMap`]. -/// -/// [`extract_if`]: HashMap::extract_if -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let mut map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter = map.extract_if(|_k, v| *v % 2 == 0); -/// ``` -#[stable(feature = "hash_extract_if", since = "1.88.0")] -#[must_use = "iterators are lazy and do nothing unless consumed; \ - use `retain` to remove and discard elements"] -pub struct ExtractIf< - 'a, - K, - V, - F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::ExtractIf<'a, K, V, F, A>, -} - -/// A mutable iterator over the values of a `HashMap`. -/// -/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its -/// documentation for more. -/// -/// [`values_mut`]: HashMap::values_mut -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let mut map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter_values = map.values_mut(); -/// ``` -#[stable(feature = "map_values_mut", since = "1.10.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_mut_ty")] -pub struct ValuesMut<'a, K: 'a, V: 'a> { - inner: IterMut<'a, K, V>, -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for ValuesMut<'_, K, V> { - #[inline] - fn default() -> Self { - ValuesMut { inner: Default::default() } - } -} - -/// An owning iterator over the keys of a `HashMap`. -/// -/// This `struct` is created by the [`into_keys`] method on [`HashMap`]. -/// See its documentation for more. -/// -/// [`into_keys`]: HashMap::into_keys -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter_keys = map.into_keys(); -/// ``` -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoKeys< - K, - V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - inner: IntoIter, -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for IntoKeys { - #[inline] - fn default() -> Self { - IntoKeys { inner: Default::default() } - } -} - -/// An owning iterator over the values of a `HashMap`. -/// -/// This `struct` is created by the [`into_values`] method on [`HashMap`]. -/// See its documentation for more. -/// -/// [`into_values`]: HashMap::into_values -/// -/// # Example -/// -/// ``` -/// use std::collections::HashMap; -/// -/// let map = HashMap::from([ -/// ("a", 1), -/// ]); -/// let iter_keys = map.into_values(); -/// ``` -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoValues< - K, - V, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - inner: IntoIter, -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for IntoValues { - #[inline] - fn default() -> Self { - IntoValues { inner: Default::default() } - } -} - -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`HashMap`]. -/// -/// [`entry`]: HashMap::entry -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "HashMapEntry")] -pub enum Entry< - 'a, - K: 'a, - V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - /// An occupied entry. - #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V, A>), - - /// A vacant entry. - #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V, A>), -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into an occupied entry in a `HashMap`. -/// It is part of the [`Entry`] enum. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry< - 'a, - K: 'a, - V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::RustcOccupiedEntry<'a, K, V, A>, -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry") - .field("key", self.key()) - .field("value", self.get()) - .finish_non_exhaustive() - } -} - -/// A view into a vacant entry in a `HashMap`. -/// It is part of the [`Entry`] enum. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry< - 'a, - K: 'a, - V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::RustcVacantEntry<'a, K, V, A>, -} - -#[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() - } -} - -/// The error returned by [`try_insert`](HashMap::try_insert) when the key already exists. -/// -/// Contains the occupied entry, and the value that was not inserted. -#[unstable(feature = "map_try_insert", issue = "82766")] -pub struct OccupiedError< - 'a, - K: 'a, - V: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - /// The entry in the map that was already occupied. - pub entry: OccupiedEntry<'a, K, V, A>, - /// The value which was not inserted, because the entry was already occupied. - pub value: V, -} - -#[unstable(feature = "map_try_insert", issue = "82766")] -impl Debug for OccupiedError<'_, K, V, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedError") - .field("key", self.entry.key()) - .field("old_value", self.entry.get()) - .field("new_value", &self.value) - .finish_non_exhaustive() - } -} - -#[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug, A: Allocator> fmt::Display for OccupiedError<'a, K, V, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "failed to insert {:?}, key {:?} already exists with value {:?}", - self.value, - self.entry.key(), - self.entry.get(), - ) - } -} - -#[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug, A: Allocator> Error for OccupiedError<'a, K, V, A> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S, A: Allocator> IntoIterator for &'a HashMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; - - #[inline] - #[rustc_lint_query_instability] - fn into_iter(self) -> Iter<'a, K, V> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S, A: Allocator> IntoIterator for &'a mut HashMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; - - #[inline] - #[rustc_lint_query_instability] - fn into_iter(self) -> IterMut<'a, K, V> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashMap { - type Item = (K, V); - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each key-value - /// pair out of the map in arbitrary order. The map cannot be used after - /// calling this. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let map = HashMap::from([ - /// ("a", 1), - /// ("b", 2), - /// ("c", 3), - /// ]); - /// - /// // Not possible with .iter() - /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); - /// ``` - #[inline] - #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { - IntoIter { base: self.base.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn count(self) -> usize { - self.base.len() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn count(self) -> usize { - self.base.len() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, K, V> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IterMut<'_, K, V> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = (K, V); - - #[inline] - fn next(&mut self) -> Option<(K, V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn count(self) -> usize { - self.base.len() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - #[inline] - fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - #[inline] - fn count(self) -> usize { - self.inner.len() - } - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.inner.fold(init, |acc, (k, _)| f(acc, k)) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Keys<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Keys<'_, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - #[inline] - fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - #[inline] - fn count(self) -> usize { - self.inner.len() - } - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.inner.fold(init, |acc, (_, v)| f(acc, v)) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Values<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Values<'_, K, V> {} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - #[inline] - fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - #[inline] - fn count(self) -> usize { - self.inner.len() - } - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.inner.fold(init, |acc, (_, v)| f(acc, v)) - } -} -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl ExactSizeIterator for ValuesMut<'_, K, V> { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ValuesMut<'_, K, V> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for ValuesMut<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.inner.iter().map(|(_, val)| val)).finish() - } -} - -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoKeys { - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.inner.next().map(|(k, _)| k) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - #[inline] - fn count(self) -> usize { - self.inner.len() - } - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.inner.fold(init, |acc, (k, _)| f(acc, k)) - } -} -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoKeys { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoKeys {} - -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoKeys { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() - } -} - -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoValues { - type Item = V; - - #[inline] - fn next(&mut self) -> Option { - self.inner.next().map(|(_, v)| v) - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - #[inline] - fn count(self) -> usize { - self.inner.len() - } - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.inner.fold(init, |acc, (_, v)| f(acc, v)) - } -} -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoValues { - #[inline] - fn len(&self) -> usize { - self.inner.len() - } -} -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoValues {} - -#[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoValues { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, K, V, A: Allocator> Iterator for Drain<'a, K, V, A> { - type Item = (K, V); - - #[inline] - fn next(&mut self) -> Option<(K, V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, K, V, A> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K, V, A> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K, V, A> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, V, F, A> -where - F: FnMut(&K, &mut V) -> bool, -{ - type Item = (K, V); - - #[inline] - fn next(&mut self) -> Option<(K, V)> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, V, F, A> where - F: FnMut(&K, &mut V) -> bool -{ -} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, V, F, A> -where - K: fmt::Debug, - V: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ExtractIf").finish_non_exhaustive() - } -} - -impl<'a, K, V, A: Allocator> Entry<'a, K, V, A> { - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.entry("poneyland").or_insert(3); - /// assert_eq!(map["poneyland"], 3); - /// - /// *map.entry("poneyland").or_insert(10) *= 2; - /// assert_eq!(map["poneyland"], 6); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), - } - } - - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map = HashMap::new(); - /// let value = "hoho"; - /// - /// map.entry("poneyland").or_insert_with(|| value); - /// - /// assert_eq!(map["poneyland"], "hoho"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), - } - } - - /// Ensures a value is in the entry by inserting, if empty, the result of the default function. - /// This method allows for generating key-derived values for insertion by providing the default - /// function a reference to the key that was moved during the `.entry(key)` method call. - /// - /// The reference to the moved key is provided so that cloning or copying the key is - /// unnecessary, unlike with `.or_insert_with(|| ... )`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, usize> = HashMap::new(); - /// - /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); - /// - /// assert_eq!(map["poneyland"], 9); - /// ``` - #[inline] - #[stable(feature = "or_insert_with_key", since = "1.50.0")] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) - } - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[inline] - #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) - } - Vacant(entry) => Vacant(entry), - } - } - - /// Sets the value of the entry, and returns an `OccupiedEntry`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let entry = map.entry("poneyland").insert_entry("hoho".to_string()); - /// - /// assert_eq!(entry.key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { - match self { - Occupied(mut entry) => { - entry.insert(value); - entry - } - Vacant(entry) => entry.insert_entry(value), - } - } -} - -impl<'a, K, V: Default> Entry<'a, K, V> { - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, Option> = HashMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// # } - /// ``` - #[inline] - #[stable(feature = "entry_or_default", since = "1.28.0")] - pub fn or_default(self) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Default::default()), - } - } -} - -impl<'a, K, V, A: Allocator> OccupiedEntry<'a, K, V, A> { - /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.base.key() - } - - /// Take the ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` - #[inline] - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn remove_entry(self) -> (K, V) { - self.base.remove_entry() - } - - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> &V { - self.base.get() - } - - /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` which may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: Self::into_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// - /// assert_eq!(map["poneyland"], 24); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut V { - self.base.get_mut() - } - - /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry - /// with a lifetime bound to the map itself. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: Self::get_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// - /// assert_eq!(map["poneyland"], 22); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_mut(self) -> &'a mut V { - self.base.into_mut() - } - - /// Sets the value of the entry, and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// - /// assert_eq!(map["poneyland"], 15); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - self.base.insert(value) - } - - /// Takes the value out of the entry, and returns it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// - /// assert_eq!(map.contains_key("poneyland"), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(self) -> V { - self.base.remove() - } -} - -impl<'a, K: 'a, V: 'a, A: Allocator> VacantEntry<'a, K, V, A> { - /// Gets a reference to the key that would be used when inserting a value - /// through the `VacantEntry`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[inline] - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.base.key() - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` - #[inline] - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn into_key(self) -> K { - self.base.into_key() - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(self, value: V) -> &'a mut V { - self.base.insert(value) - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns an `OccupiedEntry`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashMap; - /// use std::collections::hash_map::Entry; - /// - /// let mut map: HashMap<&str, u32> = HashMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert_entry(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[inline] - #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { - let base = self.base.insert_entry(value); - OccupiedEntry { base } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher + Default, -{ - /// Constructs a `HashMap` from an iterator of key-value pairs. - /// - /// If the iterator produces any pairs with equal keys, - /// all but one of the corresponding values will be dropped. - fn from_iter>(iter: T) -> HashMap { - let mut map = HashMap::with_hasher(Default::default()); - map.extend(iter); - map - } -} - -/// Inserts all new key-values from the iterator and replaces values with existing -/// keys with new values returned from the iterator. -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for HashMap -where - K: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - #[inline] - fn extend>(&mut self, iter: T) { - self.base.extend(iter) - } - - #[inline] - fn extend_one(&mut self, (k, v): (K, V)) { - self.base.insert(k, v); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.base.extend_reserve(additional); - } -} - -#[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, K, V, S, A> Extend<(&'a K, &'a V)> for HashMap -where - K: Eq + Hash + Copy, - V: Copy, - S: BuildHasher, - A: Allocator, -{ - #[inline] - fn extend>(&mut self, iter: T) { - self.base.extend(iter) - } - - #[inline] - fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { - self.base.insert(k, v); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - Extend::<(K, V)>::extend_reserve(self, additional) - } -} - -#[inline] -fn map_entry<'a, K: 'a, V: 'a, A: Allocator>( - raw: base::RustcEntry<'a, K, V, A>, -) -> Entry<'a, K, V, A> { - match raw { - base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), - base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), - } -} - -#[inline] -pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError { - match err { - hashbrown::TryReserveError::CapacityOverflow => { - TryReserveErrorKind::CapacityOverflow.into() - } - hashbrown::TryReserveError::AllocError { layout } => { - TryReserveErrorKind::AllocError { layout, non_exhaustive: () }.into() - } - } -} - -#[allow(dead_code)] -fn assert_covariance() { - fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { - v - } - fn map_val<'new>(v: HashMap) -> HashMap { - v - } - fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { - v - } - fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { - v - } - fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { - v - } - fn into_iter_val<'new>(v: IntoIter) -> IntoIter { - v - } - fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { - v - } - fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { - v - } - fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { - v - } - fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { - v - } - fn drain<'new>( - d: Drain<'static, &'static str, &'static str>, - ) -> Drain<'new, &'new str, &'new str> { - d - } -} diff --git a/crates/std/src/collections/hash/map/tests.rs b/crates/std/src/collections/hash/map/tests.rs deleted file mode 100644 index fd7d81b..0000000 --- a/crates/std/src/collections/hash/map/tests.rs +++ /dev/null @@ -1,1059 +0,0 @@ -use rand::Rng; -use realstd::collections::TryReserveErrorKind::*; - -use super::Entry::{Occupied, Vacant}; -use super::HashMap; -use crate::assert_matches; -use crate::cell::RefCell; -use crate::hash::{BuildHasher, BuildHasherDefault, DefaultHasher, RandomState}; -use crate::test_helpers::test_rng; - -// https://github.com/rust-lang/rust/issues/62301 -fn _assert_hashmap_is_unwind_safe() { - fn assert_unwind_safe() {} - assert_unwind_safe::>>(); -} - -#[test] -fn test_zero_capacities() { - type HM = HashMap; - - let m = HM::new(); - assert_eq!(m.capacity(), 0); - - let m = HM::default(); - assert_eq!(m.capacity(), 0); - - let m = HM::with_hasher(RandomState::new()); - assert_eq!(m.capacity(), 0); - - let m = HM::with_capacity(0); - assert_eq!(m.capacity(), 0); - - let m = HM::with_capacity_and_hasher(0, RandomState::new()); - assert_eq!(m.capacity(), 0); - - let mut m = HM::new(); - m.insert(1, 1); - m.insert(2, 2); - m.remove(&1); - m.remove(&2); - m.shrink_to_fit(); - assert_eq!(m.capacity(), 0); - - let mut m = HM::new(); - m.reserve(0); - assert_eq!(m.capacity(), 0); -} - -#[test] -fn test_create_capacity_zero() { - let mut m = HashMap::with_capacity(0); - - assert!(m.insert(1, 1).is_none()); - - assert!(m.contains_key(&1)); - assert!(!m.contains_key(&0)); -} - -#[test] -fn test_insert() { - let mut m = HashMap::new(); - assert_eq!(m.len(), 0); - assert!(m.insert(1, 2).is_none()); - assert_eq!(m.len(), 1); - assert!(m.insert(2, 4).is_none()); - assert_eq!(m.len(), 2); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&2).unwrap(), 4); -} - -#[test] -fn test_clone() { - let mut m = HashMap::new(); - assert_eq!(m.len(), 0); - assert!(m.insert(1, 2).is_none()); - assert_eq!(m.len(), 1); - assert!(m.insert(2, 4).is_none()); - assert_eq!(m.len(), 2); - let m2 = m.clone(); - assert_eq!(*m2.get(&1).unwrap(), 2); - assert_eq!(*m2.get(&2).unwrap(), 4); - assert_eq!(m2.len(), 2); -} - -thread_local! { static DROP_VECTOR: RefCell> = RefCell::new(Vec::new()) } - -#[derive(Hash, PartialEq, Eq)] -struct Droppable { - k: usize, -} - -impl Droppable { - fn new(k: usize) -> Droppable { - DROP_VECTOR.with(|slot| { - slot.borrow_mut()[k] += 1; - }); - - Droppable { k } - } -} - -impl Drop for Droppable { - fn drop(&mut self) { - DROP_VECTOR.with(|slot| { - slot.borrow_mut()[self.k] -= 1; - }); - } -} - -impl Clone for Droppable { - fn clone(&self) -> Droppable { - Droppable::new(self.k) - } -} - -#[test] -fn test_drops() { - DROP_VECTOR.with(|slot| { - *slot.borrow_mut() = vec![0; 200]; - }); - - { - let mut m = HashMap::new(); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - - for i in 0..100 { - let d1 = Droppable::new(i); - let d2 = Droppable::new(i + 100); - m.insert(d1, d2); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - for i in 0..50 { - let k = Droppable::new(i); - let v = m.remove(&k); - - assert!(v.is_some()); - - DROP_VECTOR.with(|v| { - assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i + 100], 1); - }); - } - - DROP_VECTOR.with(|v| { - for i in 0..50 { - assert_eq!(v.borrow()[i], 0); - assert_eq!(v.borrow()[i + 100], 0); - } - - for i in 50..100 { - assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i + 100], 1); - } - }); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); -} - -#[test] -fn test_into_iter_drops() { - DROP_VECTOR.with(|v| { - *v.borrow_mut() = vec![0; 200]; - }); - - let hm = { - let mut hm = HashMap::new(); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); - - for i in 0..100 { - let d1 = Droppable::new(i); - let d2 = Droppable::new(i + 100); - hm.insert(d1, d2); - } - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - hm - }; - - // By the way, ensure that cloning doesn't screw up the dropping. - drop(hm.clone()); - - { - let mut half = hm.into_iter().take(50); - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 1); - } - }); - - for _ in half.by_ref() {} - - DROP_VECTOR.with(|v| { - let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count(); - - let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count(); - - assert_eq!(nk, 50); - assert_eq!(nv, 50); - }); - }; - - DROP_VECTOR.with(|v| { - for i in 0..200 { - assert_eq!(v.borrow()[i], 0); - } - }); -} - -#[test] -fn test_empty_remove() { - let mut m: HashMap = HashMap::new(); - assert_eq!(m.remove(&0), None); -} - -#[test] -fn test_empty_entry() { - let mut m: HashMap = HashMap::new(); - match m.entry(0) { - Occupied(_) => panic!(), - Vacant(_) => {} - } - assert!(*m.entry(0).or_insert(true)); - assert_eq!(m.len(), 1); -} - -#[test] -fn test_empty_iter() { - let mut m: HashMap = HashMap::new(); - assert_eq!(m.drain().next(), None); - assert_eq!(m.keys().next(), None); - assert_eq!(m.values().next(), None); - assert_eq!(m.values_mut().next(), None); - assert_eq!(m.iter().next(), None); - assert_eq!(m.iter_mut().next(), None); - assert_eq!(m.len(), 0); - assert!(m.is_empty()); - assert_eq!(m.into_iter().next(), None); -} - -#[test] -fn test_lots_of_insertions() { - let mut m = HashMap::new(); - - // Try this a few times to make sure we never screw up the hashmap's - // internal state. - let loops = if cfg!(miri) { 2 } else { 10 }; - for _ in 0..loops { - assert!(m.is_empty()); - - let count = if cfg!(miri) { 66 } else { 1001 }; - - for i in 1..count { - assert!(m.insert(i, i).is_none()); - - for j in 1..=i { - let r = m.get(&j); - assert_eq!(r, Some(&j)); - } - - for j in i + 1..count { - let r = m.get(&j); - assert_eq!(r, None); - } - } - - for i in count..(2 * count) { - assert!(!m.contains_key(&i)); - } - - // remove forwards - for i in 1..count { - assert!(m.remove(&i).is_some()); - - for j in 1..=i { - assert!(!m.contains_key(&j)); - } - - for j in i + 1..count { - assert!(m.contains_key(&j)); - } - } - - for i in 1..count { - assert!(!m.contains_key(&i)); - } - - for i in 1..count { - assert!(m.insert(i, i).is_none()); - } - - // remove backwards - for i in (1..count).rev() { - assert!(m.remove(&i).is_some()); - - for j in i..count { - assert!(!m.contains_key(&j)); - } - - for j in 1..i { - assert!(m.contains_key(&j)); - } - } - } -} - -#[test] -fn test_find_mut() { - let mut m = HashMap::new(); - assert!(m.insert(1, 12).is_none()); - assert!(m.insert(2, 8).is_none()); - assert!(m.insert(5, 14).is_none()); - let new = 100; - match m.get_mut(&5) { - None => panic!(), - Some(x) => *x = new, - } - assert_eq!(m.get(&5), Some(&new)); -} - -#[test] -fn test_insert_overwrite() { - let mut m = HashMap::new(); - assert!(m.insert(1, 2).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert!(!m.insert(1, 3).is_none()); - assert_eq!(*m.get(&1).unwrap(), 3); -} - -#[test] -fn test_insert_conflicts() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert!(m.insert(5, 3).is_none()); - assert!(m.insert(9, 4).is_none()); - assert_eq!(*m.get(&9).unwrap(), 4); - assert_eq!(*m.get(&5).unwrap(), 3); - assert_eq!(*m.get(&1).unwrap(), 2); -} - -#[test] -fn test_conflict_remove() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert!(m.insert(5, 3).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&5).unwrap(), 3); - assert!(m.insert(9, 4).is_none()); - assert_eq!(*m.get(&1).unwrap(), 2); - assert_eq!(*m.get(&5).unwrap(), 3); - assert_eq!(*m.get(&9).unwrap(), 4); - assert!(m.remove(&1).is_some()); - assert_eq!(*m.get(&9).unwrap(), 4); - assert_eq!(*m.get(&5).unwrap(), 3); -} - -#[test] -fn test_is_empty() { - let mut m = HashMap::with_capacity(4); - assert!(m.insert(1, 2).is_none()); - assert!(!m.is_empty()); - assert!(m.remove(&1).is_some()); - assert!(m.is_empty()); -} - -#[test] -fn test_remove() { - let mut m = HashMap::new(); - m.insert(1, 2); - assert_eq!(m.remove(&1), Some(2)); - assert_eq!(m.remove(&1), None); -} - -#[test] -fn test_remove_entry() { - let mut m = HashMap::new(); - m.insert(1, 2); - assert_eq!(m.remove_entry(&1), Some((1, 2))); - assert_eq!(m.remove(&1), None); -} - -#[test] -fn test_iterate() { - let mut m = HashMap::with_capacity(4); - for i in 0..32 { - assert!(m.insert(i, i * 2).is_none()); - } - assert_eq!(m.len(), 32); - - let mut observed: u32 = 0; - - for (k, v) in &m { - assert_eq!(*v, *k * 2); - observed |= 1 << *k; - } - assert_eq!(observed, 0xFFFF_FFFF); -} - -#[test] -fn test_keys() { - let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = pairs.into_iter().collect(); - let keys: Vec<_> = map.keys().cloned().collect(); - assert_eq!(keys.len(), 3); - assert!(keys.contains(&1)); - assert!(keys.contains(&2)); - assert!(keys.contains(&3)); -} - -#[test] -fn test_values() { - let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = pairs.into_iter().collect(); - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&'a')); - assert!(values.contains(&'b')); - assert!(values.contains(&'c')); -} - -#[test] -fn test_values_mut() { - let pairs = [(1, 1), (2, 2), (3, 3)]; - let mut map: HashMap<_, _> = pairs.into_iter().collect(); - for value in map.values_mut() { - *value = (*value) * 2 - } - let values: Vec<_> = map.values().cloned().collect(); - assert_eq!(values.len(), 3); - assert!(values.contains(&2)); - assert!(values.contains(&4)); - assert!(values.contains(&6)); -} - -#[test] -fn test_into_keys() { - let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = pairs.into_iter().collect(); - let keys: Vec<_> = map.into_keys().collect(); - - assert_eq!(keys.len(), 3); - assert!(keys.contains(&1)); - assert!(keys.contains(&2)); - assert!(keys.contains(&3)); -} - -#[test] -fn test_into_values() { - let pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; - let map: HashMap<_, _> = pairs.into_iter().collect(); - let values: Vec<_> = map.into_values().collect(); - - assert_eq!(values.len(), 3); - assert!(values.contains(&'a')); - assert!(values.contains(&'b')); - assert!(values.contains(&'c')); -} - -#[test] -fn test_find() { - let mut m = HashMap::new(); - assert!(m.get(&1).is_none()); - m.insert(1, 2); - match m.get(&1) { - None => panic!(), - Some(v) => assert_eq!(*v, 2), - } -} - -#[test] -fn test_eq() { - let mut m1 = HashMap::new(); - m1.insert(1, 2); - m1.insert(2, 3); - m1.insert(3, 4); - - let mut m2 = HashMap::new(); - m2.insert(1, 2); - m2.insert(2, 3); - - assert!(m1 != m2); - - m2.insert(3, 4); - - assert_eq!(m1, m2); -} - -#[test] -fn test_show() { - let mut map = HashMap::new(); - let empty: HashMap = HashMap::new(); - - map.insert(1, 2); - map.insert(3, 4); - - let map_str = format!("{map:?}"); - - assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}"); - assert_eq!(format!("{empty:?}"), "{}"); -} - -#[test] -fn test_reserve_shrink_to_fit() { - let mut m = HashMap::new(); - m.insert(0, 0); - m.remove(&0); - assert!(m.capacity() >= m.len()); - for i in 0..128 { - m.insert(i, i); - } - m.reserve(256); - - let usable_cap = m.capacity(); - for i in 128..(128 + 256) { - m.insert(i, i); - assert_eq!(m.capacity(), usable_cap); - } - - for i in 100..(128 + 256) { - assert_eq!(m.remove(&i), Some(i)); - } - m.shrink_to_fit(); - - assert_eq!(m.len(), 100); - assert!(!m.is_empty()); - assert!(m.capacity() >= m.len()); - - for i in 0..100 { - assert_eq!(m.remove(&i), Some(i)); - } - m.shrink_to_fit(); - m.insert(0, 0); - - assert_eq!(m.len(), 1); - assert!(m.capacity() >= m.len()); - assert_eq!(m.remove(&0), Some(0)); -} - -#[test] -fn test_from_iter() { - let xs = [(1, 1), (2, 2), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - for &(k, v) in &xs { - assert_eq!(map.get(&k), Some(&v)); - } - - assert_eq!(map.iter().len(), xs.len() - 1); -} - -#[test] -fn test_size_hint() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.size_hint(), (3, Some(3))); -} - -#[test] -fn test_iter_len() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.len(), 3); -} - -#[test] -fn test_mut_size_hint() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter_mut(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.size_hint(), (3, Some(3))); -} - -#[test] -fn test_iter_mut_len() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - let mut iter = map.iter_mut(); - - for _ in iter.by_ref().take(3) {} - - assert_eq!(iter.len(), 3); -} - -#[test] -fn test_index() { - let mut map = HashMap::new(); - - map.insert(1, 2); - map.insert(2, 1); - map.insert(3, 4); - - assert_eq!(map[&2], 1); -} - -#[test] -#[should_panic] -fn test_index_nonexistent() { - let mut map = HashMap::new(); - - map.insert(1, 2); - map.insert(2, 1); - map.insert(3, 4); - - map[&4]; -} - -#[test] -fn test_entry() { - let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; - - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); - - // Existing key (insert) - match map.entry(1) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - assert_eq!(view.get(), &10); - assert_eq!(view.insert(100), 10); - } - } - assert_eq!(map.get(&1).unwrap(), &100); - assert_eq!(map.len(), 6); - - // Existing key (update) - match map.entry(2) { - Vacant(_) => unreachable!(), - Occupied(mut view) => { - let v = view.get_mut(); - let new_v = (*v) * 10; - *v = new_v; - } - } - assert_eq!(map.get(&2).unwrap(), &200); - assert_eq!(map.len(), 6); - - // Existing key (take) - match map.entry(3) { - Vacant(_) => unreachable!(), - Occupied(view) => { - assert_eq!(view.remove(), 30); - } - } - assert_eq!(map.get(&3), None); - assert_eq!(map.len(), 5); - - // Inexistent key (insert) - match map.entry(10) { - Occupied(_) => unreachable!(), - Vacant(view) => { - assert_eq!(*view.insert(1000), 1000); - } - } - assert_eq!(map.get(&10).unwrap(), &1000); - assert_eq!(map.len(), 6); -} - -#[test] -fn test_entry_take_doesnt_corrupt() { - #![allow(deprecated)] //rand - // Test for #19292 - fn check(m: &HashMap) { - for k in m.keys() { - assert!(m.contains_key(k), "{k} is in keys() but not in the map?"); - } - } - - let mut m = HashMap::new(); - let mut rng = test_rng(); - - // Populate the map with some items. - for _ in 0..50 { - let x = rng.gen_range(-10..10); - m.insert(x, ()); - } - - for _ in 0..1000 { - let x = rng.gen_range(-10..10); - match m.entry(x) { - Vacant(_) => {} - Occupied(e) => { - e.remove(); - } - } - - check(&m); - } -} - -#[test] -fn test_extend_ref() { - let mut a = HashMap::new(); - a.insert(1, "one"); - let mut b = HashMap::new(); - b.insert(2, "two"); - b.insert(3, "three"); - - a.extend(&b); - - assert_eq!(a.len(), 3); - assert_eq!(a[&1], "one"); - assert_eq!(a[&2], "two"); - assert_eq!(a[&3], "three"); -} - -#[test] -fn test_capacity_not_less_than_len() { - let mut a = HashMap::new(); - let mut item = 0; - - for _ in 0..116 { - a.insert(item, 0); - item += 1; - } - - assert!(a.capacity() > a.len()); - - let free = a.capacity() - a.len(); - for _ in 0..free { - a.insert(item, 0); - item += 1; - } - - assert_eq!(a.len(), a.capacity()); - - // Insert at capacity should cause allocation. - a.insert(item, 0); - assert!(a.capacity() > a.len()); -} - -#[test] -fn test_occupied_entry_key() { - let mut a = HashMap::new(); - let key = "hello there"; - let value = "value goes here"; - assert!(a.is_empty()); - a.insert(key, value); - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); - - match a.entry(key) { - Vacant(_) => panic!(), - Occupied(e) => assert_eq!(key, *e.key()), - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); -} - -#[test] -fn test_vacant_entry_key() { - let mut a = HashMap::new(); - let key = "hello there"; - let value = "value goes here"; - - assert!(a.is_empty()); - match a.entry(key) { - Occupied(_) => panic!(), - Vacant(e) => { - assert_eq!(key, *e.key()); - e.insert(value); - } - } - assert_eq!(a.len(), 1); - assert_eq!(a[key], value); -} - -#[test] -fn test_retain() { - let mut map: HashMap = (0..100).map(|x| (x, x * 10)).collect(); - - map.retain(|&k, _| k % 2 == 0); - assert_eq!(map.len(), 50); - assert_eq!(map[&2], 20); - assert_eq!(map[&4], 40); - assert_eq!(map[&6], 60); -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc -fn test_try_reserve() { - let mut empty_bytes: HashMap = HashMap::new(); - - const MAX_USIZE: usize = usize::MAX; - - assert_matches!( - empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()), - Err(CapacityOverflow), - "usize::MAX should trigger an overflow!" - ); - - if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 16).map_err(|e| e.kind()) { - } else { - // This may succeed if there is enough free memory. Attempt to - // allocate a few more hashmaps to ensure the allocation will fail. - let mut empty_bytes2: HashMap = HashMap::new(); - let _ = empty_bytes2.try_reserve(MAX_USIZE / 16); - let mut empty_bytes3: HashMap = HashMap::new(); - let _ = empty_bytes3.try_reserve(MAX_USIZE / 16); - let mut empty_bytes4: HashMap = HashMap::new(); - assert_matches!( - empty_bytes4.try_reserve(MAX_USIZE / 16).map_err(|e| e.kind()), - Err(AllocError { .. }), - "usize::MAX / 16 should trigger an OOM!" - ); - } -} - -mod test_extract_if { - use super::*; - use crate::panic::{AssertUnwindSafe, catch_unwind}; - use crate::sync::atomic::{AtomicUsize, Ordering}; - - trait EqSorted: Iterator { - fn eq_sorted>(self, other: I) -> bool; - } - - impl EqSorted for T - where - T::Item: Eq + Ord, - { - fn eq_sorted>(self, other: I) -> bool { - let mut v: Vec<_> = self.collect(); - v.sort_unstable(); - v.into_iter().eq(other) - } - } - - #[test] - fn empty() { - let mut map: HashMap = HashMap::new(); - map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); - assert!(map.is_empty()); - } - - #[test] - fn consuming_nothing() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: HashMap<_, _> = pairs.collect(); - assert!(map.extract_if(|_, _| false).eq_sorted(crate::iter::empty())); - assert_eq!(map.len(), 3); - } - - #[test] - fn consuming_all() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: HashMap<_, _> = pairs.clone().collect(); - assert!(map.extract_if(|_, _| true).eq_sorted(pairs)); - assert!(map.is_empty()); - } - - #[test] - fn mutating_and_keeping() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: HashMap<_, _> = pairs.collect(); - assert!( - map.extract_if(|_, v| { - *v += 6; - false - }) - .eq_sorted(crate::iter::empty()) - ); - assert!(map.keys().copied().eq_sorted(0..3)); - assert!(map.values().copied().eq_sorted(6..9)); - } - - #[test] - fn mutating_and_removing() { - let pairs = (0..3).map(|i| (i, i)); - let mut map: HashMap<_, _> = pairs.collect(); - assert!( - map.extract_if(|_, v| { - *v += 6; - true - }) - .eq_sorted((0..3).map(|i| (i, i + 6))) - ); - assert!(map.is_empty()); - } - - #[test] - #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] - fn drop_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - - let mut map = (0..3).map(|i| (i, D)).collect::>(); - - catch_unwind(move || { - map.extract_if(|_, _| { - PREDS.fetch_add(1, Ordering::SeqCst); - true - }) - .for_each(drop) - }) - .unwrap_err(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 2); - assert_eq!(DROPS.load(Ordering::SeqCst), 3); - } - - #[test] - #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] - fn pred_panic_leak() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - } - } - - let mut map = (0..3).map(|i| (i, D)).collect::>(); - - catch_unwind(AssertUnwindSafe(|| { - map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { - 0 => true, - _ => panic!(), - }) - .for_each(drop) - })) - .unwrap_err(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 2); - assert_eq!(DROPS.load(Ordering::SeqCst), 1); - assert_eq!(map.len(), 2); - } - - // Same as above, but attempt to use the iterator again after the panic in the predicate - #[test] - #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] - fn pred_panic_reuse() { - static PREDS: AtomicUsize = AtomicUsize::new(0); - static DROPS: AtomicUsize = AtomicUsize::new(0); - - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - } - } - - let mut map = (0..3).map(|i| (i, D)).collect::>(); - - { - let mut it = map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { - 0 => true, - _ => panic!(), - }); - catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); - // Iterator behavior after a panic is explicitly unspecified, - // so this is just the current implementation: - let result = catch_unwind(AssertUnwindSafe(|| it.next())); - assert!(result.is_err()); - } - - assert_eq!(PREDS.load(Ordering::SeqCst), 3); - assert_eq!(DROPS.load(Ordering::SeqCst), 1); - assert_eq!(map.len(), 2); - } -} - -#[test] -fn from_array() { - let map = HashMap::from([(1, 2), (3, 4)]); - let unordered_duplicates = HashMap::from([(3, 4), (1, 2), (1, 2)]); - assert_eq!(map, unordered_duplicates); - - // This next line must infer the hasher type parameter. - // If you make a change that causes this line to no longer infer, - // that's a problem! - let _must_not_require_type_annotation = HashMap::from([(1, 2)]); -} - -#[test] -fn const_with_hasher() { - const X: HashMap<(), (), BuildHasherDefault> = - HashMap::with_hasher(BuildHasherDefault::new()); - let mut x = X; - assert_eq!(x.len(), 0); - x.insert((), ()); - assert_eq!(x.len(), 1); - - // It *is* possible to do this without using the `BuildHasherDefault` type. - struct MyBuildDefaultHasher; - impl BuildHasher for MyBuildDefaultHasher { - type Hasher = DefaultHasher; - - fn build_hasher(&self) -> Self::Hasher { - DefaultHasher::new() - } - } - - const Y: HashMap<(), (), MyBuildDefaultHasher> = HashMap::with_hasher(MyBuildDefaultHasher); - let mut y = Y; - assert_eq!(y.len(), 0); - y.insert((), ()); - assert_eq!(y.len(), 1); - - const Z: HashMap<(), (), BuildHasherDefault> = Default::default(); - assert_eq!(X, Z); -} diff --git a/crates/std/src/collections/hash/mod.rs b/crates/std/src/collections/hash/mod.rs deleted file mode 100644 index 348820a..0000000 --- a/crates/std/src/collections/hash/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Unordered containers, implemented as hash-tables - -pub mod map; -pub mod set; diff --git a/crates/std/src/collections/hash/set.rs b/crates/std/src/collections/hash/set.rs deleted file mode 100644 index 7cecafe..0000000 --- a/crates/std/src/collections/hash/set.rs +++ /dev/null @@ -1,2519 +0,0 @@ -#[cfg(test)] -mod tests; - -use hashbrown::hash_set as base; - -use super::map::map_try_reserve_error; -use crate::alloc::{Allocator, Global}; -use crate::borrow::Borrow; -use crate::collections::TryReserveError; -use crate::fmt; -use crate::hash::{BuildHasher, Hash, RandomState}; -use crate::iter::{Chain, FusedIterator}; -use crate::ops::{BitAnd, BitOr, BitXor, Sub}; - -/// A [hash set] implemented as a `HashMap` where the value is `()`. -/// -/// As with the [`HashMap`] type, a `HashSet` requires that the elements -/// implement the [`Eq`] and [`Hash`] traits. This can frequently be achieved by -/// using `#[derive(PartialEq, Eq, Hash)]`. If you implement these yourself, -/// it is important that the following property holds: -/// -/// ```text -/// k1 == k2 -> hash(k1) == hash(k2) -/// ``` -/// -/// In other words, if two keys are equal, their hashes must be equal. -/// Violating this property is a logic error. -/// -/// It is also a logic error for a key to be modified in such a way that the key's -/// hash, as determined by the [`Hash`] trait, or its equality, as determined by -/// the [`Eq`] trait, changes while it is in the map. This is normally only -/// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// The behavior resulting from either logic error is not specified, but will -/// be encapsulated to the `HashSet` that observed the logic error and not -/// result in undefined behavior. This could include panics, incorrect results, -/// aborts, memory leaks, and non-termination. -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// // Type inference lets us omit an explicit type signature (which -/// // would be `HashSet` in this example). -/// let mut books = HashSet::new(); -/// -/// // Add some books. -/// books.insert("A Dance With Dragons".to_string()); -/// books.insert("To Kill a Mockingbird".to_string()); -/// books.insert("The Odyssey".to_string()); -/// books.insert("The Great Gatsby".to_string()); -/// -/// // Check for a specific one. -/// if !books.contains("The Winds of Winter") { -/// println!("We have {} books, but The Winds of Winter ain't one.", -/// books.len()); -/// } -/// -/// // Remove a book. -/// books.remove("The Odyssey"); -/// -/// // Iterate over everything. -/// for book in &books { -/// println!("{book}"); -/// } -/// ``` -/// -/// The easiest way to use `HashSet` with a custom type is to derive -/// [`Eq`] and [`Hash`]. We must also derive [`PartialEq`], -/// which is required if [`Eq`] is derived. -/// -/// ``` -/// use std::collections::HashSet; -/// #[derive(Hash, Eq, PartialEq, Debug)] -/// struct Viking { -/// name: String, -/// power: usize, -/// } -/// -/// let mut vikings = HashSet::new(); -/// -/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); -/// vikings.insert(Viking { name: "Einar".to_string(), power: 9 }); -/// vikings.insert(Viking { name: "Olaf".to_string(), power: 4 }); -/// vikings.insert(Viking { name: "Harald".to_string(), power: 8 }); -/// -/// // Use derived implementation to print the vikings. -/// for x in &vikings { -/// println!("{x:?}"); -/// } -/// ``` -/// -/// A `HashSet` with a known list of items can be initialized from an array: -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let viking_names = HashSet::from(["Einar", "Olaf", "Harald"]); -/// ``` -/// -/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when -/// [`HashMap`]: crate::collections::HashMap -/// [`RefCell`]: crate::cell::RefCell -/// [`Cell`]: crate::cell::Cell -/// -/// # Usage in `const` and `static` -/// -/// Like `HashMap`, `HashSet` is randomly seeded: each `HashSet` instance uses a different seed, -/// which means that `HashSet::new` cannot be used in const context. To construct a `HashSet` in the -/// initializer of a `const` or `static` item, you will have to use a different hasher that does not -/// involve a random seed, as demonstrated in the following example. **A `HashSet` constructed this -/// way is not resistant against HashDoS!** -/// -/// ```rust -/// use std::collections::HashSet; -/// use std::hash::{BuildHasherDefault, DefaultHasher}; -/// use std::sync::Mutex; -/// -/// const EMPTY_SET: HashSet> = -/// HashSet::with_hasher(BuildHasherDefault::new()); -/// static SET: Mutex>> = -/// Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); -/// ``` -#[cfg_attr(not(test), rustc_diagnostic_item = "HashSet")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct HashSet< - T, - S = RandomState, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::HashSet, -} - -impl HashSet { - /// Creates an empty `HashSet`. - /// - /// The hash set is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::new(); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> HashSet { - Default::default() - } - - /// Creates an empty `HashSet` with at least the specified capacity. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> HashSet { - HashSet::with_capacity_and_hasher(capacity, Default::default()) - } -} - -impl HashSet { - /// Creates an empty `HashSet` in the provided allocator. - /// - /// The hash set is initially created with a capacity of 0, so it will not allocate until it - /// is first inserted into. - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn new_in(alloc: A) -> HashSet { - HashSet::with_hasher_in(Default::default(), alloc) - } - - /// Creates an empty `HashSet` with at least the specified capacity. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_capacity_in(capacity: usize, alloc: A) -> HashSet { - HashSet::with_capacity_and_hasher_in(capacity, Default::default(), alloc) - } -} - -impl HashSet { - /// Creates a new empty hash set which will use the given hasher to hash - /// keys. - /// - /// The hash set is also created with the default initial capacity. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_hasher(s); - /// set.insert(2); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_unstable(feature = "custom_std", issue = "none")] - pub const fn with_hasher(hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_hasher(hasher) } - } - - /// Creates an empty `HashSet` with at least the specified capacity, using - /// `hasher` to hash the keys. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_capacity_and_hasher(10, s); - /// set.insert(1); - /// ``` - #[inline] - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } - } -} - -impl HashSet { - /// Creates a new empty hash set which will use the given hasher to hash - /// keys and will allocate memory using the provided allocator. - /// - /// The hash set is also created with the default initial capacity. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { - HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } - } - - /// Creates an empty `HashSet` with at least the specified capacity, using - /// `hasher` to hash the keys and `alloc` to allocate memory. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - #[inline] - #[must_use] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet { - HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) } - } - - /// Returns the number of elements the set can hold without reallocating. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(100); - /// assert!(set.capacity() >= 100); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.base.capacity() - } - - /// An iterator visiting all elements in arbitrary order. - /// The iterator element type is `&'a T`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set = HashSet::new(); - /// set.insert("a"); - /// set.insert("b"); - /// - /// // Will print in an arbitrary order. - /// for x in set.iter() { - /// println!("{x}"); - /// } - /// ``` - /// - /// # Performance - /// - /// In the current implementation, iterating over set takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { base: self.base.iter() } - } - - /// Returns the number of elements in the set. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// assert_eq!(v.len(), 0); - /// v.insert(1); - /// assert_eq!(v.len(), 1); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.base.len() - } - - /// Returns `true` if the set contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// assert!(v.is_empty()); - /// v.insert(1); - /// assert!(!v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.base.is_empty() - } - - /// Clears the set, returning all elements as an iterator. Keeps the - /// allocated memory for reuse. - /// - /// If the returned iterator is dropped before being fully consumed, it - /// drops the remaining elements. The returned iterator keeps a mutable - /// borrow on the set to optimize its implementation. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::from([1, 2, 3]); - /// assert!(!set.is_empty()); - /// - /// // print 1, 2, 3 in an arbitrary order - /// for i in set.drain() { - /// println!("{i}"); - /// } - /// - /// assert!(set.is_empty()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T, A> { - Drain { base: self.base.drain() } - } - - /// Creates an iterator which uses a closure to determine if an element should be removed. - /// - /// If the closure returns `true`, the element is removed from the set and - /// yielded. If the closure returns `false`, or panics, the element remains - /// in the set and will not be yielded. - /// - /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating - /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain`] with a negated predicate if you do not need the returned iterator. - /// - /// [`retain`]: HashSet::retain - /// - /// # Examples - /// - /// Splitting a set into even and odd values, reusing the original set: - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = (0..8).collect(); - /// let extracted: HashSet = set.extract_if(|v| v % 2 == 0).collect(); - /// - /// let mut evens = extracted.into_iter().collect::>(); - /// let mut odds = set.into_iter().collect::>(); - /// evens.sort(); - /// odds.sort(); - /// - /// assert_eq!(evens, vec![0, 2, 4, 6]); - /// assert_eq!(odds, vec![1, 3, 5, 7]); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F, A> - where - F: FnMut(&T) -> bool, - { - ExtractIf { base: self.base.extract_if(pred) } - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` for which `f(&e)` returns `false`. - /// The elements are visited in unsorted (and unspecified) order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::from([1, 2, 3, 4, 5, 6]); - /// set.retain(|&k| k % 2 == 0); - /// assert_eq!(set, HashSet::from([2, 4, 6])); - /// ``` - /// - /// # Performance - /// - /// In the current implementation, this operation takes O(capacity) time - /// instead of O(len) because it internally visits empty buckets too. - #[rustc_lint_query_instability] - #[stable(feature = "retain_hash_collection", since = "1.18.0")] - pub fn retain(&mut self, f: F) - where - F: FnMut(&T) -> bool, - { - self.base.retain(f) - } - - /// Clears the set, removing all values. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut v = HashSet::new(); - /// v.insert(1); - /// v.clear(); - /// assert!(v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.base.clear() - } - - /// Returns a reference to the set's [`BuildHasher`]. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let hasher = RandomState::new(); - /// let set: HashSet = HashSet::with_hasher(hasher); - /// let hasher: &RandomState = set.hasher(); - /// ``` - #[inline] - #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] - pub fn hasher(&self) -> &S { - self.base.hasher() - } -} - -impl HashSet -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the `HashSet`. The collection may reserve more space to speculatively - /// avoid frequent reallocations. After calling `reserve`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if capacity is already sufficient. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows `usize`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set: HashSet = HashSet::new(); - /// set.reserve(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.base.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the `HashSet`. The collection may reserve more space to speculatively - /// avoid frequent reallocations. After calling `try_reserve`, - /// capacity will be greater than or equal to `self.len() + additional` if - /// it returns `Ok(())`. - /// Does nothing if capacity is already sufficient. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set: HashSet = HashSet::new(); - /// set.try_reserve(10).expect("why is the test harness OOMing on a handful of bytes?"); - /// ``` - #[inline] - #[stable(feature = "try_reserve", since = "1.57.0")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.base.try_reserve(additional).map_err(map_try_reserve_error) - } - - /// Shrinks the capacity of the set as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::with_capacity(100); - /// set.insert(1); - /// set.insert(2); - /// assert!(set.capacity() >= 100); - /// set.shrink_to_fit(); - /// assert!(set.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.base.shrink_to_fit() - } - - /// Shrinks the capacity of the set with a lower limit. It will drop - /// down no lower than the supplied limit while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - /// - /// If the current capacity is less than the lower limit, this is a no-op. - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::with_capacity(100); - /// set.insert(1); - /// set.insert(2); - /// assert!(set.capacity() >= 100); - /// set.shrink_to(10); - /// assert!(set.capacity() >= 10); - /// set.shrink_to(0); - /// assert!(set.capacity() >= 2); - /// ``` - #[inline] - #[stable(feature = "shrink_to", since = "1.56.0")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.base.shrink_to(min_capacity) - } - - /// Visits the values representing the difference, - /// i.e., the values that are in `self` but not in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); - /// - /// // Can be seen as `a - b`. - /// for x in a.difference(&b) { - /// println!("{x}"); // Print 1 - /// } - /// - /// let diff: HashSet<_> = a.difference(&b).collect(); - /// assert_eq!(diff, [1].iter().collect()); - /// - /// // Note that difference is not symmetric, - /// // and `b - a` means something else: - /// let diff: HashSet<_> = b.difference(&a).collect(); - /// assert_eq!(diff, [4].iter().collect()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S, A> { - Difference { iter: self.iter(), other } - } - - /// Visits the values representing the symmetric difference, - /// i.e., the values that are in `self` or in `other` but not in both. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); - /// - /// // Print 1, 4 in arbitrary order. - /// for x in a.symmetric_difference(&b) { - /// println!("{x}"); - /// } - /// - /// let diff1: HashSet<_> = a.symmetric_difference(&b).collect(); - /// let diff2: HashSet<_> = b.symmetric_difference(&a).collect(); - /// - /// assert_eq!(diff1, diff2); - /// assert_eq!(diff1, [1, 4].iter().collect()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>( - &'a self, - other: &'a HashSet, - ) -> SymmetricDifference<'a, T, S, A> { - SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } - } - - /// Visits the values representing the intersection, - /// i.e., the values that are both in `self` and `other`. - /// - /// When an equal element is present in `self` and `other` - /// then the resulting `Intersection` may yield references to - /// one or the other. This can be relevant if `T` contains fields which - /// are not compared by its `Eq` implementation, and may hold different - /// value between the two equal copies of `T` in the two sets. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); - /// - /// // Print 2, 3 in arbitrary order. - /// for x in a.intersection(&b) { - /// println!("{x}"); - /// } - /// - /// let intersection: HashSet<_> = a.intersection(&b).collect(); - /// assert_eq!(intersection, [2, 3].iter().collect()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S, A> { - if self.len() <= other.len() { - Intersection { iter: self.iter(), other } - } else { - Intersection { iter: other.iter(), other: self } - } - } - - /// Visits the values representing the union, - /// i.e., all the values in `self` or `other`, without duplicates. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order. - /// for x in a.union(&b) { - /// println!("{x}"); - /// } - /// - /// let union: HashSet<_> = a.union(&b).collect(); - /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); - /// ``` - #[inline] - #[rustc_lint_query_instability] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S, A> { - if self.len() >= other.len() { - Union { iter: self.iter().chain(other.difference(self)) } - } else { - Union { iter: other.iter().chain(self.difference(other)) } - } - } - - /// Returns `true` if the set contains a value. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let set = HashSet::from([1, 2, 3]); - /// assert_eq!(set.contains(&1), true); - /// assert_eq!(set.contains(&4), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { - self.base.contains(value) - } - - /// Returns a reference to the value in the set, if any, that is equal to the given value. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let set = HashSet::from([1, 2, 3]); - /// assert_eq!(set.get(&2), Some(&2)); - /// assert_eq!(set.get(&4), None); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn get(&self, value: &Q) -> Option<&T> - where - T: Borrow, - Q: Hash + Eq, - { - self.base.get(value) - } - - /// Inserts the given `value` into the set if it is not present, then - /// returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::from([1, 2, 3]); - /// assert_eq!(set.len(), 3); - /// assert_eq!(set.get_or_insert(2), &2); - /// assert_eq!(set.get_or_insert(100), &100); - /// assert_eq!(set.len(), 4); // 100 was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert(&mut self, value: T) -> &T { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.base.get_or_insert(value) - } - - /// Inserts a value computed from `f` into the set if the given `value` is - /// not present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set: HashSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_with(pet, str::to_owned); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T - where - T: Borrow, - Q: Hash + Eq, - F: FnOnce(&Q) -> T, - { - // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with - // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.base.get_or_insert_with(value, f) - } - - /// Gets the given value's corresponding entry in the set for in-place manipulation. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// use std::collections::hash_set::Entry::*; - /// - /// let mut singles = HashSet::new(); - /// let mut dupes = HashSet::new(); - /// - /// for ch in "a short treatise on fungi".chars() { - /// if let Vacant(dupe_entry) = dupes.entry(ch) { - /// // We haven't already seen a duplicate, so - /// // check if we've at least seen it once. - /// match singles.entry(ch) { - /// Vacant(single_entry) => { - /// // We found a new character for the first time. - /// single_entry.insert() - /// } - /// Occupied(single_entry) => { - /// // We've already seen this once, "move" it to dupes. - /// single_entry.remove(); - /// dupe_entry.insert(); - /// } - /// } - /// } - /// } - /// - /// assert!(!singles.contains(&'t') && dupes.contains(&'t')); - /// assert!(singles.contains(&'u') && !dupes.contains(&'u')); - /// assert!(!singles.contains(&'v') && !dupes.contains(&'v')); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn entry(&mut self, value: T) -> Entry<'_, T, S, A> { - map_entry(self.base.entry(value)) - } - - /// Returns `true` if `self` has no elements in common with `other`. - /// This is equivalent to checking for an empty intersection. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a = HashSet::from([1, 2, 3]); - /// let mut b = HashSet::new(); - /// - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(4); - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(1); - /// assert_eq!(a.is_disjoint(&b), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &HashSet) -> bool { - if self.len() <= other.len() { - self.iter().all(|v| !other.contains(v)) - } else { - other.iter().all(|v| !self.contains(v)) - } - } - - /// Returns `true` if the set is a subset of another, - /// i.e., `other` contains at least all the values in `self`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let sup = HashSet::from([1, 2, 3]); - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(2); - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(4); - /// assert_eq!(set.is_subset(&sup), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &HashSet) -> bool { - if self.len() <= other.len() { self.iter().all(|v| other.contains(v)) } else { false } - } - - /// Returns `true` if the set is a superset of another, - /// i.e., `self` contains at least all the values in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let sub = HashSet::from([1, 2]); - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(0); - /// set.insert(1); - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(2); - /// assert_eq!(set.is_superset(&sub), true); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &HashSet) -> bool { - other.is_subset(self) - } - - /// Adds a value to the set. - /// - /// Returns whether the value was newly inserted. That is: - /// - /// - If the set did not previously contain this value, `true` is returned. - /// - If the set already contained this value, `false` is returned, - /// and the set is not modified: original value is not replaced, - /// and the value passed as argument is dropped. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// - /// assert_eq!(set.insert(2), true); - /// assert_eq!(set.insert(2), false); - /// assert_eq!(set.len(), 1); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("push", "append", "put")] - pub fn insert(&mut self, value: T) -> bool { - self.base.insert(value) - } - - /// Adds a value to the set, replacing the existing value, if any, that is equal to the given - /// one. Returns the replaced value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// set.insert(Vec::::new()); - /// - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); - /// set.replace(Vec::with_capacity(10)); - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - #[rustc_confusables("swap")] - pub fn replace(&mut self, value: T) -> Option { - self.base.replace(value) - } - - /// Removes a value from the set. Returns whether the value was - /// present in the set. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// - /// set.insert(2); - /// assert_eq!(set.remove(&2), true); - /// assert_eq!(set.remove(&2), false); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("delete", "take")] - pub fn remove(&mut self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { - self.base.remove(value) - } - - /// Removes and returns the value in the set, if any, that is equal to the given one. - /// - /// The value may be any borrowed form of the set's value type, but - /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for - /// the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::from([1, 2, 3]); - /// assert_eq!(set.take(&2), Some(2)); - /// assert_eq!(set.take(&2), None); - /// ``` - #[inline] - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn take(&mut self, value: &Q) -> Option - where - T: Borrow, - Q: Hash + Eq, - { - self.base.take(value) - } -} - -#[inline] -fn map_entry<'a, K: 'a, V: 'a, A: Allocator>(raw: base::Entry<'a, K, V, A>) -> Entry<'a, K, V, A> { - match raw { - base::Entry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), - base::Entry::Vacant(base) => Entry::Vacant(VacantEntry { base }), - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashSet -where - T: Clone, - S: Clone, - A: Allocator + Clone, -{ - #[inline] - fn clone(&self) -> Self { - Self { base: self.base.clone() } - } - - /// Overwrites the contents of `self` with a clone of the contents of `source`. - /// - /// This method is preferred over simply assigning `source.clone()` to `self`, - /// as it avoids reallocation if possible. - #[inline] - fn clone_from(&mut self, other: &Self) { - self.base.clone_from(&other.base); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashSet -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - fn eq(&self, other: &HashSet) -> bool { - if self.len() != other.len() { - return false; - } - - self.iter().all(|key| other.contains(key)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashSet -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for HashSet -where - T: fmt::Debug, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_set().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for HashSet -where - T: Eq + Hash, - S: BuildHasher + Default, -{ - #[inline] - fn from_iter>(iter: I) -> HashSet { - let mut set = HashSet::with_hasher(Default::default()); - set.extend(iter); - set - } -} - -#[stable(feature = "std_collections_from_array", since = "1.56.0")] -// Note: as what is currently the most convenient built-in way to construct -// a HashSet, a simple usage of this function must not *require* the user -// to provide a type annotation in order to infer the third type parameter -// (the hasher parameter, conventionally "S"). -// To that end, this impl is defined using RandomState as the concrete -// type of S, rather than being generic over `S: BuildHasher + Default`. -// It is expected that users who want to specify a hasher will manually use -// `with_capacity_and_hasher`. -// If type parameter defaults worked on impls, and if type parameter -// defaults could be mixed with const generics, then perhaps -// this could be generalized. -// See also the equivalent impl on HashMap. -impl From<[T; N]> for HashSet -where - T: Eq + Hash, -{ - /// Converts a `[T; N]` into a `HashSet`. - /// - /// If the array contains any equal values, - /// all but one will be dropped. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let set1 = HashSet::from([1, 2, 3, 4]); - /// let set2: HashSet<_> = [1, 2, 3, 4].into(); - /// assert_eq!(set1, set2); - /// ``` - fn from(arr: [T; N]) -> Self { - Self::from_iter(arr) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for HashSet -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - #[inline] - fn extend>(&mut self, iter: I) { - self.base.extend(iter); - } - - #[inline] - fn extend_one(&mut self, item: T) { - self.base.insert(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - self.base.extend_reserve(additional); - } -} - -#[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, T, S, A> Extend<&'a T> for HashSet -where - T: 'a + Eq + Hash + Copy, - S: BuildHasher, - A: Allocator, -{ - #[inline] - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } - - #[inline] - fn extend_one(&mut self, &item: &'a T) { - self.base.insert(item); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - Extend::::extend_reserve(self, additional) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_default", issue = "143894")] -impl const Default for HashSet -where - S: [const] Default, -{ - /// Creates an empty `HashSet` with the `Default` value for the hasher. - #[inline] - fn default() -> HashSet { - HashSet { base: base::HashSet::with_hasher(Default::default()) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitOr<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the union of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([3, 4, 5]); - /// - /// let set = &a | &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 3, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitor(self, rhs: &HashSet) -> HashSet { - self.union(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitAnd<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the intersection of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([2, 3, 4]); - /// - /// let set = &a & &b; - /// - /// let mut i = 0; - /// let expected = [2, 3]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitand(self, rhs: &HashSet) -> HashSet { - self.intersection(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BitXor<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([3, 4, 5]); - /// - /// let set = &a ^ &b; - /// - /// let mut i = 0; - /// let expected = [1, 2, 4, 5]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn bitxor(self, rhs: &HashSet) -> HashSet { - self.symmetric_difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Sub<&HashSet> for &HashSet -where - T: Eq + Hash + Clone, - S: BuildHasher + Default, -{ - type Output = HashSet; - - /// Returns the difference of `self` and `rhs` as a new `HashSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([3, 4, 5]); - /// - /// let set = &a - &b; - /// - /// let mut i = 0; - /// let expected = [1, 2]; - /// for x in &set { - /// assert!(expected.contains(x)); - /// i += 1; - /// } - /// assert_eq!(i, expected.len()); - /// ``` - fn sub(self, rhs: &HashSet) -> HashSet { - self.difference(rhs).cloned().collect() - } -} - -/// An iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`iter`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`iter`]: HashSet::iter -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// -/// let mut iter = a.iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter_ty")] -pub struct Iter<'a, K: 'a> { - base: base::Iter<'a, K>, -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for Iter<'_, K> { - #[inline] - fn default() -> Self { - Iter { base: Default::default() } - } -} - -/// An owning iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`into_iter`] method on [`HashSet`] -/// (provided by the [`IntoIterator`] trait). See its documentation for more. -/// -/// [`into_iter`]: IntoIterator::into_iter -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// -/// let mut iter = a.into_iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter< - K, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::IntoIter, -} - -#[stable(feature = "default_iters_hash", since = "1.83.0")] -impl Default for IntoIter { - #[inline] - fn default() -> Self { - IntoIter { base: Default::default() } - } -} - -/// A draining iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`drain`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`drain`]: HashSet::drain -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let mut a = HashSet::from([1, 2, 3]); -/// -/// let mut drain = a.drain(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_drain_ty")] -pub struct Drain< - 'a, - K: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::Drain<'a, K, A>, -} - -/// A draining, filtering iterator over the items of a `HashSet`. -/// -/// This `struct` is created by the [`extract_if`] method on [`HashSet`]. -/// -/// [`extract_if`]: HashSet::extract_if -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let mut a = HashSet::from([1, 2, 3]); -/// -/// let mut extract_ifed = a.extract_if(|v| v % 2 == 0); -/// ``` -#[stable(feature = "hash_extract_if", since = "1.88.0")] -#[must_use = "iterators are lazy and do nothing unless consumed; \ - use `retain` to remove and discard elements"] -pub struct ExtractIf< - 'a, - K, - F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::ExtractIf<'a, K, F, A>, -} - -/// A lazy iterator producing elements in the intersection of `HashSet`s. -/// -/// This `struct` is created by the [`intersection`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`intersection`]: HashSet::intersection -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// let b = HashSet::from([4, 2, 3, 4]); -/// -/// let mut intersection = a.intersection(&b); -/// ``` -#[must_use = "this returns the intersection as an iterator, \ - without modifying either input set"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection< - 'a, - T: 'a, - S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - // iterator of the first set - iter: Iter<'a, T>, - // the second set - other: &'a HashSet, -} - -/// A lazy iterator producing elements in the difference of `HashSet`s. -/// -/// This `struct` is created by the [`difference`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`difference`]: HashSet::difference -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// let b = HashSet::from([4, 2, 3, 4]); -/// -/// let mut difference = a.difference(&b); -/// ``` -#[must_use = "this returns the difference as an iterator, \ - without modifying either input set"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference< - 'a, - T: 'a, - S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - // iterator of the first set - iter: Iter<'a, T>, - // the second set - other: &'a HashSet, -} - -/// A lazy iterator producing elements in the symmetric difference of `HashSet`s. -/// -/// This `struct` is created by the [`symmetric_difference`] method on -/// [`HashSet`]. See its documentation for more. -/// -/// [`symmetric_difference`]: HashSet::symmetric_difference -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// let b = HashSet::from([4, 2, 3, 4]); -/// -/// let mut intersection = a.symmetric_difference(&b); -/// ``` -#[must_use = "this returns the difference as an iterator, \ - without modifying either input set"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference< - 'a, - T: 'a, - S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - iter: Chain, Difference<'a, T, S, A>>, -} - -/// A lazy iterator producing elements in the union of `HashSet`s. -/// -/// This `struct` is created by the [`union`] method on [`HashSet`]. -/// See its documentation for more. -/// -/// [`union`]: HashSet::union -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashSet; -/// -/// let a = HashSet::from([1, 2, 3]); -/// let b = HashSet::from([4, 2, 3, 4]); -/// -/// let mut union_iter = a.union(&b); -/// ``` -#[must_use = "this returns the union as an iterator, \ - without modifying either input set"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Union< - 'a, - T: 'a, - S: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - iter: Chain, Difference<'a, T, S, A>>, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S, A: Allocator> IntoIterator for &'a HashSet { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - #[inline] - #[rustc_lint_query_instability] - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashSet { - type Item = T; - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out - /// of the set in arbitrary order. The set cannot be used after calling - /// this. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// let mut set = HashSet::new(); - /// set.insert("a".to_string()); - /// set.insert("b".to_string()); - /// - /// // Not possible to collect to a Vec with a regular `.iter()`. - /// let v: Vec = set.into_iter().collect(); - /// - /// // Will print in an arbitrary order. - /// for x in &v { - /// println!("{x}"); - /// } - /// ``` - #[inline] - #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { - IntoIter { base: self.base.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, K> { - #[inline] - fn clone(&self) -> Self { - Iter { base: self.base.clone() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K> Iterator for Iter<'a, K> { - type Item = &'a K; - - #[inline] - fn next(&mut self) -> Option<&'a K> { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn count(self) -> usize { - self.base.len() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, K> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, K> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Iter<'_, K> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn count(self) -> usize { - self.base.len() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.base, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, A: Allocator> Iterator for Drain<'a, K, A> { - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.base.fold(init, f) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Drain<'_, K, A> { - #[inline] - fn len(&self) -> usize { - self.base.len() - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K, A> {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.base, f) - } -} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, F, A> -where - F: FnMut(&K) -> bool, -{ - type Item = K; - - #[inline] - fn next(&mut self) -> Option { - self.base.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.base.size_hint() - } -} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, F, A> where F: FnMut(&K) -> bool {} - -#[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, F, A> -where - K: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ExtractIf").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Intersection<'_, T, S, A> { - #[inline] - fn clone(&self) -> Self { - Intersection { iter: self.iter.clone(), ..*self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S, A> Iterator for Intersection<'a, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - loop { - let elt = self.iter.next()?; - if self.other.contains(elt) { - return Some(elt); - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, |acc, elt| if self.other.contains(elt) { f(acc, elt) } else { acc }) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Intersection<'_, T, S, A> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Intersection<'_, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Difference<'_, T, S, A> { - #[inline] - fn clone(&self) -> Self { - Difference { iter: self.iter.clone(), ..*self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S, A> Iterator for Difference<'a, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - loop { - let elt = self.iter.next()?; - if !self.other.contains(elt) { - return Some(elt); - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - - #[inline] - fn fold(self, init: B, mut f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, |acc, elt| if self.other.contains(elt) { acc } else { f(acc, elt) }) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Difference<'_, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Difference<'_, T, S, A> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SymmetricDifference<'_, T, S, A> { - #[inline] - fn clone(&self) -> Self { - SymmetricDifference { iter: self.iter.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S, A> Iterator for SymmetricDifference<'a, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, f) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SymmetricDifference<'_, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for SymmetricDifference<'_, T, S, A> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Union<'_, T, S, A> { - #[inline] - fn clone(&self) -> Self { - Union { iter: self.iter.clone() } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Union<'_, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, -{ -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Union<'_, T, S, A> -where - T: fmt::Debug + Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S, A> Iterator for Union<'a, T, S, A> -where - T: Eq + Hash, - S: BuildHasher, - A: Allocator, -{ - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - #[inline] - fn count(self) -> usize { - self.iter.count() - } - #[inline] - fn fold(self, init: B, f: F) -> B - where - Self: Sized, - F: FnMut(B, Self::Item) -> B, - { - self.iter.fold(init, f) - } -} - -/// A view into a single entry in a set, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`HashSet`]. -/// -/// [`HashSet`]: struct.HashSet.html -/// [`entry`]: struct.HashSet.html#method.entry -/// -/// # Examples -/// -/// ``` -/// #![feature(hash_set_entry)] -/// -/// use std::collections::hash_set::HashSet; -/// -/// let mut set = HashSet::new(); -/// set.extend(["a", "b", "c"]); -/// assert_eq!(set.len(), 3); -/// -/// // Existing value (insert) -/// let entry = set.entry("a"); -/// let _raw_o = entry.insert(); -/// assert_eq!(set.len(), 3); -/// // Nonexistent value (insert) -/// set.entry("d").insert(); -/// -/// // Existing value (or_insert) -/// set.entry("b").or_insert(); -/// // Nonexistent value (or_insert) -/// set.entry("e").or_insert(); -/// -/// println!("Our HashSet: {:?}", set); -/// -/// let mut vec: Vec<_> = set.iter().copied().collect(); -/// // The `Iter` iterator produces items in arbitrary order, so the -/// // items must be sorted to test them against a sorted array. -/// vec.sort_unstable(); -/// assert_eq!(vec, ["a", "b", "c", "d", "e"]); -/// ``` -#[unstable(feature = "hash_set_entry", issue = "60896")] -pub enum Entry< - 'a, - T, - S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - /// An occupied entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::hash_set::{Entry, HashSet}; - /// - /// let mut set = HashSet::from(["a", "b"]); - /// - /// match set.entry("a") { - /// Entry::Vacant(_) => unreachable!(), - /// Entry::Occupied(_) => { } - /// } - /// ``` - Occupied(OccupiedEntry<'a, T, S, A>), - - /// A vacant entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::hash_set::{Entry, HashSet}; - /// - /// let mut set = HashSet::new(); - /// - /// match set.entry("a") { - /// Entry::Occupied(_) => unreachable!(), - /// Entry::Vacant(_) => { } - /// } - /// ``` - Vacant(VacantEntry<'a, T, S, A>), -} - -#[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for Entry<'_, T, S, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Entry::Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into an occupied entry in a `HashSet`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -/// -/// # Examples -/// -/// ``` -/// #![feature(hash_set_entry)] -/// -/// use std::collections::hash_set::{Entry, HashSet}; -/// -/// let mut set = HashSet::new(); -/// set.extend(["a", "b", "c"]); -/// -/// let _entry_o = set.entry("a").insert(); -/// assert_eq!(set.len(), 3); -/// -/// // Existing key -/// match set.entry("a") { -/// Entry::Vacant(_) => unreachable!(), -/// Entry::Occupied(view) => { -/// assert_eq!(view.get(), &"a"); -/// } -/// } -/// -/// assert_eq!(set.len(), 3); -/// -/// // Existing key (take) -/// match set.entry("c") { -/// Entry::Vacant(_) => unreachable!(), -/// Entry::Occupied(view) => { -/// assert_eq!(view.remove(), "c"); -/// } -/// } -/// assert_eq!(set.get(&"c"), None); -/// assert_eq!(set.len(), 2); -/// ``` -#[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct OccupiedEntry< - 'a, - T, - S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::OccupiedEntry<'a, T, S, A>, -} - -#[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for OccupiedEntry<'_, T, S, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("value", self.get()).finish() - } -} - -/// A view into a vacant entry in a `HashSet`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -/// -/// # Examples -/// -/// ``` -/// #![feature(hash_set_entry)] -/// -/// use std::collections::hash_set::{Entry, HashSet}; -/// -/// let mut set = HashSet::<&str>::new(); -/// -/// let entry_v = match set.entry("a") { -/// Entry::Vacant(view) => view, -/// Entry::Occupied(_) => unreachable!(), -/// }; -/// entry_v.insert(); -/// assert!(set.contains("a") && set.len() == 1); -/// -/// // Nonexistent key (insert) -/// match set.entry("b") { -/// Entry::Vacant(view) => view.insert(), -/// Entry::Occupied(_) => unreachable!(), -/// } -/// assert!(set.contains("b") && set.len() == 2); -/// ``` -#[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct VacantEntry< - 'a, - T, - S, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - base: base::VacantEntry<'a, T, S, A>, -} - -#[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for VacantEntry<'_, T, S, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.get()).finish() - } -} - -impl<'a, T, S, A: Allocator> Entry<'a, T, S, A> { - /// Sets the value of the entry, and returns an OccupiedEntry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// let entry = set.entry("horseyland").insert(); - /// - /// assert_eq!(entry.get(), &"horseyland"); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn insert(self) -> OccupiedEntry<'a, T, S, A> - where - T: Hash, - S: BuildHasher, - { - match self { - Entry::Occupied(entry) => entry, - Entry::Vacant(entry) => entry.insert_entry(), - } - } - - /// Ensures a value is in the entry by inserting if it was vacant. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// - /// // nonexistent key - /// set.entry("poneyland").or_insert(); - /// assert!(set.contains("poneyland")); - /// - /// // existing key - /// set.entry("poneyland").or_insert(); - /// assert!(set.contains("poneyland")); - /// assert_eq!(set.len(), 1); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn or_insert(self) - where - T: Hash, - S: BuildHasher, - { - if let Entry::Vacant(entry) = self { - entry.insert(); - } - } - - /// Returns a reference to this entry's value. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// set.entry("poneyland").or_insert(); - /// - /// // existing key - /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); - /// // nonexistent key - /// assert_eq!(set.entry("horseland").get(), &"horseland"); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get(&self) -> &T { - match *self { - Entry::Occupied(ref entry) => entry.get(), - Entry::Vacant(ref entry) => entry.get(), - } - } -} - -impl OccupiedEntry<'_, T, S, A> { - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::hash_set::{Entry, HashSet}; - /// - /// let mut set = HashSet::new(); - /// set.entry("poneyland").or_insert(); - /// - /// match set.entry("poneyland") { - /// Entry::Vacant(_) => panic!(), - /// Entry::Occupied(entry) => assert_eq!(entry.get(), &"poneyland"), - /// } - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get(&self) -> &T { - self.base.get() - } - - /// Takes the value out of the entry, and returns it. - /// Keeps the allocated memory for reuse. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// use std::collections::hash_set::Entry; - /// - /// let mut set = HashSet::new(); - /// // The set is empty - /// assert!(set.is_empty() && set.capacity() == 0); - /// - /// set.entry("poneyland").or_insert(); - /// let capacity_before_remove = set.capacity(); - /// - /// if let Entry::Occupied(o) = set.entry("poneyland") { - /// assert_eq!(o.remove(), "poneyland"); - /// } - /// - /// assert_eq!(set.contains("poneyland"), false); - /// // Now set hold none elements but capacity is equal to the old one - /// assert!(set.len() == 0 && set.capacity() == capacity_before_remove); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn remove(self) -> T { - self.base.remove() - } -} - -impl<'a, T, S, A: Allocator> VacantEntry<'a, T, S, A> { - /// Gets a reference to the value that would be used when inserting - /// through the `VacantEntry`. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// - /// let mut set = HashSet::new(); - /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn get(&self) -> &T { - self.base.get() - } - - /// Take ownership of the value. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::hash_set::{Entry, HashSet}; - /// - /// let mut set = HashSet::new(); - /// - /// match set.entry("poneyland") { - /// Entry::Occupied(_) => panic!(), - /// Entry::Vacant(v) => assert_eq!(v.into_value(), "poneyland"), - /// } - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn into_value(self) -> T { - self.base.into_value() - } - - /// Sets the value of the entry with the VacantEntry's value. - /// - /// # Examples - /// - /// ``` - /// #![feature(hash_set_entry)] - /// - /// use std::collections::HashSet; - /// use std::collections::hash_set::Entry; - /// - /// let mut set = HashSet::new(); - /// - /// if let Entry::Vacant(o) = set.entry("poneyland") { - /// o.insert(); - /// } - /// assert!(set.contains("poneyland")); - /// ``` - #[inline] - #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn insert(self) - where - T: Hash, - S: BuildHasher, - { - self.base.insert(); - } - - #[inline] - fn insert_entry(self) -> OccupiedEntry<'a, T, S, A> - where - T: Hash, - S: BuildHasher, - { - OccupiedEntry { base: self.base.insert() } - } -} - -#[allow(dead_code)] -fn assert_covariance() { - fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { - v - } - fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { - v - } - fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { - v - } - fn difference<'a, 'new>( - v: Difference<'a, &'static str, RandomState>, - ) -> Difference<'a, &'new str, RandomState> { - v - } - fn symmetric_difference<'a, 'new>( - v: SymmetricDifference<'a, &'static str, RandomState>, - ) -> SymmetricDifference<'a, &'new str, RandomState> { - v - } - fn intersection<'a, 'new>( - v: Intersection<'a, &'static str, RandomState>, - ) -> Intersection<'a, &'new str, RandomState> { - v - } - fn union<'a, 'new>( - v: Union<'a, &'static str, RandomState>, - ) -> Union<'a, &'new str, RandomState> { - v - } - fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { - d - } -} diff --git a/crates/std/src/collections/hash/set/tests.rs b/crates/std/src/collections/hash/set/tests.rs deleted file mode 100644 index d7bbc7b..0000000 --- a/crates/std/src/collections/hash/set/tests.rs +++ /dev/null @@ -1,529 +0,0 @@ -use super::HashSet; -use crate::hash::RandomState; -use crate::panic::{AssertUnwindSafe, catch_unwind}; -use crate::sync::Arc; -use crate::sync::atomic::{AtomicU32, Ordering}; - -#[test] -fn test_zero_capacities() { - type HS = HashSet; - - let s = HS::new(); - assert_eq!(s.capacity(), 0); - - let s = HS::default(); - assert_eq!(s.capacity(), 0); - - let s = HS::with_hasher(RandomState::new()); - assert_eq!(s.capacity(), 0); - - let s = HS::with_capacity(0); - assert_eq!(s.capacity(), 0); - - let s = HS::with_capacity_and_hasher(0, RandomState::new()); - assert_eq!(s.capacity(), 0); - - let mut s = HS::new(); - s.insert(1); - s.insert(2); - s.remove(&1); - s.remove(&2); - s.shrink_to_fit(); - assert_eq!(s.capacity(), 0); - - let mut s = HS::new(); - s.reserve(0); - assert_eq!(s.capacity(), 0); -} - -#[test] -fn test_disjoint() { - let mut xs = HashSet::new(); - let mut ys = HashSet::new(); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(5)); - assert!(ys.insert(11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(xs.insert(7)); - assert!(xs.insert(19)); - assert!(xs.insert(4)); - assert!(ys.insert(2)); - assert!(ys.insert(-11)); - assert!(xs.is_disjoint(&ys)); - assert!(ys.is_disjoint(&xs)); - assert!(ys.insert(7)); - assert!(!xs.is_disjoint(&ys)); - assert!(!ys.is_disjoint(&xs)); -} - -#[test] -fn test_subset_and_superset() { - let mut a = HashSet::new(); - assert!(a.insert(0)); - assert!(a.insert(5)); - assert!(a.insert(11)); - assert!(a.insert(7)); - - let mut b = HashSet::new(); - assert!(b.insert(0)); - assert!(b.insert(7)); - assert!(b.insert(19)); - assert!(b.insert(250)); - assert!(b.insert(11)); - assert!(b.insert(200)); - - assert!(!a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(!b.is_superset(&a)); - - assert!(b.insert(5)); - - assert!(a.is_subset(&b)); - assert!(!a.is_superset(&b)); - assert!(!b.is_subset(&a)); - assert!(b.is_superset(&a)); -} - -#[test] -fn test_iterate() { - let mut a = HashSet::new(); - for i in 0..32 { - assert!(a.insert(i)); - } - let mut observed: u32 = 0; - for k in &a { - observed |= 1 << *k; - } - assert_eq!(observed, 0xFFFF_FFFF); -} - -#[test] -fn test_intersection() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - assert!(a.intersection(&b).next().is_none()); - - assert!(a.insert(11)); - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(77)); - assert!(a.insert(103)); - assert!(a.insert(5)); - assert!(a.insert(-5)); - - assert!(b.insert(2)); - assert!(b.insert(11)); - assert!(b.insert(77)); - assert!(b.insert(-9)); - assert!(b.insert(-42)); - assert!(b.insert(5)); - assert!(b.insert(3)); - - let mut i = 0; - let expected = [3, 5, 11, 77]; - for x in a.intersection(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - assert!(a.insert(9)); // make a bigger than b - - i = 0; - for x in a.intersection(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - i = 0; - for x in b.intersection(&a) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_difference() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(3)); - assert!(b.insert(9)); - - let mut i = 0; - let expected = [1, 5, 11]; - for x in a.difference(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_symmetric_difference() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(5)); - assert!(a.insert(9)); - assert!(a.insert(11)); - - assert!(b.insert(-2)); - assert!(b.insert(3)); - assert!(b.insert(9)); - assert!(b.insert(14)); - assert!(b.insert(22)); - - let mut i = 0; - let expected = [-2, 1, 5, 11, 14, 22]; - for x in a.symmetric_difference(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_union() { - let mut a = HashSet::new(); - let mut b = HashSet::new(); - assert!(a.union(&b).next().is_none()); - assert!(b.union(&a).next().is_none()); - - assert!(a.insert(1)); - assert!(a.insert(3)); - assert!(a.insert(11)); - assert!(a.insert(16)); - assert!(a.insert(19)); - assert!(a.insert(24)); - - assert!(b.insert(-2)); - assert!(b.insert(1)); - assert!(b.insert(5)); - assert!(b.insert(9)); - assert!(b.insert(13)); - assert!(b.insert(19)); - - let mut i = 0; - let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24]; - for x in a.union(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - assert!(a.insert(9)); // make a bigger than b - assert!(a.insert(5)); - - i = 0; - for x in a.union(&b) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); - - i = 0; - for x in b.union(&a) { - assert!(expected.contains(x)); - i += 1 - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_from_iter() { - let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; - - let set: HashSet<_> = xs.iter().cloned().collect(); - - for x in &xs { - assert!(set.contains(x)); - } - - assert_eq!(set.iter().len(), xs.len() - 1); -} - -#[test] -fn test_move_iter() { - let hs = { - let mut hs = HashSet::new(); - - hs.insert('a'); - hs.insert('b'); - - hs - }; - - let v = hs.into_iter().collect::>(); - assert!(v == ['a', 'b'] || v == ['b', 'a']); -} - -#[test] -fn test_eq() { - // These constants once happened to expose a bug in insert(). - // I'm keeping them around to prevent a regression. - let mut s1 = HashSet::new(); - - s1.insert(1); - s1.insert(2); - s1.insert(3); - - let mut s2 = HashSet::new(); - - s2.insert(1); - s2.insert(2); - - assert!(s1 != s2); - - s2.insert(3); - - assert_eq!(s1, s2); -} - -#[test] -fn test_show() { - let mut set = HashSet::new(); - let empty = HashSet::::new(); - - set.insert(1); - set.insert(2); - - let set_str = format!("{set:?}"); - - assert!(set_str == "{1, 2}" || set_str == "{2, 1}"); - assert_eq!(format!("{empty:?}"), "{}"); -} - -#[test] -fn test_trivial_drain() { - let mut s = HashSet::::new(); - for _ in s.drain() {} - assert!(s.is_empty()); - drop(s); - - let mut s = HashSet::::new(); - drop(s.drain()); - assert!(s.is_empty()); -} - -#[test] -fn test_drain() { - let mut s: HashSet<_> = (1..100).collect(); - - // try this a bunch of times to make sure we don't screw up internal state. - for _ in 0..20 { - assert_eq!(s.len(), 99); - - { - let mut last_i = 0; - let mut d = s.drain(); - for (i, x) in d.by_ref().take(50).enumerate() { - last_i = i; - assert!(x != 0); - } - assert_eq!(last_i, 49); - } - - for _ in &s { - panic!("s should be empty!"); - } - - // reset to try again. - s.extend(1..100); - } -} - -#[test] -fn test_replace() { - use crate::hash; - - #[derive(Debug)] - struct Foo(&'static str, #[allow(dead_code)] i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl Eq for Foo {} - - impl hash::Hash for Foo { - fn hash(&self, h: &mut H) { - self.0.hash(h); - } - } - - let mut s = HashSet::new(); - assert_eq!(s.replace(Foo("a", 1)), None); - assert_eq!(s.len(), 1); - assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1))); - assert_eq!(s.len(), 1); - - let mut it = s.iter(); - assert_eq!(it.next(), Some(&Foo("a", 2))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_extend_ref() { - let mut a = HashSet::new(); - a.insert(1); - - a.extend(&[2, 3, 4]); - - assert_eq!(a.len(), 4); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - - let mut b = HashSet::new(); - b.insert(5); - b.insert(6); - - a.extend(&b); - - assert_eq!(a.len(), 6); - assert!(a.contains(&1)); - assert!(a.contains(&2)); - assert!(a.contains(&3)); - assert!(a.contains(&4)); - assert!(a.contains(&5)); - assert!(a.contains(&6)); -} - -#[test] -fn test_retain() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut set: HashSet = xs.iter().cloned().collect(); - set.retain(|&k| k % 2 == 0); - assert_eq!(set.len(), 3); - assert!(set.contains(&2)); - assert!(set.contains(&4)); - assert!(set.contains(&6)); -} - -#[test] -fn test_extract_if() { - let mut x: HashSet<_> = [1].iter().copied().collect(); - let mut y: HashSet<_> = [1].iter().copied().collect(); - - x.extract_if(|_| true).for_each(drop); - y.extract_if(|_| false).for_each(drop); - assert_eq!(x.len(), 0); - assert_eq!(y.len(), 1); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_extract_if_drop_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Hash)] - struct D(i32); - impl Drop for D { - fn drop(&mut self) { - if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { - panic!("panic in `drop`"); - } - } - } - - let mut set = (0..3).map(|i| D(i)).collect::>(); - - catch_unwind(move || { - set.extract_if(|_| { - PREDS.fetch_add(1, Ordering::SeqCst); - true - }) - .for_each(drop) - }) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 2); - assert_eq!(DROPS.load(Ordering::SeqCst), 3); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_extract_if_pred_panic_leak() { - static PREDS: AtomicU32 = AtomicU32::new(0); - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(PartialEq, Eq, PartialOrd, Hash)] - struct D; - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - } - } - - let mut set: HashSet<_> = (0..3).map(|_| D).collect(); - - catch_unwind(AssertUnwindSafe(|| { - set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { - 0 => true, - _ => panic!(), - }) - .for_each(drop) - })) - .ok(); - - assert_eq!(PREDS.load(Ordering::SeqCst), 1); - assert_eq!(DROPS.load(Ordering::SeqCst), 3); - assert_eq!(set.len(), 0); -} - -#[test] -fn from_array() { - let set = HashSet::from([1, 2, 3, 4]); - let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]); - assert_eq!(set, unordered_duplicates); - - // This next line must infer the hasher type parameter. - // If you make a change that causes this line to no longer infer, - // that's a problem! - let _must_not_require_type_annotation = HashSet::from([1, 2]); -} - -#[test] -fn const_with_hasher() { - const X: HashSet<(), ()> = HashSet::with_hasher(()); - const Y: HashSet<(), ()> = Default::default(); - assert_eq!(X.len(), 0); - assert_eq!(Y.len(), 0); -} - -#[test] -fn test_insert_does_not_overwrite_the_value() { - let first_value = Arc::new(17); - let second_value = Arc::new(17); - - let mut set = HashSet::new(); - let inserted = set.insert(first_value.clone()); - assert!(inserted); - - let inserted = set.insert(second_value); - assert!(!inserted); - - assert!( - Arc::ptr_eq(set.iter().next().unwrap(), &first_value), - "Insert must not overwrite the value, so the contained value pointer \ - must be the same as first value pointer we inserted" - ); -} diff --git a/crates/std/src/collections/mod.rs b/crates/std/src/collections/mod.rs deleted file mode 100644 index 460deb4..0000000 --- a/crates/std/src/collections/mod.rs +++ /dev/null @@ -1,459 +0,0 @@ -//! Collection types. -//! -//! Rust's standard collection library provides efficient implementations of the -//! most common general purpose programming data structures. By using the -//! standard implementations, it should be possible for two libraries to -//! communicate without significant data conversion. -//! -//! To get this out of the way: you should probably just use [`Vec`] or [`HashMap`]. -//! These two collections cover most use cases for generic data storage and -//! processing. They are exceptionally good at doing what they do. All the other -//! collections in the standard library have specific use cases where they are -//! the optimal choice, but these cases are borderline *niche* in comparison. -//! Even when `Vec` and `HashMap` are technically suboptimal, they're probably a -//! good enough choice to get started. -//! -//! Rust's collections can be grouped into four major categories: -//! -//! * Sequences: [`Vec`], [`VecDeque`], [`LinkedList`] -//! * Maps: [`HashMap`], [`BTreeMap`] -//! * Sets: [`HashSet`], [`BTreeSet`] -//! * Misc: [`BinaryHeap`] -//! -//! # When Should You Use Which Collection? -//! -//! These are fairly high-level and quick break-downs of when each collection -//! should be considered. Detailed discussions of strengths and weaknesses of -//! individual collections can be found on their own documentation pages. -//! -//! ### Use a [`Vec`] when: -//! * You want to collect items up to be processed or sent elsewhere later, and -//! don't care about any properties of the actual values being stored. -//! * You want a sequence of elements in a particular order, and will only be -//! appending to (or near) the end. -//! * You want a stack. -//! * You want a resizable array. -//! * You want a heap-allocated array. -//! -//! ### Use a [`VecDeque`] when: -//! * You want a [`Vec`] that supports efficient insertion at both ends of the -//! sequence. -//! * You want a queue. -//! * You want a double-ended queue (deque). -//! -//! ### Use a [`LinkedList`] when: -//! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate -//! amortization. -//! * You want to efficiently split and append lists. -//! * You are *absolutely* certain you *really*, *truly*, want a doubly linked -//! list. -//! -//! ### Use a [`HashMap`] when: -//! * You want to associate arbitrary keys with an arbitrary value. -//! * You want a cache. -//! * You want a map, with no extra functionality. -//! -//! ### Use a [`BTreeMap`] when: -//! * You want a map sorted by its keys. -//! * You want to be able to get a range of entries on-demand. -//! * You're interested in what the smallest or largest key-value pair is. -//! * You want to find the largest or smallest key that is smaller or larger -//! than something. -//! -//! ### Use the `Set` variant of any of these `Map`s when: -//! * You just want to remember which keys you've seen. -//! * There is no meaningful value to associate with your keys. -//! * You just want a set. -//! -//! ### Use a [`BinaryHeap`] when: -//! -//! * You want to store a bunch of elements, but only ever want to process the -//! "biggest" or "most important" one at any given time. -//! * You want a priority queue. -//! -//! # Performance -//! -//! Choosing the right collection for the job requires an understanding of what -//! each collection is good at. Here we briefly summarize the performance of -//! different collections for certain important operations. For further details, -//! see each type's documentation, and note that the names of actual methods may -//! differ from the tables below on certain collections. -//! -//! Throughout the documentation, we will adhere to the following conventions -//! for operation notation: -//! -//! * The collection's size is denoted by `n`. -//! * If a second collection is involved, its size is denoted by `m`. -//! * Item indices are denoted by `i`. -//! * Operations which have an *amortized* cost are suffixed with a `*`. -//! * Operations with an *expected* cost are suffixed with a `~`. -//! -//! Calling operations that add to a collection will occasionally require a -//! collection to be resized - an extra operation that takes *O*(*n*) time. -//! -//! *Amortized* costs are calculated to account for the time cost of such resize -//! operations *over a sufficiently large series of operations*. An individual -//! operation may be slower or faster due to the sporadic nature of collection -//! resizing, however the average cost per operation will approach the amortized -//! cost. -//! -//! Rust's collections never automatically shrink, so removal operations aren't -//! amortized. -//! -//! [`HashMap`] uses *expected* costs. It is theoretically possible, though very -//! unlikely, for [`HashMap`] to experience significantly worse performance than -//! the expected cost. This is due to the probabilistic nature of hashing - i.e. -//! it is possible to generate a duplicate hash given some input key that will -//! require extra computation to correct. -//! -//! ## Cost of Collection Operations -//! -//! -//! | | get(i) | insert(i) | remove(i) | append(Vec(m)) | split_off(i) | range | append | -//! |----------------|------------------------|-------------------------|------------------------|-------------------|------------------------|-----------------|--------------| -//! | [`Vec`] | *O*(1) | *O*(*n*-*i*)* | *O*(*n*-*i*) | *O*(*m*)* | *O*(*n*-*i*) | N/A | N/A | -//! | [`VecDeque`] | *O*(1) | *O*(min(*i*, *n*-*i*))* | *O*(min(*i*, *n*-*i*)) | *O*(*m*)* | *O*(min(*i*, *n*-*i*)) | N/A | N/A | -//! | [`LinkedList`] | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(1) | *O*(min(*i*, *n*-*i*)) | N/A | N/A | -//! | [`HashMap`] | *O*(1)~ | *O*(1)~* | *O*(1)~ | N/A | N/A | N/A | N/A | -//! | [`BTreeMap`] | *O*(log(*n*)) | *O*(log(*n*)) | *O*(log(*n*)) | N/A | N/A | *O*(log(*n*)) | *O*(*n*+*m*) | -//! -//! Note that where ties occur, [`Vec`] is generally going to be faster than -//! [`VecDeque`], and [`VecDeque`] is generally going to be faster than -//! [`LinkedList`]. -//! -//! For Sets, all operations have the cost of the equivalent Map operation. -//! -//! # Correct and Efficient Usage of Collections -//! -//! Of course, knowing which collection is the right one for the job doesn't -//! instantly permit you to use it correctly. Here are some quick tips for -//! efficient and correct usage of the standard collections in general. If -//! you're interested in how to use a specific collection in particular, consult -//! its documentation for detailed discussion and code examples. -//! -//! ## Capacity Management -//! -//! Many collections provide several constructors and methods that refer to -//! "capacity". These collections are generally built on top of an array. -//! Optimally, this array would be exactly the right size to fit only the -//! elements stored in the collection, but for the collection to do this would -//! be very inefficient. If the backing array was exactly the right size at all -//! times, then every time an element is inserted, the collection would have to -//! grow the array to fit it. Due to the way memory is allocated and managed on -//! most computers, this would almost surely require allocating an entirely new -//! array and copying every single element from the old one into the new one. -//! Hopefully you can see that this wouldn't be very efficient to do on every -//! operation. -//! -//! Most collections therefore use an *amortized* allocation strategy. They -//! generally let themselves have a fair amount of unoccupied space so that they -//! only have to grow on occasion. When they do grow, they allocate a -//! substantially larger array to move the elements into so that it will take a -//! while for another grow to be required. While this strategy is great in -//! general, it would be even better if the collection *never* had to resize its -//! backing array. Unfortunately, the collection itself doesn't have enough -//! information to do this itself. Therefore, it is up to us programmers to give -//! it hints. -//! -//! Any `with_capacity` constructor will instruct the collection to allocate -//! enough space for the specified number of elements. Ideally this will be for -//! exactly that many elements, but some implementation details may prevent -//! this. See collection-specific documentation for details. In general, use -//! `with_capacity` when you know exactly how many elements will be inserted, or -//! at least have a reasonable upper-bound on that number. -//! -//! When anticipating a large influx of elements, the `reserve` family of -//! methods can be used to hint to the collection how much room it should make -//! for the coming items. As with `with_capacity`, the precise behavior of -//! these methods will be specific to the collection of interest. -//! -//! For optimal performance, collections will generally avoid shrinking -//! themselves. If you believe that a collection will not soon contain any more -//! elements, or just really need the memory, the `shrink_to_fit` method prompts -//! the collection to shrink the backing array to the minimum size capable of -//! holding its elements. -//! -//! Finally, if ever you're interested in what the actual capacity of the -//! collection is, most collections provide a `capacity` method to query this -//! information on demand. This can be useful for debugging purposes, or for -//! use with the `reserve` methods. -//! -//! ## Iterators -//! -//! [Iterators][crate::iter] -//! are a powerful and robust mechanism used throughout Rust's -//! standard libraries. Iterators provide a sequence of values in a generic, -//! safe, efficient and convenient way. The contents of an iterator are usually -//! *lazily* evaluated, so that only the values that are actually needed are -//! ever actually produced, and no allocation need be done to temporarily store -//! them. Iterators are primarily consumed using a `for` loop, although many -//! functions also take iterators where a collection or sequence of values is -//! desired. -//! -//! All of the standard collections provide several iterators for performing -//! bulk manipulation of their contents. The three primary iterators almost -//! every collection should provide are `iter`, `iter_mut`, and `into_iter`. -//! Some of these are not provided on collections where it would be unsound or -//! unreasonable to provide them. -//! -//! `iter` provides an iterator of immutable references to all the contents of a -//! collection in the most "natural" order. For sequence collections like [`Vec`], -//! this means the items will be yielded in increasing order of index starting -//! at 0. For ordered collections like [`BTreeMap`], this means that the items -//! will be yielded in sorted order. For unordered collections like [`HashMap`], -//! the items will be yielded in whatever order the internal representation made -//! most convenient. This is great for reading through all the contents of the -//! collection. -//! -//! ``` -//! let vec = vec![1, 2, 3, 4]; -//! for x in vec.iter() { -//! println!("vec contained {x:?}"); -//! } -//! ``` -//! -//! `iter_mut` provides an iterator of *mutable* references in the same order as -//! `iter`. This is great for mutating all the contents of the collection. -//! -//! ``` -//! let mut vec = vec![1, 2, 3, 4]; -//! for x in vec.iter_mut() { -//! *x += 1; -//! } -//! ``` -//! -//! `into_iter` transforms the actual collection into an iterator over its -//! contents by-value. This is great when the collection itself is no longer -//! needed, and the values are needed elsewhere. Using `extend` with `into_iter` -//! is the main way that contents of one collection are moved into another. -//! `extend` automatically calls `into_iter`, and takes any T: [IntoIterator]. -//! Calling `collect` on an iterator itself is also a great way to convert one -//! collection into another. Both of these methods should internally use the -//! capacity management tools discussed in the previous section to do this as -//! efficiently as possible. -//! -//! ``` -//! let mut vec1 = vec![1, 2, 3, 4]; -//! let vec2 = vec![10, 20, 30, 40]; -//! vec1.extend(vec2); -//! ``` -//! -//! ``` -//! use std::collections::VecDeque; -//! -//! let vec = [1, 2, 3, 4]; -//! let buf: VecDeque<_> = vec.into_iter().collect(); -//! ``` -//! -//! Iterators also provide a series of *adapter* methods for performing common -//! threads to sequences. Among the adapters are functional favorites like `map`, -//! `fold`, `skip` and `take`. Of particular interest to collections is the -//! `rev` adapter, which reverses any iterator that supports this operation. Most -//! collections provide reversible iterators as the way to iterate over them in -//! reverse order. -//! -//! ``` -//! let vec = vec![1, 2, 3, 4]; -//! for x in vec.iter().rev() { -//! println!("vec contained {x:?}"); -//! } -//! ``` -//! -//! Several other collection methods also return iterators to yield a sequence -//! of results but avoid allocating an entire collection to store the result in. -//! This provides maximum flexibility as -//! [`collect`][crate::iter::Iterator::collect] or -//! [`extend`][crate::iter::Extend::extend] can be called to -//! "pipe" the sequence into any collection if desired. Otherwise, the sequence -//! can be looped over with a `for` loop. The iterator can also be discarded -//! after partial use, preventing the computation of the unused items. -//! -//! ## Entries -//! -//! The `entry` API is intended to provide an efficient mechanism for -//! manipulating the contents of a map conditionally on the presence of a key or -//! not. The primary motivating use case for this is to provide efficient -//! accumulator maps. For instance, if one wishes to maintain a count of the -//! number of times each key has been seen, they will have to perform some -//! conditional logic on whether this is the first time the key has been seen or -//! not. Normally, this would require a `find` followed by an `insert`, -//! effectively duplicating the search effort on each insertion. -//! -//! When a user calls `map.entry(key)`, the map will search for the key and -//! then yield a variant of the `Entry` enum. -//! -//! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case -//! the only valid operation is to `insert` a value into the entry. When this is -//! done, the vacant entry is consumed and converted into a mutable reference to -//! the value that was inserted. This allows for further manipulation of the -//! value beyond the lifetime of the search itself. This is useful if complex -//! logic needs to be performed on the value regardless of whether the value was -//! just inserted. -//! -//! If an `Occupied(entry)` is yielded, then the key *was* found. In this case, -//! the user has several options: they can `get`, `insert` or `remove` the -//! value of the occupied entry. Additionally, they can convert the occupied -//! entry into a mutable reference to its value, providing symmetry to the -//! vacant `insert` case. -//! -//! ### Examples -//! -//! Here are the two primary ways in which `entry` is used. First, a simple -//! example where the logic performed on the values is trivial. -//! -//! #### Counting the number of times each character in a string occurs -//! -//! ``` -//! use std::collections::btree_map::BTreeMap; -//! -//! let mut count = BTreeMap::new(); -//! let message = "she sells sea shells by the sea shore"; -//! -//! for c in message.chars() { -//! *count.entry(c).or_insert(0) += 1; -//! } -//! -//! assert_eq!(count.get(&'s'), Some(&8)); -//! -//! println!("Number of occurrences of each character"); -//! for (char, count) in &count { -//! println!("{char}: {count}"); -//! } -//! ``` -//! -//! When the logic to be performed on the value is more complex, we may simply -//! use the `entry` API to ensure that the value is initialized and perform the -//! logic afterwards. -//! -//! #### Tracking the inebriation of customers at a bar -//! -//! ``` -//! use std::collections::btree_map::BTreeMap; -//! -//! // A client of the bar. They have a blood alcohol level. -//! struct Person { blood_alcohol: f32 } -//! -//! // All the orders made to the bar, by client ID. -//! let orders = vec![1, 2, 1, 2, 3, 4, 1, 2, 2, 3, 4, 1, 1, 1]; -//! -//! // Our clients. -//! let mut blood_alcohol = BTreeMap::new(); -//! -//! for id in orders { -//! // If this is the first time we've seen this customer, initialize them -//! // with no blood alcohol. Otherwise, just retrieve them. -//! let person = blood_alcohol.entry(id).or_insert(Person { blood_alcohol: 0.0 }); -//! -//! // Reduce their blood alcohol level. It takes time to order and drink a beer! -//! person.blood_alcohol *= 0.9; -//! -//! // Check if they're sober enough to have another beer. -//! if person.blood_alcohol > 0.3 { -//! // Too drunk... for now. -//! println!("Sorry {id}, I have to cut you off"); -//! } else { -//! // Have another! -//! person.blood_alcohol += 0.1; -//! } -//! } -//! ``` -//! -//! # Insert and complex keys -//! -//! If we have a more complex key, calls to `insert` will -//! not update the value of the key. For example: -//! -//! ``` -//! use std::cmp::Ordering; -//! use std::collections::BTreeMap; -//! use std::hash::{Hash, Hasher}; -//! -//! #[derive(Debug)] -//! struct Foo { -//! a: u32, -//! b: &'static str, -//! } -//! -//! // we will compare `Foo`s by their `a` value only. -//! impl PartialEq for Foo { -//! fn eq(&self, other: &Self) -> bool { self.a == other.a } -//! } -//! -//! impl Eq for Foo {} -//! -//! // we will hash `Foo`s by their `a` value only. -//! impl Hash for Foo { -//! fn hash(&self, h: &mut H) { self.a.hash(h); } -//! } -//! -//! impl PartialOrd for Foo { -//! fn partial_cmp(&self, other: &Self) -> Option { self.a.partial_cmp(&other.a) } -//! } -//! -//! impl Ord for Foo { -//! fn cmp(&self, other: &Self) -> Ordering { self.a.cmp(&other.a) } -//! } -//! -//! let mut map = BTreeMap::new(); -//! map.insert(Foo { a: 1, b: "baz" }, 99); -//! -//! // We already have a Foo with an a of 1, so this will be updating the value. -//! map.insert(Foo { a: 1, b: "xyz" }, 100); -//! -//! // The value has been updated... -//! assert_eq!(map.values().next().unwrap(), &100); -//! -//! // ...but the key hasn't changed. b is still "baz", not "xyz". -//! assert_eq!(map.keys().next().unwrap().b, "baz"); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "try_reserve", since = "1.57.0")] -pub use alloc_crate::collections::TryReserveError; -#[unstable( - feature = "try_reserve_kind", - reason = "Uncertain how much info should be exposed", - issue = "48043" -)] -pub use alloc_crate::collections::TryReserveErrorKind; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{LinkedList, VecDeque}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::collections::{linked_list, vec_deque}; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::hash_map::HashMap; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::hash_set::HashSet; -#[stable(feature = "rust1", since = "1.0.0")] -// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning. -#[deprecated(note = "moved to `std::ops::Bound`", since = "1.26.0")] -#[doc(hidden)] -pub use crate::ops::Bound; - -mod hash; - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod hash_map { - //! A hash map implemented with quadratic probing and SIMD lookup. - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::hash::map::*; - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub use crate::hash::random::DefaultHasher; - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub use crate::hash::random::RandomState; -} - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod hash_set { - //! A hash set implemented as a `HashMap` where the value is `()`. - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::hash::set::*; -} diff --git a/crates/std/src/ffi/c_str.rs b/crates/std/src/ffi/c_str.rs deleted file mode 100644 index 8865d68..0000000 --- a/crates/std/src/ffi/c_str.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! [`CStr`], [`CString`], and related types. - -#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] -pub use alloc_crate::ffi::c_str::FromVecWithNulError; -#[stable(feature = "cstring_into", since = "1.7.0")] -pub use alloc_crate::ffi::c_str::IntoStringError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::ffi::c_str::{CString, NulError}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ffi::c_str::CStr; -#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] -pub use core::ffi::c_str::FromBytesUntilNulError; -#[stable(feature = "cstr_from_bytes", since = "1.10.0")] -pub use core::ffi::c_str::FromBytesWithNulError; diff --git a/crates/std/src/ffi/mod.rs b/crates/std/src/ffi/mod.rs deleted file mode 100644 index 999bd5e..0000000 --- a/crates/std/src/ffi/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -//! Utilities related to FFI bindings. -//! -//! This module provides utilities to handle data across non-Rust -//! interfaces, like other programming languages and the underlying -//! operating system. It is mainly of use for FFI (Foreign Function -//! Interface) bindings and code that needs to exchange C-like strings -//! with other languages. -//! -//! # Overview -//! -//! Rust represents owned strings with the [`String`] type, and -//! borrowed slices of strings with the [`str`] primitive. Both are -//! always in UTF-8 encoding, and may contain nul bytes in the middle, -//! i.e., if you look at the bytes that make up the string, there may -//! be a `\0` among them. Both `String` and `str` store their length -//! explicitly; there are no nul terminators at the end of strings -//! like in C. -//! -//! C strings are different from Rust strings: -//! -//! * **Encodings** - Rust strings are UTF-8, but C strings may use -//! other encodings. If you are using a string from C, you should -//! check its encoding explicitly, rather than just assuming that it -//! is UTF-8 like you can do in Rust. -//! -//! * **Character size** - C strings may use `char` or `wchar_t`-sized -//! characters; please **note** that C's `char` is different from Rust's. -//! The C standard leaves the actual sizes of those types open to -//! interpretation, but defines different APIs for strings made up of -//! each character type. Rust strings are always UTF-8, so different -//! Unicode characters will be encoded in a variable number of bytes -//! each. The Rust type [`char`] represents a '[Unicode scalar -//! value]', which is similar to, but not the same as, a '[Unicode -//! code point]'. -//! -//! * **Nul terminators and implicit string lengths** - Often, C -//! strings are nul-terminated, i.e., they have a `\0` character at the -//! end. The length of a string buffer is not stored, but has to be -//! calculated; to compute the length of a string, C code must -//! manually call a function like `strlen()` for `char`-based strings, -//! or `wcslen()` for `wchar_t`-based ones. Those functions return -//! the number of characters in the string excluding the nul -//! terminator, so the buffer length is really `len+1` characters. -//! Rust strings don't have a nul terminator; their length is always -//! stored and does not need to be calculated. While in Rust -//! accessing a string's length is an *O*(1) operation (because the -//! length is stored); in C it is an *O*(*n*) operation because the -//! length needs to be computed by scanning the string for the nul -//! terminator. -//! -//! * **Internal nul characters** - When C strings have a nul -//! terminator character, this usually means that they cannot have nul -//! characters in the middle — a nul character would essentially -//! truncate the string. Rust strings *can* have nul characters in -//! the middle, because nul does not have to mark the end of the -//! string in Rust. -//! -//! # Representations of non-Rust strings -//! -//! [`CString`] and [`CStr`] are useful when you need to transfer -//! UTF-8 strings to and from languages with a C ABI, like Python. -//! -//! * **From Rust to C:** [`CString`] represents an owned, C-friendly -//! string: it is nul-terminated, and has no internal nul characters. -//! Rust code can create a [`CString`] out of a normal string (provided -//! that the string doesn't have nul characters in the middle), and -//! then use a variety of methods to obtain a raw \*mut [u8] that can -//! then be passed as an argument to functions which use the C -//! conventions for strings. -//! -//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it -//! is what you would use to wrap a raw \*const [u8] that you got from -//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array -//! of bytes. Once you have a [`CStr`], you can convert it to a Rust -//! &[str] if it's valid UTF-8, or lossily convert it by adding -//! replacement characters. -//! -//! [`OsString`] and [`OsStr`] are useful when you need to transfer -//! strings to and from the operating system itself, or when capturing -//! the output of external commands. Conversions between [`OsString`], -//! [`OsStr`] and Rust strings work similarly to those for [`CString`] -//! and [`CStr`]. -//! -//! * [`OsString`] losslessly represents an owned platform string. However, this -//! representation is not necessarily in a form native to the platform. -//! In the Rust standard library, various APIs that transfer strings to/from the operating -//! system use [`OsString`] instead of plain strings. For example, -//! [`env::var_os()`] is used to query environment variables; it -//! returns an [Option]<[OsString]>. If the environment variable -//! exists you will get a [Some]\(os_string), which you can -//! *then* try to convert to a Rust string. This yields a [`Result`], so that -//! your code can detect errors in case the environment variable did -//! not in fact contain valid Unicode data. -//! -//! * [`OsStr`] losslessly represents a borrowed reference to a platform string. -//! However, this representation is not necessarily in a form native to the platform. -//! It can be converted into a UTF-8 Rust string slice in a similar way to -//! [`OsString`]. -//! -//! # Conversions -//! -//! ## On Unix -//! -//! On Unix, [`OsStr`] implements the -//! std::os::unix::ffi::[OsStrExt][unix.OsStrExt] trait, which -//! augments it with two methods, [`from_bytes`] and [`as_bytes`]. -//! These do inexpensive conversions from and to byte slices. -//! -//! Additionally, on Unix [`OsString`] implements the -//! std::os::unix::ffi::[OsStringExt][unix.OsStringExt] trait, -//! which provides [`from_vec`] and [`into_vec`] methods that consume -//! their arguments, and take or produce vectors of [`u8`]. -//! -//! ## On Windows -//! -//! An [`OsStr`] can be losslessly converted to a native Windows string. And -//! a native Windows string can be losslessly converted to an [`OsString`]. -//! -//! On Windows, [`OsStr`] implements the -//! std::os::windows::ffi::[OsStrExt][windows.OsStrExt] trait, -//! which provides an [`encode_wide`] method. This provides an -//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul -//! characters is appended, this is the same as a native Windows string. -//! -//! Additionally, on Windows [`OsString`] implements the -//! std::os::windows:ffi::[OsStringExt][windows.OsStringExt] -//! trait, which provides a [`from_wide`] method to convert a native Windows -//! string (without the terminating nul character) to an [`OsString`]. -//! -//! ## Other platforms -//! -//! Many other platforms provide their own extension traits in a -//! `std::os::*::ffi` module. -//! -//! ## On all platforms -//! -//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of -//! UTF-8; see [`OsString`] for more details on its encoding on different platforms. -//! -//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and -//! [`OsStr::from_encoded_bytes_unchecked`]. -//! -//! For basic string processing, see [`OsStr::slice_encoded_bytes`]. -//! -//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value -//! [Unicode code point]: https://www.unicode.org/glossary/#code_point -//! [`env::set_var()`]: crate::env::set_var "env::set_var" -//! [`env::var_os()`]: crate::env::var_os "env::var_os" -//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt" -//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec" -//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec" -//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt" -//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes" -//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes" -//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt" -//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt" -//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide" -//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect" -//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt" -//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide" - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "c_str_module", since = "1.88.0")] -pub mod c_str; - -#[stable(feature = "core_c_void", since = "1.30.0")] -pub use core::ffi::c_void; -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -pub use core::ffi::{VaArgSafe, VaList}; -#[stable(feature = "core_ffi_c", since = "1.64.0")] -pub use core::ffi::{ - c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, - c_ulong, c_ulonglong, c_ushort, -}; -#[unstable(feature = "c_size_t", issue = "88345")] -pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t}; - -#[doc(inline)] -#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] -pub use self::c_str::FromBytesUntilNulError; -#[doc(inline)] -#[stable(feature = "cstr_from_bytes", since = "1.10.0")] -pub use self::c_str::FromBytesWithNulError; -#[doc(inline)] -#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] -pub use self::c_str::FromVecWithNulError; -#[doc(inline)] -#[stable(feature = "cstring_into", since = "1.7.0")] -pub use self::c_str::IntoStringError; -#[doc(inline)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::c_str::NulError; -#[doc(inline)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::c_str::{CStr, CString}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::os_str::{OsStr, OsString}; - -#[stable(feature = "os_str_display", since = "1.87.0")] -pub mod os_str; diff --git a/crates/std/src/ffi/os_str.rs b/crates/std/src/ffi/os_str.rs deleted file mode 100644 index d8000e5..0000000 --- a/crates/std/src/ffi/os_str.rs +++ /dev/null @@ -1,1851 +0,0 @@ -//! The [`OsStr`] and [`OsString`] types and associated utilities. - -#[cfg(test)] -mod tests; - -use core::clone::CloneToUninit; - -use crate::borrow::{Borrow, Cow}; -use crate::collections::TryReserveError; -use crate::hash::{Hash, Hasher}; -use crate::ops::{self, Range}; -use crate::rc::Rc; -use crate::str::FromStr; -use crate::sync::Arc; -use crate::sys::os_str::{Buf, Slice}; -use crate::sys::{AsInner, FromInner, IntoInner}; -use crate::{cmp, fmt, slice}; - -/// A type that can represent owned, mutable platform-native strings, but is -/// cheaply inter-convertible with Rust strings. -/// -/// The need for this type arises from the fact that: -/// -/// * On Unix systems, strings are often arbitrary sequences of non-zero -/// bytes, in many cases interpreted as UTF-8. -/// -/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit -/// values, interpreted as UTF-16 when it is valid to do so. -/// -/// * In Rust, strings are always valid UTF-8, which may contain zeros. -/// -/// `OsString` and [`OsStr`] bridge this gap by simultaneously representing Rust -/// and platform-native string values, and in particular allowing a Rust string -/// to be converted into an "OS" string with no cost if possible. A consequence -/// of this is that `OsString` instances are *not* `NUL` terminated; in order -/// to pass to e.g., Unix system call, you should create a [`CStr`]. -/// -/// `OsString` is to &[OsStr] as [`String`] is to &[str]: the former -/// in each pair are owned strings; the latter are borrowed -/// references. -/// -/// Note, `OsString` and [`OsStr`] internally do not necessarily hold strings in -/// the form native to the platform; While on Unix, strings are stored as a -/// sequence of 8-bit values, on Windows, where strings are 16-bit value based -/// as just discussed, strings are also actually stored as a sequence of 8-bit -/// values, encoded in a less-strict variant of UTF-8. This is useful to -/// understand when handling capacity and length values. -/// -/// # Capacity of `OsString` -/// -/// Capacity uses units of UTF-8 bytes for OS strings which were created from valid unicode, and -/// uses units of bytes in an unspecified encoding for other contents. On a given target, all -/// `OsString` and `OsStr` values use the same units for capacity, so the following will work: -/// ``` -/// use std::ffi::{OsStr, OsString}; -/// -/// fn concat_os_strings(a: &OsStr, b: &OsStr) -> OsString { -/// let mut ret = OsString::with_capacity(a.len() + b.len()); // This will allocate -/// ret.push(a); // This will not allocate further -/// ret.push(b); // This will not allocate further -/// ret -/// } -/// ``` -/// -/// # Creating an `OsString` -/// -/// **From a Rust string**: `OsString` implements -/// [From]<[String]>, so you can use my_string.[into]\() to -/// create an `OsString` from a normal Rust string. -/// -/// **From slices:** Just like you can start with an empty Rust -/// [`String`] and then [`String::push_str`] some &[str] -/// sub-string slices into it, you can create an empty `OsString` with -/// the [`OsString::new`] method and then push string slices into it with the -/// [`OsString::push`] method. -/// -/// # Extracting a borrowed reference to the whole OS string -/// -/// You can use the [`OsString::as_os_str`] method to get an &[OsStr] from -/// an `OsString`; this is effectively a borrowed reference to the -/// whole string. -/// -/// # Conversions -/// -/// See the [module's toplevel documentation about conversions][conversions] for a discussion on -/// the traits which `OsString` implements for [conversions] from/to native representations. -/// -/// [`CStr`]: crate::ffi::CStr -/// [conversions]: super#conversions -/// [into]: Into::into -#[cfg_attr(not(test), rustc_diagnostic_item = "OsString")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OsString { - inner: Buf, -} - -/// Allows extension traits within `std`. -#[unstable(feature = "sealed", issue = "none")] -impl crate::sealed::Sealed for OsString {} - -/// Borrowed reference to an OS string (see [`OsString`]). -/// -/// This type represents a borrowed reference to a string in the operating system's preferred -/// representation. -/// -/// `&OsStr` is to [`OsString`] as &[str] is to [`String`]: the -/// former in each pair are borrowed references; the latter are owned strings. -/// -/// See the [module's toplevel documentation about conversions][conversions] for a discussion on -/// the traits which `OsStr` implements for [conversions] from/to native representations. -/// -/// [conversions]: super#conversions -#[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")] -#[stable(feature = "rust1", since = "1.0.0")] -// `OsStr::from_inner` and `impl CloneToUninit for OsStr` current implementation relies -// on `OsStr` being layout-compatible with `Slice`. -// However, `OsStr` layout is considered an implementation detail and must not be relied upon. -#[repr(transparent)] -pub struct OsStr { - inner: Slice, -} - -/// Allows extension traits within `std`. -#[unstable(feature = "sealed", issue = "none")] -impl crate::sealed::Sealed for OsStr {} - -impl OsString { - /// Constructs a new empty `OsString`. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "1.91.0")] - pub const fn new() -> OsString { - OsString { inner: Buf::from_string(String::new()) } - } - - /// Converts bytes to an `OsString` without checking that the bytes contains - /// valid [`OsStr`]-encoded data. - /// - /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. - /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit - /// ASCII. - /// - /// See the [module's toplevel documentation about conversions][conversions] for safe, - /// cross-platform [conversions] from/to native representations. - /// - /// # Safety - /// - /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of - /// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version - /// built for the same target platform. For example, reconstructing an `OsString` from bytes sent - /// over the network or stored in a file will likely violate these safety rules. - /// - /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_encoded_bytes`] can be - /// split either immediately before or immediately after any valid non-empty UTF-8 substring. - /// - /// # Example - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("Mary had a little lamb"); - /// let bytes = os_str.as_encoded_bytes(); - /// let words = bytes.split(|b| *b == b' '); - /// let words: Vec<&OsStr> = words.map(|word| { - /// // SAFETY: - /// // - Each `word` only contains content that originated from `OsStr::as_encoded_bytes` - /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring - /// unsafe { OsStr::from_encoded_bytes_unchecked(word) } - /// }).collect(); - /// ``` - /// - /// [conversions]: super#conversions - #[inline] - #[stable(feature = "os_str_bytes", since = "1.74.0")] - pub unsafe fn from_encoded_bytes_unchecked(bytes: Vec) -> Self { - OsString { inner: unsafe { Buf::from_encoded_bytes_unchecked(bytes) } } - } - - /// Converts to an [`OsStr`] slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsString, OsStr}; - /// - /// let os_string = OsString::from("foo"); - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_string.as_os_str(), os_str); - /// ``` - #[cfg_attr(not(test), rustc_diagnostic_item = "os_string_as_os_str")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn as_os_str(&self) -> &OsStr { - self - } - - /// Converts the `OsString` into a byte vector. To convert the byte vector back into an - /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. - /// - /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. - /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit - /// ASCII. - /// - /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should - /// be treated as opaque and only comparable within the same Rust version built for the same - /// target platform. For example, sending the bytes over the network or storing it in a file - /// will likely result in incompatible data. See [`OsString`] for more encoding details - /// and [`std::ffi`] for platform-specific, specified conversions. - /// - /// [`std::ffi`]: crate::ffi - #[inline] - #[stable(feature = "os_str_bytes", since = "1.74.0")] - pub fn into_encoded_bytes(self) -> Vec { - self.inner.into_encoded_bytes() - } - - /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. - /// - /// On failure, ownership of the original `OsString` is returned. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::from("foo"); - /// let string = os_string.into_string(); - /// assert_eq!(string, Ok(String::from("foo"))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn into_string(self) -> Result { - self.inner.into_string().map_err(|buf| OsString { inner: buf }) - } - - /// Extends the string with the given &[OsStr] slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::from("foo"); - /// os_string.push("bar"); - /// assert_eq!(&os_string, "foobar"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[rustc_confusables("append", "put")] - pub fn push>(&mut self, s: T) { - trait SpecPushTo { - fn spec_push_to(&self, buf: &mut OsString); - } - - impl> SpecPushTo for T { - #[inline] - default fn spec_push_to(&self, buf: &mut OsString) { - buf.inner.push_slice(&self.as_ref().inner); - } - } - - // Use a more efficient implementation when the string is UTF-8. - macro spec_str($T:ty) { - impl SpecPushTo for $T { - #[inline] - fn spec_push_to(&self, buf: &mut OsString) { - buf.inner.push_str(self); - } - } - } - spec_str!(str); - spec_str!(String); - - s.spec_push_to(self) - } - - /// Creates a new `OsString` with at least the given capacity. - /// - /// The string will be able to hold at least `capacity` length units of other - /// OS strings without reallocating. This method is allowed to allocate for - /// more units than `capacity`. If `capacity` is 0, the string will not - /// allocate. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::with_capacity(10); - /// let capacity = os_string.capacity(); - /// - /// // This push is done without reallocating - /// os_string.push("foo"); - /// - /// assert_eq!(capacity, os_string.capacity()); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[must_use] - #[inline] - pub fn with_capacity(capacity: usize) -> OsString { - OsString { inner: Buf::with_capacity(capacity) } - } - - /// Truncates the `OsString` to zero length. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut os_string = OsString::from("foo"); - /// assert_eq!(&os_string, "foo"); - /// - /// os_string.clear(); - /// assert_eq!(&os_string, ""); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[inline] - pub fn clear(&mut self) { - self.inner.clear() - } - - /// Returns the capacity this `OsString` can hold without reallocating. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let os_string = OsString::with_capacity(10); - /// assert!(os_string.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[must_use] - #[inline] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - /// Reserves capacity for at least `additional` more capacity to be inserted - /// in the given `OsString`. Does nothing if the capacity is - /// already sufficient. - /// - /// The collection may reserve more space to speculatively avoid frequent reallocations. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::new(); - /// s.reserve(10); - /// assert!(s.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more length units - /// in the given `OsString`. The string may reserve more space to speculatively avoid - /// frequent reallocations. After calling `try_reserve`, capacity will be - /// greater than or equal to `self.len() + additional` if it returns `Ok(())`. - /// Does nothing if capacity is already sufficient. This method preserves - /// the contents even if an error occurs. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsStr, OsString}; - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &str) -> Result { - /// let mut s = OsString::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// s.try_reserve(OsStr::new(data).len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// s.push(data); - /// - /// Ok(s) - /// } - /// # process_data("123").expect("why is the test harness OOMing on 3 bytes?"); - /// ``` - #[stable(feature = "try_reserve_2", since = "1.63.0")] - #[inline] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.inner.try_reserve(additional) - } - - /// Reserves the minimum capacity for at least `additional` more capacity to - /// be inserted in the given `OsString`. Does nothing if the capacity is - /// already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer [`reserve`] if future insertions are expected. - /// - /// [`reserve`]: OsString::reserve - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::new(); - /// s.reserve_exact(10); - /// assert!(s.capacity() >= 10); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - /// Tries to reserve the minimum capacity for at least `additional` - /// more length units in the given `OsString`. After calling - /// `try_reserve_exact`, capacity will be greater than or equal to - /// `self.len() + additional` if it returns `Ok(())`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the `OsString` more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer [`try_reserve`] if future insertions are expected. - /// - /// [`try_reserve`]: OsString::try_reserve - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsStr, OsString}; - /// use std::collections::TryReserveError; - /// - /// fn process_data(data: &str) -> Result { - /// let mut s = OsString::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// s.try_reserve_exact(OsStr::new(data).len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// s.push(data); - /// - /// Ok(s) - /// } - /// # process_data("123").expect("why is the test harness OOMing on 3 bytes?"); - /// ``` - #[stable(feature = "try_reserve_2", since = "1.63.0")] - #[inline] - pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.inner.try_reserve_exact(additional) - } - - /// Shrinks the capacity of the `OsString` to match its length. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to_fit(); - /// assert_eq!(3, s.capacity()); - /// ``` - #[stable(feature = "osstring_shrink_to_fit", since = "1.19.0")] - #[inline] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - /// Shrinks the capacity of the `OsString` with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// If the current capacity is less than the lower limit, this is a no-op. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("foo"); - /// - /// s.reserve(100); - /// assert!(s.capacity() >= 100); - /// - /// s.shrink_to(10); - /// assert!(s.capacity() >= 10); - /// s.shrink_to(0); - /// assert!(s.capacity() >= 3); - /// ``` - #[inline] - #[stable(feature = "shrink_to", since = "1.56.0")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - /// Converts this `OsString` into a boxed [`OsStr`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsString, OsStr}; - /// - /// let s = OsString::from("hello"); - /// - /// let b: Box = s.into_boxed_os_str(); - /// ``` - #[must_use = "`self` will be dropped if the result is not used"] - #[stable(feature = "into_boxed_os_str", since = "1.20.0")] - pub fn into_boxed_os_str(self) -> Box { - let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } - } - - /// Consumes and leaks the `OsString`, returning a mutable reference to the contents, - /// `&'a mut OsStr`. - /// - /// The caller has free choice over the returned lifetime, including 'static. - /// Indeed, this function is ideally used for data that lives for the remainder of - /// the program’s life, as dropping the returned reference will cause a memory leak. - /// - /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include - /// unused capacity that is not part of the returned slice. If you want to discard excess - /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead. - /// However, keep in mind that trimming the capacity may result in a reallocation and copy. - /// - /// [`into_boxed_os_str`]: Self::into_boxed_os_str - #[stable(feature = "os_string_pathbuf_leak", since = "1.89.0")] - #[inline] - pub fn leak<'a>(self) -> &'a mut OsStr { - OsStr::from_inner_mut(self.inner.leak()) - } - - /// Truncate the `OsString` to the specified length. - /// - /// If `new_len` is greater than the string's current length, this has no - /// effect. - /// - /// # Panics - /// - /// Panics if `len` does not lie on a valid `OsStr` boundary - /// (as described in [`OsStr::slice_encoded_bytes`]). - #[inline] - #[unstable(feature = "os_string_truncate", issue = "133262")] - pub fn truncate(&mut self, len: usize) { - if len <= self.len() { - self.as_os_str().inner.check_public_boundary(len); - // SAFETY: The length was just checked to be at a valid boundary. - unsafe { self.inner.truncate_unchecked(len) }; - } - } - - /// Provides plumbing to `Vec::extend_from_slice` without giving full - /// mutable access to the `Vec`. - /// - /// # Safety - /// - /// The slice must be valid for the platform encoding (as described in - /// [`OsStr::from_encoded_bytes_unchecked`]). - /// - /// This bypasses the encoding-dependent surrogate joining, so either - /// `self` must not end with a leading surrogate half, or `other` must not - /// start with a trailing surrogate half. - #[inline] - pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { - // SAFETY: Guaranteed by caller. - unsafe { self.inner.extend_from_slice_unchecked(other) }; - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for OsString { - /// Converts a [`String`] into an [`OsString`]. - /// - /// This conversion does not allocate or copy memory. - #[inline] - fn from(s: String) -> OsString { - OsString { inner: Buf::from_string(s) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl> From<&T> for OsString { - /// Copies any value implementing [AsRef]<[OsStr]> - /// into a newly allocated [`OsString`]. - fn from(s: &T) -> OsString { - trait SpecToOsString { - fn spec_to_os_string(&self) -> OsString; - } - - impl> SpecToOsString for T { - #[inline] - default fn spec_to_os_string(&self) -> OsString { - self.as_ref().to_os_string() - } - } - - // Preserve the known-UTF-8 property for strings. - macro spec_str($T:ty) { - impl SpecToOsString for $T { - #[inline] - fn spec_to_os_string(&self) -> OsString { - OsString::from(String::from(self)) - } - } - } - spec_str!(str); - spec_str!(String); - - s.spec_to_os_string() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for OsString { - type Output = OsStr; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &OsStr { - OsStr::from_inner(self.inner.as_slice()) - } -} - -#[stable(feature = "mut_osstr", since = "1.44.0")] -impl ops::IndexMut for OsString { - #[inline] - fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr { - OsStr::from_inner_mut(self.inner.as_mut_slice()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for OsString { - type Target = OsStr; - - #[inline] - fn deref(&self) -> &OsStr { - &self[..] - } -} - -#[stable(feature = "mut_osstr", since = "1.44.0")] -impl ops::DerefMut for OsString { - #[inline] - fn deref_mut(&mut self) -> &mut OsStr { - &mut self[..] - } -} - -#[stable(feature = "osstring_default", since = "1.9.0")] -impl Default for OsString { - /// Constructs an empty `OsString`. - #[inline] - fn default() -> OsString { - OsString::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for OsString { - #[inline] - fn clone(&self) -> Self { - OsString { inner: self.inner.clone() } - } - - /// Clones the contents of `source` into `self`. - /// - /// This method is preferred over simply assigning `source.clone()` to `self`, - /// as it avoids reallocation if possible. - #[inline] - fn clone_from(&mut self, source: &Self) { - self.inner.clone_from(&source.inner) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for OsString { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, formatter) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsString { - #[inline] - fn eq(&self, other: &OsString) -> bool { - &**self == &**other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsString { - #[inline] - fn eq(&self, other: &str) -> bool { - &**self == other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for str { - #[inline] - fn eq(&self, other: &OsString) -> bool { - &**other == self - } -} - -#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] -impl PartialEq<&str> for OsString { - #[inline] - fn eq(&self, other: &&str) -> bool { - **self == **other - } -} - -#[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] -impl<'a> PartialEq for &'a str { - #[inline] - fn eq(&self, other: &OsString) -> bool { - **other == **self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for OsString {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsString { - #[inline] - fn partial_cmp(&self, other: &OsString) -> Option { - (&**self).partial_cmp(&**other) - } - #[inline] - fn lt(&self, other: &OsString) -> bool { - &**self < &**other - } - #[inline] - fn le(&self, other: &OsString) -> bool { - &**self <= &**other - } - #[inline] - fn gt(&self, other: &OsString) -> bool { - &**self > &**other - } - #[inline] - fn ge(&self, other: &OsString) -> bool { - &**self >= &**other - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsString { - #[inline] - fn partial_cmp(&self, other: &str) -> Option { - (&**self).partial_cmp(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for OsString { - #[inline] - fn cmp(&self, other: &OsString) -> cmp::Ordering { - (&**self).cmp(&**other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for OsString { - #[inline] - fn hash(&self, state: &mut H) { - (&**self).hash(state) - } -} - -#[stable(feature = "os_string_fmt_write", since = "1.64.0")] -impl fmt::Write for OsString { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.push(s); - Ok(()) - } -} - -impl OsStr { - /// Coerces into an `OsStr` slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - pub const fn new + ?Sized>(s: &S) -> &OsStr { - s.as_ref() - } - - /// Converts a slice of bytes to an OS string slice without checking that the string contains - /// valid `OsStr`-encoded data. - /// - /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. - /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit - /// ASCII. - /// - /// See the [module's toplevel documentation about conversions][conversions] for safe, - /// cross-platform [conversions] from/to native representations. - /// - /// # Safety - /// - /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of - /// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version - /// built for the same target platform. For example, reconstructing an `OsStr` from bytes sent - /// over the network or stored in a file will likely violate these safety rules. - /// - /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_encoded_bytes`] can be - /// split either immediately before or immediately after any valid non-empty UTF-8 substring. - /// - /// # Example - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("Mary had a little lamb"); - /// let bytes = os_str.as_encoded_bytes(); - /// let words = bytes.split(|b| *b == b' '); - /// let words: Vec<&OsStr> = words.map(|word| { - /// // SAFETY: - /// // - Each `word` only contains content that originated from `OsStr::as_encoded_bytes` - /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring - /// unsafe { OsStr::from_encoded_bytes_unchecked(word) } - /// }).collect(); - /// ``` - /// - /// [conversions]: super#conversions - #[inline] - #[stable(feature = "os_str_bytes", since = "1.74.0")] - pub unsafe fn from_encoded_bytes_unchecked(bytes: &[u8]) -> &Self { - Self::from_inner(unsafe { Slice::from_encoded_bytes_unchecked(bytes) }) - } - - #[inline] - #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - const fn from_inner(inner: &Slice) -> &OsStr { - // SAFETY: OsStr is just a wrapper of Slice, - // therefore converting &Slice to &OsStr is safe. - unsafe { &*(inner as *const Slice as *const OsStr) } - } - - #[inline] - #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - const fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { - // SAFETY: OsStr is just a wrapper of Slice, - // therefore converting &mut Slice to &mut OsStr is safe. - // Any method that mutates OsStr must be careful not to - // break platform-specific encoding, in particular Wtf8 on Windows. - unsafe { &mut *(inner as *mut Slice as *mut OsStr) } - } - - /// Yields a &[str] slice if the `OsStr` is valid Unicode. - /// - /// This conversion may entail doing a check for UTF-8 validity. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_str.to_str(), Some("foo")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub fn to_str(&self) -> Option<&str> { - self.inner.to_str().ok() - } - - /// Converts an `OsStr` to a [Cow]<[str]>. - /// - /// Any non-UTF-8 sequences are replaced with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. - /// - /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER - /// - /// # Examples - /// - /// Calling `to_string_lossy` on an `OsStr` with invalid unicode: - /// - /// ``` - /// // Note, due to differences in how Unix and Windows represent strings, - /// // we are forced to complicate this example, setting up example `OsStr`s - /// // with different source data and via different platform extensions. - /// // Understand that in reality you could end up with such example invalid - /// // sequences simply through collecting user command line arguments, for - /// // example. - /// - /// #[cfg(unix)] { - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' - /// // respectively. The value 0x80 is a lone continuation byte, invalid - /// // in a UTF-8 sequence. - /// let source = [0x66, 0x6f, 0x80, 0x6f]; - /// let os_str = OsStr::from_bytes(&source[..]); - /// - /// assert_eq!(os_str.to_string_lossy(), "fo�o"); - /// } - /// #[cfg(windows)] { - /// use std::ffi::OsString; - /// use std::os::windows::prelude::*; - /// - /// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' - /// // respectively. The value 0xD800 is a lone surrogate half, invalid - /// // in a UTF-16 sequence. - /// let source = [0x0066, 0x006f, 0xD800, 0x006f]; - /// let os_string = OsString::from_wide(&source[..]); - /// let os_str = os_string.as_os_str(); - /// - /// assert_eq!(os_str.to_string_lossy(), "fo�o"); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub fn to_string_lossy(&self) -> Cow<'_, str> { - self.inner.to_string_lossy() - } - - /// Copies the slice into an owned [`OsString`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{OsStr, OsString}; - /// - /// let os_str = OsStr::new("foo"); - /// let os_string = os_str.to_os_string(); - /// assert_eq!(os_string, OsString::from("foo")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[cfg_attr(not(test), rustc_diagnostic_item = "os_str_to_os_string")] - pub fn to_os_string(&self) -> OsString { - OsString { inner: self.inner.to_owned() } - } - - /// Checks whether the `OsStr` is empty. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new(""); - /// assert!(os_str.is_empty()); - /// - /// let os_str = OsStr::new("foo"); - /// assert!(!os_str.is_empty()); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[must_use] - #[inline] - pub fn is_empty(&self) -> bool { - self.inner.inner.is_empty() - } - - /// Returns the length of this `OsStr`. - /// - /// Note that this does **not** return the number of bytes in the string in - /// OS string form. - /// - /// The length returned is that of the underlying storage used by `OsStr`. - /// As discussed in the [`OsString`] introduction, [`OsString`] and `OsStr` - /// store strings in a form best suited for cheap inter-conversion between - /// native-platform and Rust string forms, which may differ significantly - /// from both of them, including in storage size and encoding. - /// - /// This number is simply useful for passing to other methods, like - /// [`OsString::with_capacity`] to avoid reallocations. - /// - /// See the main `OsString` documentation information about encoding and capacity units. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new(""); - /// assert_eq!(os_str.len(), 0); - /// - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_str.len(), 3); - /// ``` - #[stable(feature = "osstring_simple_functions", since = "1.9.0")] - #[must_use] - #[inline] - pub fn len(&self) -> usize { - self.inner.inner.len() - } - - /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or allocating. - #[stable(feature = "into_boxed_os_str", since = "1.20.0")] - #[must_use = "`self` will be dropped if the result is not used"] - pub fn into_os_string(self: Box) -> OsString { - let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; - OsString { inner: Buf::from_box(boxed) } - } - - /// Converts an OS string slice to a byte slice. To convert the byte slice back into an OS - /// string slice, use the [`OsStr::from_encoded_bytes_unchecked`] function. - /// - /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. - /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit - /// ASCII. - /// - /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should - /// be treated as opaque and only comparable within the same Rust version built for the same - /// target platform. For example, sending the slice over the network or storing it in a file - /// will likely result in incompatible byte slices. See [`OsString`] for more encoding details - /// and [`std::ffi`] for platform-specific, specified conversions. - /// - /// [`std::ffi`]: crate::ffi - #[inline] - #[stable(feature = "os_str_bytes", since = "1.74.0")] - pub fn as_encoded_bytes(&self) -> &[u8] { - self.inner.as_encoded_bytes() - } - - /// Takes a substring based on a range that corresponds to the return value of - /// [`OsStr::as_encoded_bytes`]. - /// - /// The range's start and end must lie on valid `OsStr` boundaries. - /// A valid `OsStr` boundary is one of: - /// - The start of the string - /// - The end of the string - /// - Immediately before a valid non-empty UTF-8 substring - /// - Immediately after a valid non-empty UTF-8 substring - /// - /// # Panics - /// - /// Panics if `range` does not lie on valid `OsStr` boundaries or if it - /// exceeds the end of the string. - /// - /// # Example - /// - /// ``` - /// #![feature(os_str_slice)] - /// - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo=bar"); - /// let bytes = os_str.as_encoded_bytes(); - /// if let Some(index) = bytes.iter().position(|b| *b == b'=') { - /// let key = os_str.slice_encoded_bytes(..index); - /// let value = os_str.slice_encoded_bytes(index + 1..); - /// assert_eq!(key, "foo"); - /// assert_eq!(value, "bar"); - /// } - /// ``` - #[unstable(feature = "os_str_slice", issue = "118485")] - pub fn slice_encoded_bytes>(&self, range: R) -> &Self { - let encoded_bytes = self.as_encoded_bytes(); - let Range { start, end } = slice::range(range, ..encoded_bytes.len()); - - // `check_public_boundary` should panic if the index does not lie on an - // `OsStr` boundary as described above. It's possible to do this in an - // encoding-agnostic way, but details of the internal encoding might - // permit a more efficient implementation. - self.inner.check_public_boundary(start); - self.inner.check_public_boundary(end); - - // SAFETY: `slice::range` ensures that `start` and `end` are valid - let slice = unsafe { encoded_bytes.get_unchecked(start..end) }; - - // SAFETY: `slice` comes from `self` and we validated the boundaries - unsafe { Self::from_encoded_bytes_unchecked(slice) } - } - - /// Converts this string to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`OsStr::to_ascii_lowercase`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); - /// - /// s.make_ascii_lowercase(); - /// - /// assert_eq!("grÜße, jÜrgen ❤", s); - /// ``` - #[stable(feature = "osstring_ascii", since = "1.53.0")] - #[inline] - pub fn make_ascii_lowercase(&mut self) { - self.inner.make_ascii_lowercase() - } - - /// Converts this string to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`OsStr::to_ascii_uppercase`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let mut s = OsString::from("Grüße, Jürgen ❤"); - /// - /// s.make_ascii_uppercase(); - /// - /// assert_eq!("GRüßE, JüRGEN ❤", s); - /// ``` - #[stable(feature = "osstring_ascii", since = "1.53.0")] - #[inline] - pub fn make_ascii_uppercase(&mut self) { - self.inner.make_ascii_uppercase() - } - - /// Returns a copy of this string where each character is mapped to its - /// ASCII lower case equivalent. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To lowercase the value in-place, use [`OsStr::make_ascii_lowercase`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// let s = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); - /// ``` - #[must_use = "to lowercase the value in-place, use `make_ascii_lowercase`"] - #[stable(feature = "osstring_ascii", since = "1.53.0")] - pub fn to_ascii_lowercase(&self) -> OsString { - OsString::from_inner(self.inner.to_ascii_lowercase()) - } - - /// Returns a copy of this string where each character is mapped to its - /// ASCII upper case equivalent. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To uppercase the value in-place, use [`OsStr::make_ascii_uppercase`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// let s = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); - /// ``` - #[must_use = "to uppercase the value in-place, use `make_ascii_uppercase`"] - #[stable(feature = "osstring_ascii", since = "1.53.0")] - pub fn to_ascii_uppercase(&self) -> OsString { - OsString::from_inner(self.inner.to_ascii_uppercase()) - } - - /// Checks if all characters in this string are within the ASCII range. - /// - /// An empty string returns `true`. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// let ascii = OsString::from("hello!\n"); - /// let non_ascii = OsString::from("Grüße, Jürgen ❤"); - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// ``` - #[stable(feature = "osstring_ascii", since = "1.53.0")] - #[must_use] - #[inline] - pub fn is_ascii(&self) -> bool { - self.inner.is_ascii() - } - - /// Checks that two strings are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// - /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); - /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); - /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); - /// ``` - #[stable(feature = "osstring_ascii", since = "1.53.0")] - pub fn eq_ignore_ascii_case>(&self, other: S) -> bool { - self.inner.eq_ignore_ascii_case(&other.as_ref().inner) - } - - /// Returns an object that implements [`Display`] for safely printing an - /// [`OsStr`] that may contain non-Unicode data. This may perform lossy - /// conversion, depending on the platform. If you would like an - /// implementation which escapes the [`OsStr`] please use [`Debug`] - /// instead. - /// - /// [`Display`]: fmt::Display - /// [`Debug`]: fmt::Debug - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let s = OsStr::new("Hello, world!"); - /// println!("{}", s.display()); - /// ``` - #[stable(feature = "os_str_display", since = "1.87.0")] - #[must_use = "this does not display the `OsStr`; \ - it returns an object that can be displayed"] - #[inline] - pub fn display(&self) -> Display<'_> { - Display { os_str: self } - } - - /// Returns the same string as a string slice `&OsStr`. - /// - /// This method is redundant when used directly on `&OsStr`, but - /// it helps dereferencing other string-like types to string slices, - /// for example references to `Box` or `Arc`. - #[inline] - #[unstable(feature = "str_as_str", issue = "130366")] - pub const fn as_os_str(&self) -> &OsStr { - self - } -} - -#[stable(feature = "box_from_os_str", since = "1.17.0")] -impl From<&OsStr> for Box { - /// Copies the string into a newly allocated [Box]<[OsStr]>. - #[inline] - fn from(s: &OsStr) -> Box { - Box::clone_from_ref(s) - } -} - -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] -impl From<&mut OsStr> for Box { - /// Copies the string into a newly allocated [Box]<[OsStr]>. - #[inline] - fn from(s: &mut OsStr) -> Box { - Self::from(&*s) - } -} - -#[stable(feature = "box_from_cow", since = "1.45.0")] -impl From> for Box { - /// Converts a `Cow<'a, OsStr>` into a [Box]<[OsStr]>, - /// by copying the contents if they are borrowed. - #[inline] - fn from(cow: Cow<'_, OsStr>) -> Box { - match cow { - Cow::Borrowed(s) => Box::from(s), - Cow::Owned(s) => Box::from(s), - } - } -} - -#[stable(feature = "os_string_from_box", since = "1.18.0")] -impl From> for OsString { - /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or - /// allocating. - #[inline] - fn from(boxed: Box) -> OsString { - boxed.into_os_string() - } -} - -#[stable(feature = "box_from_os_string", since = "1.20.0")] -impl From for Box { - /// Converts an [`OsString`] into a [Box]<[OsStr]> without copying or allocating. - #[inline] - fn from(s: OsString) -> Box { - s.into_boxed_os_str() - } -} - -#[stable(feature = "more_box_slice_clone", since = "1.29.0")] -impl Clone for Box { - #[inline] - fn clone(&self) -> Self { - self.to_os_string().into_boxed_os_str() - } -} - -#[unstable(feature = "clone_to_uninit", issue = "126799")] -unsafe impl CloneToUninit for OsStr { - #[inline] - #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { - // SAFETY: we're just a transparent wrapper around a platform-specific Slice - unsafe { self.inner.clone_to_uninit(dst) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Arc { - /// Converts an [`OsString`] into an [Arc]<[OsStr]> by moving the [`OsString`] - /// data into a new [`Arc`] buffer. - #[inline] - fn from(s: OsString) -> Arc { - let arc = s.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&OsStr> for Arc { - /// Copies the string into a newly allocated [Arc]<[OsStr]>. - #[inline] - fn from(s: &OsStr) -> Arc { - let arc = s.inner.into_arc(); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] -impl From<&mut OsStr> for Arc { - /// Copies the string into a newly allocated [Arc]<[OsStr]>. - #[inline] - fn from(s: &mut OsStr) -> Arc { - Arc::from(&*s) - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From for Rc { - /// Converts an [`OsString`] into an [Rc]<[OsStr]> by moving the [`OsString`] - /// data into a new [`Rc`] buffer. - #[inline] - fn from(s: OsString) -> Rc { - let rc = s.inner.into_rc(); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_slice2", since = "1.24.0")] -impl From<&OsStr> for Rc { - /// Copies the string into a newly allocated [Rc]<[OsStr]>. - #[inline] - fn from(s: &OsStr) -> Rc { - let rc = s.inner.into_rc(); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } - } -} - -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] -impl From<&mut OsStr> for Rc { - /// Copies the string into a newly allocated [Rc]<[OsStr]>. - #[inline] - fn from(s: &mut OsStr) -> Rc { - Rc::from(&*s) - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From for Cow<'a, OsStr> { - /// Moves the string into a [`Cow::Owned`]. - #[inline] - fn from(s: OsString) -> Cow<'a, OsStr> { - Cow::Owned(s) - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { - /// Converts the string reference into a [`Cow::Borrowed`]. - #[inline] - fn from(s: &'a OsStr) -> Cow<'a, OsStr> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_osstr", since = "1.28.0")] -impl<'a> From<&'a OsString> for Cow<'a, OsStr> { - /// Converts the string reference into a [`Cow::Borrowed`]. - #[inline] - fn from(s: &'a OsString) -> Cow<'a, OsStr> { - Cow::Borrowed(s.as_os_str()) - } -} - -#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] -impl<'a> From> for OsString { - /// Converts a `Cow<'a, OsStr>` into an [`OsString`], - /// by copying the contents if they are borrowed. - #[inline] - fn from(s: Cow<'a, OsStr>) -> Self { - s.into_owned() - } -} - -#[stable(feature = "str_tryfrom_osstr_impl", since = "1.72.0")] -impl<'a> TryFrom<&'a OsStr> for &'a str { - type Error = crate::str::Utf8Error; - - /// Tries to convert an `&OsStr` to a `&str`. - /// - /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// let as_str = <&str>::try_from(os_str).unwrap(); - /// assert_eq!(as_str, "foo"); - /// ``` - fn try_from(value: &'a OsStr) -> Result { - value.inner.to_str() - } -} - -#[stable(feature = "box_default_extra", since = "1.17.0")] -impl Default for Box { - #[inline] - fn default() -> Box { - let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } - } -} - -#[stable(feature = "osstring_default", since = "1.9.0")] -impl Default for &OsStr { - /// Creates an empty `OsStr`. - #[inline] - fn default() -> Self { - OsStr::new("") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsStr { - #[inline] - fn eq(&self, other: &OsStr) -> bool { - self.as_encoded_bytes().eq(other.as_encoded_bytes()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for OsStr { - #[inline] - fn eq(&self, other: &str) -> bool { - *self == *OsStr::new(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for str { - #[inline] - fn eq(&self, other: &OsStr) -> bool { - *other == *OsStr::new(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for OsStr {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsStr { - #[inline] - fn partial_cmp(&self, other: &OsStr) -> Option { - self.as_encoded_bytes().partial_cmp(other.as_encoded_bytes()) - } - #[inline] - fn lt(&self, other: &OsStr) -> bool { - self.as_encoded_bytes().lt(other.as_encoded_bytes()) - } - #[inline] - fn le(&self, other: &OsStr) -> bool { - self.as_encoded_bytes().le(other.as_encoded_bytes()) - } - #[inline] - fn gt(&self, other: &OsStr) -> bool { - self.as_encoded_bytes().gt(other.as_encoded_bytes()) - } - #[inline] - fn ge(&self, other: &OsStr) -> bool { - self.as_encoded_bytes().ge(other.as_encoded_bytes()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for OsStr { - #[inline] - fn partial_cmp(&self, other: &str) -> Option { - self.partial_cmp(OsStr::new(other)) - } -} - -// FIXME (#19470): cannot provide PartialOrd for str until we -// have more flexible coherence rules. - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for OsStr { - #[inline] - fn cmp(&self, other: &OsStr) -> cmp::Ordering { - self.as_encoded_bytes().cmp(other.as_encoded_bytes()) - } -} - -macro_rules! impl_cmp { - ($lhs:ty, $rhs: ty) => { - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl PartialEq<$rhs> for $lhs { - #[inline] - fn eq(&self, other: &$rhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl PartialEq<$lhs> for $rhs { - #[inline] - fn eq(&self, other: &$lhs) -> bool { - ::eq(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl PartialOrd<$rhs> for $lhs { - #[inline] - fn partial_cmp(&self, other: &$rhs) -> Option { - ::partial_cmp(self, other) - } - } - - #[stable(feature = "cmp_os_str", since = "1.8.0")] - impl PartialOrd<$lhs> for $rhs { - #[inline] - fn partial_cmp(&self, other: &$lhs) -> Option { - ::partial_cmp(self, other) - } - } - }; -} - -impl_cmp!(OsString, OsStr); -impl_cmp!(OsString, &OsStr); -impl_cmp!(Cow<'_, OsStr>, OsStr); -impl_cmp!(Cow<'_, OsStr>, &OsStr); -impl_cmp!(Cow<'_, OsStr>, OsString); - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for OsStr { - #[inline] - fn hash(&self, state: &mut H) { - self.as_encoded_bytes().hash(state) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for OsStr { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.inner, formatter) - } -} - -/// Helper struct for safely printing an [`OsStr`] with [`format!`] and `{}`. -/// -/// An [`OsStr`] might contain non-Unicode data. This `struct` implements the -/// [`Display`] trait in a way that mitigates that. It is created by the -/// [`display`](OsStr::display) method on [`OsStr`]. This may perform lossy -/// conversion, depending on the platform. If you would like an implementation -/// which escapes the [`OsStr`] please use [`Debug`] instead. -/// -/// # Examples -/// -/// ``` -/// use std::ffi::OsStr; -/// -/// let s = OsStr::new("Hello, world!"); -/// println!("{}", s.display()); -/// ``` -/// -/// [`Display`]: fmt::Display -/// [`format!`]: crate::format -#[stable(feature = "os_str_display", since = "1.87.0")] -pub struct Display<'a> { - os_str: &'a OsStr, -} - -#[stable(feature = "os_str_display", since = "1.87.0")] -impl fmt::Debug for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.os_str, f) - } -} - -#[stable(feature = "os_str_display", since = "1.87.0")] -impl fmt::Display for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.os_str.inner, f) - } -} - -#[unstable(feature = "slice_concat_ext", issue = "27747")] -impl> alloc_crate::slice::Join<&OsStr> for [S] { - type Output = OsString; - - fn join(slice: &Self, sep: &OsStr) -> OsString { - let Some((first, suffix)) = slice.split_first() else { - return OsString::new(); - }; - let first_owned = first.borrow().to_owned(); - suffix.iter().fold(first_owned, |mut a, b| { - a.push(sep); - a.push(b.borrow()); - a - }) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for OsString { - #[inline] - fn borrow(&self) -> &OsStr { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for OsStr { - type Owned = OsString; - #[inline] - fn to_owned(&self) -> OsString { - self.to_os_string() - } - #[inline] - fn clone_into(&self, target: &mut OsString) { - self.inner.clone_into(&mut target.inner) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_convert", issue = "143773")] -impl const AsRef for OsStr { - #[inline] - fn as_ref(&self) -> &OsStr { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for OsString { - #[inline] - fn as_ref(&self) -> &OsStr { - self - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for str { - #[inline] - fn as_ref(&self) -> &OsStr { - OsStr::from_inner(Slice::from_str(self)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRef for String { - #[inline] - fn as_ref(&self) -> &OsStr { - (&**self).as_ref() - } -} - -impl FromInner for OsString { - #[inline] - fn from_inner(buf: Buf) -> OsString { - OsString { inner: buf } - } -} - -impl IntoInner for OsString { - #[inline] - fn into_inner(self) -> Buf { - self.inner - } -} - -impl AsInner for OsStr { - #[inline] - fn as_inner(&self) -> &Slice { - &self.inner - } -} - -#[stable(feature = "osstring_from_str", since = "1.45.0")] -impl FromStr for OsString { - type Err = core::convert::Infallible; - - #[inline] - fn from_str(s: &str) -> Result { - Ok(OsString::from(s)) - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl Extend for OsString { - #[inline] - fn extend>(&mut self, iter: T) { - for s in iter { - self.push(&s); - } - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl<'a> Extend<&'a OsStr> for OsString { - #[inline] - fn extend>(&mut self, iter: T) { - for s in iter { - self.push(s); - } - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl<'a> Extend> for OsString { - #[inline] - fn extend>>(&mut self, iter: T) { - for s in iter { - self.push(&s); - } - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl FromIterator for OsString { - #[inline] - fn from_iter>(iter: I) -> Self { - let mut iterator = iter.into_iter(); - - // Because we're iterating over `OsString`s, we can avoid at least - // one allocation by getting the first string from the iterator - // and appending to it all the subsequent strings. - match iterator.next() { - None => OsString::new(), - Some(mut buf) => { - buf.extend(iterator); - buf - } - } - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl<'a> FromIterator<&'a OsStr> for OsString { - #[inline] - fn from_iter>(iter: I) -> Self { - let mut buf = Self::new(); - for s in iter { - buf.push(s); - } - buf - } -} - -#[stable(feature = "osstring_extend", since = "1.52.0")] -impl<'a> FromIterator> for OsString { - #[inline] - fn from_iter>>(iter: I) -> Self { - let mut iterator = iter.into_iter(); - - // Because we're iterating over `OsString`s, we can avoid at least - // one allocation by getting the first owned string from the iterator - // and appending to it all the subsequent strings. - match iterator.next() { - None => OsString::new(), - Some(Cow::Owned(mut buf)) => { - buf.extend(iterator); - buf - } - Some(Cow::Borrowed(buf)) => { - let mut buf = OsString::from(buf); - buf.extend(iterator); - buf - } - } - } -} diff --git a/crates/std/src/ffi/os_str/tests.rs b/crates/std/src/ffi/os_str/tests.rs deleted file mode 100644 index 3474f0a..0000000 --- a/crates/std/src/ffi/os_str/tests.rs +++ /dev/null @@ -1,311 +0,0 @@ -use super::*; -use crate::mem::MaybeUninit; -use crate::ptr; - -#[test] -fn test_os_string_with_capacity() { - let os_string = OsString::with_capacity(0); - assert_eq!(0, os_string.inner.into_inner().capacity()); - - let os_string = OsString::with_capacity(10); - assert_eq!(10, os_string.inner.into_inner().capacity()); - - let mut os_string = OsString::with_capacity(0); - os_string.push("abc"); - assert!(os_string.inner.into_inner().capacity() >= 3); -} - -#[test] -fn test_os_string_clear() { - let mut os_string = OsString::from("abc"); - assert_eq!(3, os_string.inner.as_inner().len()); - - os_string.clear(); - assert_eq!(&os_string, ""); - assert_eq!(0, os_string.inner.as_inner().len()); -} - -#[test] -fn test_os_string_leak() { - let os_string = OsString::from("have a cake"); - let (len, cap) = (os_string.len(), os_string.capacity()); - let leaked = os_string.leak(); - assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); - unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) } -} - -#[test] -fn test_os_string_capacity() { - let os_string = OsString::with_capacity(0); - assert_eq!(0, os_string.capacity()); - - let os_string = OsString::with_capacity(10); - assert_eq!(10, os_string.capacity()); - - let mut os_string = OsString::with_capacity(0); - os_string.push("abc"); - assert!(os_string.capacity() >= 3); -} - -#[test] -fn test_os_string_reserve() { - let mut os_string = OsString::new(); - assert_eq!(os_string.capacity(), 0); - - os_string.reserve(2); - assert!(os_string.capacity() >= 2); - - for _ in 0..16 { - os_string.push("a"); - } - - assert!(os_string.capacity() >= 16); - os_string.reserve(16); - assert!(os_string.capacity() >= 32); - - os_string.push("a"); - - os_string.reserve(16); - assert!(os_string.capacity() >= 33) -} - -#[test] -fn test_os_string_reserve_exact() { - let mut os_string = OsString::new(); - assert_eq!(os_string.capacity(), 0); - - os_string.reserve_exact(2); - assert!(os_string.capacity() >= 2); - - for _ in 0..16 { - os_string.push("a"); - } - - assert!(os_string.capacity() >= 16); - os_string.reserve_exact(16); - assert!(os_string.capacity() >= 32); - - os_string.push("a"); - - os_string.reserve_exact(16); - assert!(os_string.capacity() >= 33) -} - -#[test] -fn test_os_string_join() { - let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")]; - assert_eq!("hello", strings[..1].join(OsStr::new(" "))); - assert_eq!("hello dear world", strings.join(OsStr::new(" "))); - assert_eq!("hellodearworld", strings.join(OsStr::new(""))); - assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n "))); - - assert_eq!("dear world", strings[1..].join(&OsString::from(" "))); - - let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")]; - assert_eq!("a b c", strings_abc.join(OsStr::new(" "))); -} - -#[test] -fn test_os_string_default() { - let os_string: OsString = Default::default(); - assert_eq!("", &os_string); -} - -#[test] -fn test_os_str_is_empty() { - let mut os_string = OsString::new(); - assert!(os_string.is_empty()); - - os_string.push("abc"); - assert!(!os_string.is_empty()); - - os_string.clear(); - assert!(os_string.is_empty()); -} - -#[test] -fn test_os_str_len() { - let mut os_string = OsString::new(); - assert_eq!(0, os_string.len()); - - os_string.push("abc"); - assert_eq!(3, os_string.len()); - - os_string.clear(); - assert_eq!(0, os_string.len()); -} - -#[test] -fn test_os_str_default() { - let os_str: &OsStr = Default::default(); - assert_eq!("", os_str); -} - -#[test] -fn into_boxed() { - let orig = "Hello, world!"; - let os_str = OsStr::new(orig); - let boxed: Box = Box::from(os_str); - let os_string = os_str.to_owned().into_boxed_os_str().into_os_string(); - assert_eq!(os_str, &*boxed); - assert_eq!(&*boxed, &*os_string); - assert_eq!(&*os_string, os_str); -} - -#[test] -fn boxed_default() { - let boxed = >::default(); - assert!(boxed.is_empty()); -} - -#[test] -fn test_os_str_clone_into() { - let mut os_string = OsString::with_capacity(123); - os_string.push("hello"); - let os_str = OsStr::new("bonjour"); - os_str.clone_into(&mut os_string); - assert_eq!(os_str, os_string); - assert!(os_string.capacity() >= 123); -} - -#[test] -fn into_rc() { - let orig = "Hello, world!"; - let os_str = OsStr::new(orig); - let rc: Rc = Rc::from(os_str); - let arc: Arc = Arc::from(os_str); - - assert_eq!(&*rc, os_str); - assert_eq!(&*arc, os_str); - - let rc2: Rc = Rc::from(os_str.to_owned()); - let arc2: Arc = Arc::from(os_str.to_owned()); - - assert_eq!(&*rc2, os_str); - assert_eq!(&*arc2, os_str); -} - -#[test] -fn slice_encoded_bytes() { - let os_str = OsStr::new("123θგ🦀"); - // ASCII - let digits = os_str.slice_encoded_bytes(..3); - assert_eq!(digits, "123"); - let three = os_str.slice_encoded_bytes(2..3); - assert_eq!(three, "3"); - // 2-byte UTF-8 - let theta = os_str.slice_encoded_bytes(3..5); - assert_eq!(theta, "θ"); - // 3-byte UTF-8 - let gani = os_str.slice_encoded_bytes(5..8); - assert_eq!(gani, "გ"); - // 4-byte UTF-8 - let crab = os_str.slice_encoded_bytes(8..); - assert_eq!(crab, "🦀"); -} - -#[test] -#[should_panic] -fn slice_out_of_bounds() { - let crab = OsStr::new("🦀"); - let _ = crab.slice_encoded_bytes(..5); -} - -#[test] -#[should_panic] -fn slice_mid_char() { - let crab = OsStr::new("🦀"); - let _ = crab.slice_encoded_bytes(..2); -} - -#[cfg(unix)] -#[test] -#[should_panic(expected = "byte index 1 is not an OsStr boundary")] -fn slice_invalid_data() { - use crate::os::unix::ffi::OsStrExt; - - let os_string = OsStr::from_bytes(b"\xFF\xFF"); - let _ = os_string.slice_encoded_bytes(1..); -} - -#[cfg(unix)] -#[test] -#[should_panic(expected = "byte index 1 is not an OsStr boundary")] -fn slice_partial_utf8() { - use crate::os::unix::ffi::{OsStrExt, OsStringExt}; - - let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]); - let mut os_string = OsString::from_vec(vec![0xFF]); - os_string.push(part_crab); - let _ = os_string.slice_encoded_bytes(1..); -} - -#[cfg(unix)] -#[test] -fn slice_invalid_edge() { - use crate::os::unix::ffi::{OsStrExt, OsStringExt}; - - let os_string = OsStr::from_bytes(b"a\xFFa"); - assert_eq!(os_string.slice_encoded_bytes(..1), "a"); - assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa")); - assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF")); - assert_eq!(os_string.slice_encoded_bytes(2..), "a"); - - let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]); - assert_eq!(os_string.slice_encoded_bytes(..3), "abc"); - assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6")); - - let mut os_string = OsString::from_vec(vec![0xFF]); - os_string.push("🦀"); - assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF")); - assert_eq!(os_string.slice_encoded_bytes(1..), "🦀"); -} - -#[cfg(windows)] -#[test] -#[should_panic(expected = "byte index 3 lies between surrogate codepoints")] -fn slice_between_surrogates() { - use crate::os::windows::ffi::OsStringExt; - - let os_string = OsString::from_wide(&[0xD800, 0xD800]); - assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]); - let _ = os_string.slice_encoded_bytes(..3); -} - -#[cfg(windows)] -#[test] -fn slice_surrogate_edge() { - use crate::os::windows::ffi::OsStringExt; - - let surrogate = OsString::from_wide(&[0xD800]); - let mut pre_crab = surrogate.clone(); - pre_crab.push("🦀"); - assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate); - assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀"); - - let mut post_crab = OsString::from("🦀"); - post_crab.push(&surrogate); - assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀"); - assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate); -} - -#[test] -fn clone_to_uninit() { - let a = OsStr::new("hello.txt"); - - let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; - unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); - - let mut b: Box = OsStr::new("world.exe").into(); - assert_eq!(size_of_val::(a), size_of_val::(&b)); - assert_ne!(a, &*b); - unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b).cast()) }; - assert_eq!(a, &*b); -} - -#[test] -fn debug() { - let s = "'single quotes'"; - assert_eq!(format!("{:?}", OsStr::new(s)), format!("{:?}", s)); -} diff --git a/crates/std/src/fs/tests.rs b/crates/std/src/fs/tests.rs deleted file mode 100644 index 42f3ccc..0000000 --- a/crates/std/src/fs/tests.rs +++ /dev/null @@ -1,2548 +0,0 @@ -use rand::RngCore; - -#[cfg(not(miri))] -use super::Dir; -use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError}; -#[cfg(not(miri))] -use crate::io; -use crate::io::prelude::*; -use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; -use crate::mem::MaybeUninit; -#[cfg(unix)] -use crate::os::unix::fs::symlink as symlink_dir; -#[cfg(unix)] -use crate::os::unix::fs::symlink as symlink_file; -#[cfg(unix)] -use crate::os::unix::fs::symlink as junction_point; -#[cfg(windows)] -use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; -use crate::path::Path; -use crate::sync::Arc; -use crate::test_helpers::{TempDir, tmpdir}; -use crate::time::{Duration, Instant, SystemTime}; -use crate::{assert_matches, env, str, thread}; - -macro_rules! check { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("{} failed with: {e}", stringify!($e)), - } - }; -} - -#[cfg(windows)] -macro_rules! error { - ($e:expr, $s:expr) => { - match $e { - Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => { - assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s) - } - } - }; -} - -#[cfg(unix)] -macro_rules! error { - ($e:expr, $s:expr) => { - error_contains!($e, $s) - }; -} - -macro_rules! error_contains { - ($e:expr, $s:expr) => { - match $e { - Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), - Err(ref err) => { - assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s) - } - } - }; -} - -// Several test fail on windows if the user does not have permission to -// create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of -// disabling these test on Windows, use this function to test whether we -// have permission, and return otherwise. This way, we still don't run these -// tests most of the time, but at least we do if the user has the right -// permissions. -pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { - if cfg!(not(windows)) || env::var_os("CI").is_some() { - return true; - } - let link = tmpdir.join("some_hopefully_unique_link_name"); - - match symlink_file(r"nonexisting_target", link) { - // ERROR_PRIVILEGE_NOT_HELD = 1314 - Err(ref err) if err.raw_os_error() == Some(1314) => false, - Ok(_) | Err(_) => true, - } -} - -#[test] -fn file_test_io_smoke_test() { - let message = "it's alright. have a good time"; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test.txt"); - { - let mut write_stream = check!(File::create(filename)); - check!(write_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - let mut read_buf = [0; 1028]; - let read_str = match check!(read_stream.read(&mut read_buf)) { - 0 => panic!("shouldn't happen"), - n => str::from_utf8(&read_buf[..n]).unwrap().to_string(), - }; - assert_eq!(read_str, message); - } - check!(fs::remove_file(filename)); -} - -#[test] -fn invalid_path_raises() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_that_does_not_exist.txt"); - let result = File::open(filename); - - #[cfg(all(unix, not(target_os = "vxworks")))] - error!(result, "No such file or directory"); - #[cfg(target_os = "vxworks")] - error!(result, "no such file or directory"); - #[cfg(windows)] - error!(result, 2); // ERROR_FILE_NOT_FOUND -} - -#[test] -fn file_test_iounlinking_invalid_path_should_raise_condition() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); - - let result = fs::remove_file(filename); - - #[cfg(all(unix, not(target_os = "vxworks")))] - error!(result, "No such file or directory"); - #[cfg(target_os = "vxworks")] - error!(result, "no such file or directory"); - #[cfg(windows)] - error!(result, 2); // ERROR_FILE_NOT_FOUND -} - -#[test] -fn file_test_io_non_positional_read() { - let message: &str = "ten-four"; - let mut read_mem = [0; 8]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - { - let read_buf = &mut read_mem[0..4]; - check!(read_stream.read(read_buf)); - } - { - let read_buf = &mut read_mem[4..8]; - check!(read_stream.read(read_buf)); - } - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, message); -} - -#[test] -fn file_test_io_seek_and_tell_smoke_test() { - let message = "ten-four"; - let mut read_mem = [0; char::MAX_LEN_UTF8]; - let set_cursor = 4 as u64; - let tell_pos_pre_read; - let tell_pos_post_read; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(message.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - check!(read_stream.seek(SeekFrom::Start(set_cursor))); - tell_pos_pre_read = check!(read_stream.stream_position()); - check!(read_stream.read(&mut read_mem)); - tell_pos_post_read = check!(read_stream.stream_position()); - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert_eq!(read_str, &message[4..8]); - assert_eq!(tell_pos_pre_read, set_cursor); - assert_eq!(tell_pos_post_read, message.len() as u64); -} - -#[test] -fn file_test_io_seek_and_write() { - let initial_msg = "food-is-yummy"; - let overwrite_msg = "-the-bar!!"; - let final_msg = "foo-the-bar!!"; - let seek_idx = 3; - let mut read_mem = [0; 13]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(initial_msg.as_bytes())); - check!(rw_stream.seek(SeekFrom::Start(seek_idx))); - check!(rw_stream.write(overwrite_msg.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - check!(read_stream.read(&mut read_mem)); - } - check!(fs::remove_file(filename)); - let read_str = str::from_utf8(&read_mem).unwrap(); - assert!(read_str == final_msg); -} - -#[test] -#[cfg_attr( - not(any( - windows, - target_os = "aix", - target_os = "cygwin", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_vendor = "apple", - )), - should_panic -)] -fn file_lock_multiple_shared() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_multiple_shared_test.txt"); - let f1 = check!(File::create(filename)); - let f2 = check!(OpenOptions::new().write(true).open(filename)); - - // Check that we can acquire concurrent shared locks - check!(f1.lock_shared()); - check!(f2.lock_shared()); - check!(f1.unlock()); - check!(f2.unlock()); - check!(f1.try_lock_shared()); - check!(f2.try_lock_shared()); -} - -#[test] -#[cfg_attr( - not(any( - windows, - target_os = "aix", - target_os = "cygwin", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_vendor = "apple", - )), - should_panic -)] -fn file_lock_blocking() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_blocking_test.txt"); - let f1 = check!(File::create(filename)); - let f2 = check!(OpenOptions::new().write(true).open(filename)); - - // Check that shared locks block exclusive locks - check!(f1.lock_shared()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - check!(f1.unlock()); - - // Check that exclusive locks block shared locks - check!(f1.lock()); - assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); -} - -#[test] -#[cfg_attr( - not(any( - windows, - target_os = "aix", - target_os = "cygwin", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_vendor = "apple", - )), - should_panic -)] -fn file_lock_drop() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_dup_test.txt"); - let f1 = check!(File::create(filename)); - let f2 = check!(OpenOptions::new().write(true).open(filename)); - - // Check that locks are released when the File is dropped - check!(f1.lock_shared()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - drop(f1); - check!(f2.try_lock()); -} - -#[test] -#[cfg_attr( - not(any( - windows, - target_os = "aix", - target_os = "cygwin", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_vendor = "apple", - )), - should_panic -)] -fn file_lock_dup() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_dup_test.txt"); - let f1 = check!(File::create(filename)); - let f2 = check!(OpenOptions::new().write(true).open(filename)); - - // Check that locks are not dropped if the File has been cloned - check!(f1.lock_shared()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - let cloned = check!(f1.try_clone()); - drop(f1); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - drop(cloned) -} - -#[test] -#[cfg(windows)] -fn file_lock_double_unlock() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_double_unlock_test.txt"); - let f1 = check!(File::create(filename)); - let f2 = check!(OpenOptions::new().write(true).open(filename)); - - // On Windows a file handle may acquire both a shared and exclusive lock. - // Check that both are released by unlock() - check!(f1.lock()); - check!(f1.lock_shared()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - check!(f1.unlock()); - check!(f2.try_lock()); -} - -#[test] -#[cfg(windows)] -fn file_lock_blocking_async() { - use crate::thread::{sleep, spawn}; - const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; - - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_blocking_async.txt"); - let f1 = check!(File::create(filename)); - let f2 = - check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); - - check!(f1.lock()); - - // Ensure that lock() is synchronous when the file is opened for asynchronous IO - let t = spawn(move || { - check!(f2.lock()); - }); - sleep(Duration::from_secs(1)); - assert!(!t.is_finished()); - check!(f1.unlock()); - t.join().unwrap(); - - // Ensure that lock_shared() is synchronous when the file is opened for asynchronous IO - let f2 = - check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); - check!(f1.lock()); - - // Ensure that lock() is synchronous when the file is opened for asynchronous IO - let t = spawn(move || { - check!(f2.lock_shared()); - }); - sleep(Duration::from_secs(1)); - assert!(!t.is_finished()); - check!(f1.unlock()); - t.join().unwrap(); -} - -#[test] -#[cfg(windows)] -fn file_try_lock_async() { - const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; - - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_try_lock_async.txt"); - let f1 = check!(File::create(filename)); - let f2 = - check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); - - // Check that shared locks block exclusive locks - check!(f1.lock_shared()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - check!(f1.unlock()); - - // Check that exclusive locks block all locks - check!(f1.lock()); - assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); - assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); -} - -#[test] -fn file_test_io_seek_shakedown() { - // 01234567890123 - let initial_msg = "qwer-asdf-zxcv"; - let chunk_one: &str = "qwer"; - let chunk_two: &str = "asdf"; - let chunk_three: &str = "zxcv"; - let mut read_mem = [0; char::MAX_LEN_UTF8]; - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); - { - let mut rw_stream = check!(File::create(filename)); - check!(rw_stream.write(initial_msg.as_bytes())); - } - { - let mut read_stream = check!(File::open(filename)); - - check!(read_stream.seek(SeekFrom::End(-4))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three); - - check!(read_stream.seek(SeekFrom::Current(-9))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two); - - check!(read_stream.seek(SeekFrom::Start(0))); - check!(read_stream.read(&mut read_mem)); - assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one); - } - check!(fs::remove_file(filename)); -} - -#[test] -fn file_test_io_eof() { - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); - let mut buf = [0; 256]; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.read(&mut buf)), 0); - assert_eq!(check!(rw.read(&mut buf)), 0); - } - check!(fs::remove_file(&filename)); -} - -#[test] -#[cfg(unix)] -fn file_test_io_read_write_at() { - use crate::os::unix::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); - let mut buf = [0; 256]; - let write1 = "asdf"; - let write2 = "qwer-"; - let write3 = "-zxcv"; - let content = "qwer-asdf-zxcv"; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.stream_position()), 0); - assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.stream_position()), 0); - assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); - assert_eq!(check!(rw.stream_position()), 0); - assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.stream_position()), 5); - assert_eq!(check!(rw.read(&mut buf)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.stream_position()), 9); - assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.stream_position()), 9); - assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.stream_position()), 9); - } - { - let mut read = check!(File::open(&filename)); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 0); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 9); - assert_eq!(check!(read.read(&mut buf)), write3.len()); - assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.read_at(&mut buf, 14)), 0); - assert_eq!(check!(read.read_at(&mut buf, 15)), 0); - assert_eq!(check!(read.stream_position()), 14); - } - check!(fs::remove_file(&filename)); -} - -#[test] -#[cfg(unix)] -fn test_read_buf_at() { - use crate::os::unix::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt"); - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut file = check!(oo.open(&filename)); - check!(file.write_all(b"0123456789")); - } - { - let mut file = check!(File::open(&filename)); - let mut buf: [MaybeUninit; 5] = [MaybeUninit::uninit(); 5]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - - // Fill entire buffer with potentially short reads - while buf.unfilled().capacity() > 0 { - let len = buf.len(); - check!(file.read_buf_at(buf.unfilled(), 2 + len as u64)); - assert!(!buf.filled().is_empty()); - assert!(b"23456".starts_with(buf.filled())); - assert_eq!(check!(file.stream_position()), 0); - } - assert_eq!(buf.filled(), b"23456"); - - // Already full - check!(file.read_buf_at(buf.unfilled(), 3)); - check!(file.read_buf_at(buf.unfilled(), 10)); - assert_eq!(buf.filled(), b"23456"); - assert_eq!(check!(file.stream_position()), 0); - - // Read past eof is noop - check!(file.read_buf_at(buf.clear().unfilled(), 10)); - assert_eq!(buf.filled(), b""); - check!(file.read_buf_at(buf.clear().unfilled(), 11)); - assert_eq!(buf.filled(), b""); - assert_eq!(check!(file.stream_position()), 0); - } - check!(fs::remove_file(&filename)); -} - -#[test] -#[cfg(unix)] -fn test_read_buf_exact_at() { - use crate::os::unix::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_read_buf_exact_at.txt"); - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut file = check!(oo.open(&filename)); - check!(file.write_all(b"0123456789")); - } - { - let mut file = check!(File::open(&filename)); - let mut buf: [MaybeUninit; 5] = [MaybeUninit::uninit(); 5]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - - // Exact read - check!(file.read_buf_exact_at(buf.unfilled(), 2)); - assert_eq!(buf.filled(), b"23456"); - assert_eq!(check!(file.stream_position()), 0); - - // Already full - check!(file.read_buf_exact_at(buf.unfilled(), 3)); - check!(file.read_buf_exact_at(buf.unfilled(), 10)); - assert_eq!(buf.filled(), b"23456"); - assert_eq!(check!(file.stream_position()), 0); - - // Non-empty exact read past eof fails - let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err(); - assert_eq!(err.kind(), ErrorKind::UnexpectedEof); - assert_eq!(check!(file.stream_position()), 0); - } - check!(fs::remove_file(&filename)); -} - -#[test] -#[cfg(unix)] -fn set_get_unix_permissions() { - use crate::os::unix::fs::PermissionsExt; - - let tmpdir = tmpdir(); - let filename = &tmpdir.join("set_get_unix_permissions"); - check!(fs::create_dir(filename)); - let mask = 0o7777; - - check!(fs::set_permissions(filename, fs::Permissions::from_mode(0))); - let metadata0 = check!(fs::metadata(filename)); - assert_eq!(mask & metadata0.permissions().mode(), 0); - - check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777))); - let metadata1 = check!(fs::metadata(filename)); - #[cfg(all(unix, not(target_os = "vxworks")))] - assert_eq!(mask & metadata1.permissions().mode(), 0o1777); - #[cfg(target_os = "vxworks")] - assert_eq!(mask & metadata1.permissions().mode(), 0o0777); -} - -#[test] -#[cfg(windows)] -fn file_test_io_seek_read_write() { - use crate::os::windows::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); - let mut buf = [0; 256]; - let write1 = "asdf"; - let write2 = "qwer-"; - let write3 = "-zxcv"; - let content = "qwer-asdf-zxcv"; - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut rw = check!(oo.open(&filename)); - assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.stream_position()), 9); - assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.stream_position()), 9); - assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); - assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.stream_position()), 5); - assert_eq!(check!(rw.read(&mut buf)), write1.len()); - assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.stream_position()), 9); - assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); - assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.stream_position()), 5); - assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.stream_position()), 14); - } - { - let mut read = check!(File::open(&filename)); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); - assert_eq!(check!(read.read(&mut buf)), write3.len()); - assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); - assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.stream_position()), 14); - assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); - assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); - } - check!(fs::remove_file(&filename)); -} - -#[test] -#[cfg(windows)] -fn test_seek_read_buf() { - use crate::os::windows::fs::FileExt; - - let tmpdir = tmpdir(); - let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt"); - { - let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); - let mut file = check!(oo.open(&filename)); - check!(file.write_all(b"0123456789")); - } - { - let mut file = check!(File::open(&filename)); - let mut buf: [MaybeUninit; 1] = [MaybeUninit::uninit()]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - - // Seek read - check!(file.seek_read_buf(buf.unfilled(), 8)); - assert_eq!(buf.filled(), b"8"); - assert_eq!(check!(file.stream_position()), 9); - - // Empty seek read - check!(file.seek_read_buf(buf.unfilled(), 0)); - assert_eq!(buf.filled(), b"8"); - - // Seek read past eof - check!(file.seek_read_buf(buf.clear().unfilled(), 10)); - assert_eq!(buf.filled(), b""); - } - check!(fs::remove_file(&filename)); -} - -#[test] -fn file_test_read_buf() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("test"); - check!(fs::write(filename, &[1, 2, 3, 4])); - - let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - let mut file = check!(File::open(filename)); - check!(file.read_buf(buf.unfilled())); - assert_eq!(buf.filled(), &[1, 2, 3, 4]); - // File::read_buf should omit buffer initialization. - assert_eq!(buf.init_len(), 4); - - check!(fs::remove_file(filename)); -} - -#[test] -fn file_test_stat_is_correct_on_is_file() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); - { - let mut opts = OpenOptions::new(); - let mut fs = check!(opts.read(true).write(true).create(true).open(filename)); - let msg = "hw"; - fs.write(msg.as_bytes()).unwrap(); - - let fstat_res = check!(fs.metadata()); - assert!(fstat_res.is_file()); - } - let stat_res_fn = check!(fs::metadata(filename)); - assert!(stat_res_fn.is_file()); - let stat_res_meth = check!(filename.metadata()); - assert!(stat_res_meth.is_file()); - check!(fs::remove_file(filename)); -} - -#[test] -fn file_test_stat_is_correct_on_is_dir() { - let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_stat_correct_on_is_dir"); - check!(fs::create_dir(filename)); - let stat_res_fn = check!(fs::metadata(filename)); - assert!(stat_res_fn.is_dir()); - let stat_res_meth = check!(filename.metadata()); - assert!(stat_res_meth.is_dir()); - check!(fs::remove_dir(filename)); -} - -#[test] -fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("fileinfo_false_on_dir"); - check!(fs::create_dir(dir)); - assert!(!dir.is_file()); - check!(fs::remove_dir(dir)); -} - -#[test] -fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - let tmpdir = tmpdir(); - let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); - check!(check!(File::create(file)).write(b"foo")); - assert!(file.exists()); - check!(fs::remove_file(file)); - assert!(!file.exists()); -} - -#[test] -fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("before_and_after_dir"); - assert!(!dir.exists()); - check!(fs::create_dir(dir)); - assert!(dir.exists()); - assert!(dir.is_dir()); - check!(fs::remove_dir(dir)); - assert!(!dir.exists()); -} - -#[test] -fn file_test_directoryinfo_readdir() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("di_readdir"); - check!(fs::create_dir(dir)); - let prefix = "foo"; - for n in 0..3 { - let f = dir.join(&format!("{n}.txt")); - let mut w = check!(File::create(&f)); - let msg_str = format!("{}{}", prefix, n.to_string()); - let msg = msg_str.as_bytes(); - check!(w.write(msg)); - } - let files = check!(fs::read_dir(dir)); - let mut mem = [0; char::MAX_LEN_UTF8]; - for f in files { - let f = f.unwrap().path(); - { - let n = f.file_stem().unwrap(); - check!(check!(File::open(&f)).read(&mut mem)); - let read_str = str::from_utf8(&mem).unwrap(); - let expected = format!("{}{}", prefix, n.to_str().unwrap()); - assert_eq!(expected, read_str); - } - check!(fs::remove_file(&f)); - } - check!(fs::remove_dir(dir)); -} - -#[test] -fn file_create_new_already_exists_error() { - let tmpdir = tmpdir(); - let file = &tmpdir.join("file_create_new_error_exists"); - check!(fs::File::create(file)); - let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err(); - assert_eq!(e.kind(), ErrorKind::AlreadyExists); -} - -#[test] -fn mkdir_path_already_exists_error() { - let tmpdir = tmpdir(); - let dir = &tmpdir.join("mkdir_error_twice"); - check!(fs::create_dir(dir)); - let e = fs::create_dir(dir).unwrap_err(); - assert_eq!(e.kind(), ErrorKind::AlreadyExists); -} - -#[test] -fn recursive_mkdir() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1/d2"); - check!(fs::create_dir_all(&dir)); - assert!(dir.is_dir()) -} - -#[test] -fn recursive_mkdir_failure() { - let tmpdir = tmpdir(); - let dir = tmpdir.join("d1"); - let file = dir.join("f1"); - - check!(fs::create_dir_all(&dir)); - check!(File::create(&file)); - - let result = fs::create_dir_all(&file); - - assert!(result.is_err()); -} - -#[test] -fn concurrent_recursive_mkdir() { - for _ in 0..100 { - let dir = tmpdir(); - let mut dir = dir.join("a"); - for _ in 0..40 { - dir = dir.join("a"); - } - let mut join = vec![]; - for _ in 0..8 { - let dir = dir.clone(); - join.push(thread::spawn(move || { - check!(fs::create_dir_all(&dir)); - })) - } - - // No `Display` on result of `join()` - join.drain(..).map(|join| join.join().unwrap()).count(); - } -} - -#[test] -fn recursive_mkdir_slash() { - check!(fs::create_dir_all(Path::new("/"))); -} - -#[test] -fn recursive_mkdir_dot() { - check!(fs::create_dir_all(Path::new("."))); -} - -#[test] -fn recursive_mkdir_empty() { - check!(fs::create_dir_all(Path::new(""))); -} - -#[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] -fn recursive_rmdir() { - let tmpdir = tmpdir(); - let d1 = tmpdir.join("d1"); - let dt = d1.join("t"); - let dtt = dt.join("t"); - let d2 = tmpdir.join("d2"); - let canary = d2.join("do_not_delete"); - check!(fs::create_dir_all(&dtt)); - check!(fs::create_dir_all(&d2)); - check!(check!(File::create(&canary)).write(b"foo")); - check!(junction_point(&d2, &dt.join("d2"))); - let _ = symlink_file(&canary, &d1.join("canary")); - check!(fs::remove_dir_all(&d1)); - - assert!(!d1.is_dir()); - assert!(canary.exists()); -} - -#[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] -fn recursive_rmdir_of_symlink() { - // test we do not recursively delete a symlink but only dirs. - let tmpdir = tmpdir(); - let link = tmpdir.join("d1"); - let dir = tmpdir.join("d2"); - let canary = dir.join("do_not_delete"); - check!(fs::create_dir_all(&dir)); - check!(check!(File::create(&canary)).write(b"foo")); - check!(junction_point(&dir, &link)); - check!(fs::remove_dir_all(&link)); - - assert!(!link.is_dir()); - assert!(canary.exists()); -} - -#[test] -fn recursive_rmdir_of_file_fails() { - // test we do not delete a directly specified file. - let tmpdir = tmpdir(); - let canary = tmpdir.join("do_not_delete"); - check!(check!(File::create(&canary)).write(b"foo")); - let result = fs::remove_dir_all(&canary); - #[cfg(unix)] - error!(result, "Not a directory"); - #[cfg(windows)] - error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid. - assert!(result.is_err()); - assert!(canary.exists()); -} - -#[test] -// only Windows makes a distinction between file and directory symlinks. -#[cfg(windows)] -fn recursive_rmdir_of_file_symlink() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let f1 = tmpdir.join("f1"); - let f2 = tmpdir.join("f2"); - check!(check!(File::create(&f1)).write(b"foo")); - check!(symlink_file(&f1, &f2)); - match fs::remove_dir_all(&f2) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } -} - -#[test] -#[ignore] // takes too much time -fn recursive_rmdir_toctou() { - // Test for time-of-check to time-of-use issues. - // - // Scenario: - // The attacker wants to get directory contents deleted, to which they do not have access. - // They have a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a - // directory they control, e.g. in their home directory. - // - // The POC sets up the `attack_dest/attack_file` which the attacker wants to have deleted. - // The attacker repeatedly creates a directory and replaces it with a symlink from - // `victim_del` to `attack_dest` while the victim code calls `std::fs::remove_dir_all()` - // on `victim_del`. After a few seconds the attack has succeeded and - // `attack_dest/attack_file` is deleted. - let tmpdir = tmpdir(); - let victim_del_path = tmpdir.join("victim_del"); - let victim_del_path_clone = victim_del_path.clone(); - - // setup dest - let attack_dest_dir = tmpdir.join("attack_dest"); - let attack_dest_dir = attack_dest_dir.as_path(); - fs::create_dir(attack_dest_dir).unwrap(); - let attack_dest_file = tmpdir.join("attack_dest/attack_file"); - File::create(&attack_dest_file).unwrap(); - - let drop_canary_arc = Arc::new(()); - let drop_canary_weak = Arc::downgrade(&drop_canary_arc); - - eprintln!("x: {victim_del_path:?}"); - - // victim just continuously removes `victim_del` - thread::spawn(move || { - while drop_canary_weak.upgrade().is_some() { - let _ = fs::remove_dir_all(&victim_del_path_clone); - } - }); - - // attacker (could of course be in a separate process) - let start_time = Instant::now(); - while Instant::now().duration_since(start_time) < Duration::from_secs(1000) { - if !attack_dest_file.exists() { - panic!( - "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.", - Instant::now().duration_since(start_time) - ); - } - let _ = fs::create_dir(&victim_del_path); - let _ = fs::remove_dir(&victim_del_path); - let _ = symlink_dir(attack_dest_dir, &victim_del_path); - } -} - -#[test] -fn unicode_path_is_dir() { - assert!(Path::new(".").is_dir()); - assert!(!Path::new("test/stdtest/fs.rs").is_dir()); - - let tmpdir = tmpdir(); - - let mut dirpath = tmpdir.path().to_path_buf(); - dirpath.push("test-가一ー你好"); - check!(fs::create_dir(&dirpath)); - assert!(dirpath.is_dir()); - - let mut filepath = dirpath; - filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); - check!(File::create(&filepath)); // ignore return; touch only - assert!(!filepath.is_dir()); - assert!(filepath.exists()); -} - -#[test] -fn unicode_path_exists() { - assert!(Path::new(".").exists()); - assert!(!Path::new("test/nonexistent-bogus-path").exists()); - - let tmpdir = tmpdir(); - let unicode = tmpdir.path(); - let unicode = unicode.join("test-각丁ー再见"); - check!(fs::create_dir(&unicode)); - assert!(unicode.exists()); - assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); -} - -#[test] -fn copy_file_does_not_exist() { - let from = Path::new("test/nonexistent-bogus-path"); - let to = Path::new("test/other-bogus-path"); - - match fs::copy(&from, &to) { - Ok(..) => panic!(), - Err(..) => { - assert!(!from.exists()); - assert!(!to.exists()); - } - } -} - -#[test] -fn copy_src_does_not_exist() { - let tmpdir = tmpdir(); - let from = Path::new("test/nonexistent-bogus-path"); - let to = tmpdir.join("out.txt"); - check!(check!(File::create(&to)).write(b"hello")); - assert!(fs::copy(&from, &to).is_err()); - assert!(!from.exists()); - let mut v = Vec::new(); - check!(check!(File::open(&to)).read_to_end(&mut v)); - assert_eq!(v, b"hello"); -} - -#[test] -fn copy_file_ok() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write(b"hello")); - check!(fs::copy(&input, &out)); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"hello"); - - assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions()); -} - -#[test] -fn copy_file_dst_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - check!(File::create(&out)); - match fs::copy(&*out, tmpdir.path()) { - Ok(..) => panic!(), - Err(..) => {} - } -} - -#[test] -fn copy_file_dst_exists() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in"); - let output = tmpdir.join("out"); - - check!(check!(File::create(&input)).write("foo".as_bytes())); - check!(check!(File::create(&output)).write("bar".as_bytes())); - check!(fs::copy(&input, &output)); - - let mut v = Vec::new(); - check!(check!(File::open(&output)).read_to_end(&mut v)); - assert_eq!(v, b"foo".to_vec()); -} - -#[test] -fn copy_file_src_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - match fs::copy(tmpdir.path(), &out) { - Ok(..) => panic!(), - Err(..) => {} - } - assert!(!out.exists()); -} - -#[test] -fn copy_file_preserves_perm_bits() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - let attr = check!(check!(File::create(&input)).metadata()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(fs::set_permissions(&input, p)); - check!(fs::copy(&input, &out)); - assert!(check!(out.metadata()).permissions().readonly()); - check!(fs::set_permissions(&input, attr.permissions())); - check!(fs::set_permissions(&out, attr.permissions())); -} - -#[test] -#[cfg(windows)] -fn copy_file_preserves_streams() { - let tmp = tmpdir(); - check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); - assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); - assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); - let mut v = Vec::new(); - check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); - assert_eq!(v, b"carrot".to_vec()); -} - -#[test] -fn copy_file_returns_metadata_len() { - let tmp = tmpdir(); - let in_path = tmp.join("in.txt"); - let out_path = tmp.join("out.txt"); - check!(check!(File::create(&in_path)).write(b"lettuce")); - #[cfg(windows)] - check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); - let copied_len = check!(fs::copy(&in_path, &out_path)); - assert_eq!(check!(out_path.metadata()).len(), copied_len); -} - -#[test] -fn copy_file_follows_dst_symlink() { - let tmp = tmpdir(); - if !got_symlink_permission(&tmp) { - return; - }; - - let in_path = tmp.join("in.txt"); - let out_path = tmp.join("out.txt"); - let out_path_symlink = tmp.join("out_symlink.txt"); - - check!(fs::write(&in_path, "foo")); - check!(fs::write(&out_path, "bar")); - check!(symlink_file(&out_path, &out_path_symlink)); - - check!(fs::copy(&in_path, &out_path_symlink)); - - assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink()); - assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec()); - assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec()); -} - -#[test] -fn symlinks_work() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write("foobar".as_bytes())); - check!(symlink_file(&input, &out)); - assert!(check!(out.symlink_metadata()).file_type().is_symlink()); - assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"foobar".to_vec()); -} - -#[test] -fn symlink_noexist() { - // Symlinks can point to things that don't exist - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - // Use a relative path for testing. Symlinks get normalized by Windows, - // so we might not get the same path back for absolute paths - check!(symlink_file(&"foo", &tmpdir.join("bar"))); - assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo"); -} - -#[test] -fn read_link() { - let tmpdir = tmpdir(); - if cfg!(windows) { - // directory symlink - assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData")); - // junction - assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default")); - // junction with special permissions - // Since not all localized windows versions contain the folder "Documents and Settings" in english, - // we will briefly check, if it exists and otherwise skip the test. Except during CI we will always execute the test. - if Path::new(r"C:\Documents and Settings\").exists() || env::var_os("CI").is_some() { - assert_eq!( - check!(fs::read_link(r"C:\Documents and Settings\")), - Path::new(r"C:\Users") - ); - } - // Check that readlink works with non-drive paths on Windows. - let link = tmpdir.join("link_unc"); - if got_symlink_permission(&tmpdir) { - check!(symlink_dir(r"\\localhost\c$\", &link)); - assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\")); - }; - } - let link = tmpdir.join("link"); - if !got_symlink_permission(&tmpdir) { - return; - }; - check!(symlink_file(&"foo", &link)); - assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo"); -} - -#[test] -fn readlink_not_symlink() { - let tmpdir = tmpdir(); - match fs::read_link(tmpdir.path()) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } -} - -#[test] -#[cfg_attr(target_os = "android", ignore = "Android SELinux rules prevent creating hardlinks")] -fn links_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - check!(check!(File::create(&input)).write("foobar".as_bytes())); - check!(fs::hard_link(&input, &out)); - assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len()); - assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len()); - let mut v = Vec::new(); - check!(check!(File::open(&out)).read_to_end(&mut v)); - assert_eq!(v, b"foobar".to_vec()); - - // can't link to yourself - match fs::hard_link(&input, &input) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } - // can't link to something that doesn't exist - match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) { - Ok(..) => panic!("wanted a failure"), - Err(..) => {} - } -} - -#[test] -fn chmod_works() { - let tmpdir = tmpdir(); - let file = tmpdir.join("in.txt"); - - check!(File::create(&file)); - let attr = check!(fs::metadata(&file)); - assert!(!attr.permissions().readonly()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(fs::set_permissions(&file, p.clone())); - let attr = check!(fs::metadata(&file)); - assert!(attr.permissions().readonly()); - - match fs::set_permissions(&tmpdir.join("foo"), p.clone()) { - Ok(..) => panic!("wanted an error"), - Err(..) => {} - } - - p.set_readonly(false); - check!(fs::set_permissions(&file, p)); -} - -#[test] -fn fchmod_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let file = check!(File::create(&path)); - let attr = check!(fs::metadata(&path)); - assert!(!attr.permissions().readonly()); - let mut p = attr.permissions(); - p.set_readonly(true); - check!(file.set_permissions(p.clone())); - let attr = check!(fs::metadata(&path)); - assert!(attr.permissions().readonly()); - - p.set_readonly(false); - check!(file.set_permissions(p)); -} - -#[test] -fn sync_doesnt_kill_anything() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::create(&path)); - check!(file.sync_all()); - check!(file.sync_data()); - check!(file.write(b"foo")); - check!(file.sync_all()); - check!(file.sync_data()); -} - -#[test] -fn truncate_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = check!(File::create(&path)); - check!(file.write(b"foo")); - check!(file.sync_all()); - - // Do some simple things with truncation - assert_eq!(check!(file.metadata()).len(), 3); - check!(file.set_len(10)); - assert_eq!(check!(file.metadata()).len(), 10); - check!(file.write(b"bar")); - check!(file.sync_all()); - assert_eq!(check!(file.metadata()).len(), 10); - - let mut v = Vec::new(); - check!(check!(File::open(&path)).read_to_end(&mut v)); - assert_eq!(v, b"foobar\0\0\0\0".to_vec()); - - // Truncate to a smaller length, don't seek, and then write something. - // Ensure that the intermediate zeroes are all filled in (we have `seek`ed - // past the end of the file). - check!(file.set_len(2)); - assert_eq!(check!(file.metadata()).len(), 2); - check!(file.write(b"wut")); - check!(file.sync_all()); - assert_eq!(check!(file.metadata()).len(), 9); - let mut v = Vec::new(); - check!(check!(File::open(&path)).read_to_end(&mut v)); - assert_eq!(v, b"fo\0\0\0\0wut".to_vec()); -} - -#[test] -fn open_flavors() { - use crate::fs::OpenOptions as OO; - fn c(t: &T) -> T { - t.clone() - } - - let tmpdir = tmpdir(); - - let mut r = OO::new(); - r.read(true); - let mut w = OO::new(); - w.write(true); - let mut rw = OO::new(); - rw.read(true).write(true); - let mut a = OO::new(); - a.append(true); - let mut ra = OO::new(); - ra.read(true).append(true); - - let invalid_options = "creating or truncating a file requires write or append access"; - - // Test various combinations of creation modes and access modes. - // - // Allowed: - // creation mode | read | write | read-write | append | read-append | - // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:| - // not set (open existing) | X | X | X | X | X | - // create | | X | X | X | X | - // truncate | | X | X | | | - // create and truncate | | X | X | | | - // create_new | | X | X | X | X | - // - // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it. - - // write-only - check!(c(&w).create_new(true).open(&tmpdir.join("a"))); - check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a"))); - check!(c(&w).truncate(true).open(&tmpdir.join("a"))); - check!(c(&w).create(true).open(&tmpdir.join("a"))); - check!(c(&w).open(&tmpdir.join("a"))); - - // read-only - error_contains!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options); - error_contains!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options); - error_contains!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options); - error_contains!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options); - check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only - - // read-write - check!(c(&rw).create_new(true).open(&tmpdir.join("c"))); - check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c"))); - check!(c(&rw).truncate(true).open(&tmpdir.join("c"))); - check!(c(&rw).create(true).open(&tmpdir.join("c"))); - check!(c(&rw).open(&tmpdir.join("c"))); - - // append - check!(c(&a).create_new(true).open(&tmpdir.join("d"))); - error_contains!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); - error_contains!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); - check!(c(&a).create(true).open(&tmpdir.join("d"))); - check!(c(&a).open(&tmpdir.join("d"))); - - // read-append - check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); - error_contains!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); - error_contains!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); - check!(c(&ra).create(true).open(&tmpdir.join("e"))); - check!(c(&ra).open(&tmpdir.join("e"))); - - // Test opening a file without setting an access mode - let mut blank = OO::new(); - error_contains!(blank.create(true).open(&tmpdir.join("f")), invalid_options); - - // Test write works - check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes())); - - // Test write fails for read-only - check!(r.open(&tmpdir.join("h"))); - { - let mut f = check!(r.open(&tmpdir.join("h"))); - assert!(f.write("wut".as_bytes()).is_err()); - } - - // Test write overwrites - { - let mut f = check!(c(&w).open(&tmpdir.join("h"))); - check!(f.write("baz".as_bytes())); - } - { - let mut f = check!(c(&r).open(&tmpdir.join("h"))); - let mut b = vec![0; 6]; - check!(f.read(&mut b)); - assert_eq!(b, "bazbar".as_bytes()); - } - - // Test truncate works - { - let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h"))); - check!(f.write("foo".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); - - // Test append works - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3); - { - let mut f = check!(c(&a).open(&tmpdir.join("h"))); - check!(f.write("bar".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6); - - // Test .append(true) equals .write(true).append(true) - { - let mut f = check!(c(&w).append(true).open(&tmpdir.join("h"))); - check!(f.write("baz".as_bytes())); - } - assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9); -} - -#[test] -fn _assert_send_sync() { - fn _assert_send_sync() {} - _assert_send_sync::(); -} - -#[test] -fn binary_file() { - let mut bytes = [0; 1024]; - crate::test_helpers::test_rng().fill_bytes(&mut bytes); - - let tmpdir = tmpdir(); - - check!(check!(File::create(&tmpdir.join("test"))).write(&bytes)); - let mut v = Vec::new(); - check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v)); - assert!(v == &bytes[..]); -} - -#[test] -fn write_then_read() { - let mut bytes = [0; 1024]; - crate::test_helpers::test_rng().fill_bytes(&mut bytes); - - let tmpdir = tmpdir(); - - check!(fs::write(&tmpdir.join("test"), &bytes[..])); - let v = check!(fs::read(&tmpdir.join("test"))); - assert!(v == &bytes[..]); - - check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF])); - error_contains!( - fs::read_to_string(&tmpdir.join("not-utf8")), - "stream did not contain valid UTF-8" - ); - - let s = "𐁁𐀓𐀠𐀴𐀍"; - check!(fs::write(&tmpdir.join("utf8"), s.as_bytes())); - let string = check!(fs::read_to_string(&tmpdir.join("utf8"))); - assert_eq!(string, s); -} - -#[test] -fn file_try_clone() { - let tmpdir = tmpdir(); - - let mut f1 = - check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test"))); - let mut f2 = check!(f1.try_clone()); - - check!(f1.write_all(b"hello world")); - check!(f1.seek(SeekFrom::Start(2))); - - let mut buf = vec![]; - check!(f2.read_to_end(&mut buf)); - assert_eq!(buf, b"llo world"); - drop(f2); - - check!(f1.write_all(b"!")); -} - -#[test] -#[cfg(not(target_vendor = "win7"))] -fn unlink_readonly() { - let tmpdir = tmpdir(); - let path = tmpdir.join("file"); - check!(File::create(&path)); - let mut perm = check!(fs::metadata(&path)).permissions(); - perm.set_readonly(true); - check!(fs::set_permissions(&path, perm)); - check!(fs::remove_file(&path)); -} - -#[test] -fn mkdir_trailing_slash() { - let tmpdir = tmpdir(); - let path = tmpdir.join("file"); - check!(fs::create_dir_all(&path.join("a/"))); -} - -#[test] -fn canonicalize_works_simple() { - let tmpdir = tmpdir(); - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let file = tmpdir.join("test"); - File::create(&file).unwrap(); - assert_eq!(fs::canonicalize(&file).unwrap(), file); -} - -#[test] -fn realpath_works() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let file = tmpdir.join("test"); - let dir = tmpdir.join("test2"); - let link = dir.join("link"); - let linkdir = tmpdir.join("test3"); - - File::create(&file).unwrap(); - fs::create_dir(&dir).unwrap(); - symlink_file(&file, &link).unwrap(); - symlink_dir(&dir, &linkdir).unwrap(); - - assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); - - assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); - assert_eq!(fs::canonicalize(&file).unwrap(), file); - assert_eq!(fs::canonicalize(&link).unwrap(), file); - assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); - assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); -} - -#[test] -fn realpath_works_tricky() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); - let a = tmpdir.join("a"); - let b = a.join("b"); - let c = b.join("c"); - let d = a.join("d"); - let e = d.join("e"); - let f = a.join("f"); - - fs::create_dir_all(&b).unwrap(); - fs::create_dir_all(&d).unwrap(); - File::create(&f).unwrap(); - if cfg!(not(windows)) { - symlink_file("../d/e", &c).unwrap(); - symlink_file("../f", &e).unwrap(); - } - if cfg!(windows) { - symlink_file(r"..\d\e", &c).unwrap(); - symlink_file(r"..\f", &e).unwrap(); - } - - assert_eq!(fs::canonicalize(&c).unwrap(), f); - assert_eq!(fs::canonicalize(&e).unwrap(), f); -} - -#[test] -fn dir_entry_methods() { - let tmpdir = tmpdir(); - - fs::create_dir_all(&tmpdir.join("a")).unwrap(); - File::create(&tmpdir.join("b")).unwrap(); - - for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { - let fname = file.file_name(); - match fname.to_str() { - Some("a") => { - assert!(file.file_type().unwrap().is_dir()); - assert!(file.metadata().unwrap().is_dir()); - } - Some("b") => { - assert!(file.file_type().unwrap().is_file()); - assert!(file.metadata().unwrap().is_file()); - } - f => panic!("unknown file name: {f:?}"), - } - } -} - -#[test] -fn dir_entry_debug() { - let tmpdir = tmpdir(); - File::create(&tmpdir.join("b")).unwrap(); - let mut read_dir = tmpdir.path().read_dir().unwrap(); - let dir_entry = read_dir.next().unwrap().unwrap(); - let actual = format!("{dir_entry:?}"); - let expected = format!("DirEntry({:?})", dir_entry.0.path()); - assert_eq!(actual, expected); -} - -#[test] -fn read_dir_not_found() { - let res = fs::read_dir("/path/that/does/not/exist"); - assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); -} - -#[test] -fn file_open_not_found() { - let res = File::open("/path/that/does/not/exist"); - assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); -} - -#[test] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] -fn create_dir_all_with_junctions() { - let tmpdir = tmpdir(); - let target = tmpdir.join("target"); - - let junction = tmpdir.join("junction"); - let b = junction.join("a/b"); - - let link = tmpdir.join("link"); - let d = link.join("c/d"); - - fs::create_dir(&target).unwrap(); - - check!(junction_point(&target, &junction)); - check!(fs::create_dir_all(&b)); - // the junction itself is not a directory, but `is_dir()` on a Path - // follows links - assert!(junction.is_dir()); - assert!(b.exists()); - - if !got_symlink_permission(&tmpdir) { - return; - }; - check!(symlink_dir(&target, &link)); - check!(fs::create_dir_all(&d)); - assert!(link.is_dir()); - assert!(d.exists()); -} - -#[test] -fn metadata_access_times() { - let tmpdir = tmpdir(); - - let b = tmpdir.join("b"); - File::create(&b).unwrap(); - - let a = check!(fs::metadata(&tmpdir.path())); - let b = check!(fs::metadata(&b)); - - assert_eq!(check!(a.accessed()), check!(a.accessed())); - assert_eq!(check!(a.modified()), check!(a.modified())); - assert_eq!(check!(b.accessed()), check!(b.modified())); - - if cfg!(target_vendor = "apple") || cfg!(target_os = "windows") { - check!(a.created()); - check!(b.created()); - } - - if cfg!(target_os = "linux") { - // Not always available - match (a.created(), b.created()) { - (Ok(t1), Ok(t2)) => assert!(t1 <= t2), - (Err(e1), Err(e2)) - if e1.kind() == ErrorKind::Uncategorized - && e2.kind() == ErrorKind::Uncategorized - || e1.kind() == ErrorKind::Unsupported - && e2.kind() == ErrorKind::Unsupported => {} - (a, b) => { - panic!("creation time must be always supported or not supported: {a:?} {b:?}") - } - } - } -} - -/// Test creating hard links to symlinks. -#[test] -#[cfg_attr(target_os = "android", ignore = "Android SELinux rules prevent creating hardlinks")] -fn symlink_hard_link() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - // Create "file", a file. - check!(fs::File::create(tmpdir.join("file"))); - - // Create "symlink", a symlink to "file". - check!(symlink_file("file", tmpdir.join("symlink"))); - - // Create "hard_link", a hard link to "symlink". - check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link"))); - - // "hard_link" should appear as a symlink. - assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); - - // We should be able to open "file" via any of the above names. - let _ = check!(fs::File::open(tmpdir.join("file"))); - assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); - let _ = check!(fs::File::open(tmpdir.join("symlink"))); - let _ = check!(fs::File::open(tmpdir.join("hard_link"))); - - // Rename "file" to "file.renamed". - check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed"))); - - // Now, the symlink and the hard link should be dangling. - assert!(fs::File::open(tmpdir.join("file")).is_err()); - let _ = check!(fs::File::open(tmpdir.join("file.renamed"))); - assert!(fs::File::open(tmpdir.join("symlink")).is_err()); - assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); - - // The symlink and the hard link should both still point to "file". - assert!(fs::read_link(tmpdir.join("file")).is_err()); - assert!(fs::read_link(tmpdir.join("file.renamed")).is_err()); - assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file")); - assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file")); - - // Remove "file.renamed". - check!(fs::remove_file(tmpdir.join("file.renamed"))); - - // Now, we can't open the file by any name. - assert!(fs::File::open(tmpdir.join("file")).is_err()); - assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); - assert!(fs::File::open(tmpdir.join("symlink")).is_err()); - assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); - - // "hard_link" should still appear as a symlink. - assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); -} - -/// Ensure `fs::create_dir` works on Windows with longer paths. -#[test] -#[cfg(windows)] -fn create_dir_long_paths() { - use crate::ffi::OsStr; - use crate::iter; - use crate::os::windows::ffi::OsStrExt; - const PATH_LEN: usize = 247; - - let tmpdir = tmpdir(); - let mut path = tmpdir.path().to_path_buf(); - path.push("a"); - let mut path = path.into_os_string(); - - let utf16_len = path.encode_wide().count(); - if utf16_len >= PATH_LEN { - // Skip the test in the unlikely event the local user has a long temp directory path. - // This should not affect CI. - return; - } - // Increase the length of the path. - path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len)); - - // This should succeed. - fs::create_dir(&path).unwrap(); - - // This will fail if the path isn't converted to verbatim. - path.push("a"); - fs::create_dir(&path).unwrap(); - - // #90940: Ensure an empty path returns the "Not Found" error. - let path = Path::new(""); - assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound); -} - -/// Ensure ReadDir works on large directories. -/// Regression test for https://github.com/rust-lang/rust/issues/93384. -#[test] -fn read_large_dir() { - let tmpdir = tmpdir(); - - let count = 32 * 1024; - for i in 0..count { - check!(fs::File::create(tmpdir.join(&i.to_string()))); - } - - for entry in fs::read_dir(tmpdir.path()).unwrap() { - entry.unwrap(); - } -} - -/// Test the fallback for getting the metadata of files like hiberfil.sys that -/// Windows holds a special lock on, preventing normal means of querying -/// metadata. See #96980. -/// -/// Note this fails in CI because `hiberfil.sys` does not actually exist there. -/// Therefore it's marked as ignored. -#[test] -#[ignore] -#[cfg(windows)] -fn hiberfil_sys() { - let hiberfil = Path::new(r"C:\hiberfil.sys"); - assert_eq!(true, hiberfil.try_exists().unwrap()); - fs::symlink_metadata(hiberfil).unwrap(); - fs::metadata(hiberfil).unwrap(); - assert_eq!(true, hiberfil.exists()); -} - -/// Test that two different ways of obtaining the FileType give the same result. -/// Cf. https://github.com/rust-lang/rust/issues/104900 -#[test] -fn test_eq_direntry_metadata() { - let tmpdir = tmpdir(); - let file_path = tmpdir.join("file"); - File::create(file_path).unwrap(); - for e in fs::read_dir(tmpdir.path()).unwrap() { - let e = e.unwrap(); - let p = e.path(); - let ft1 = e.file_type().unwrap(); - let ft2 = p.metadata().unwrap().file_type(); - assert_eq!(ft1, ft2); - } -} - -/// Test that windows file type equality is not affected by attributes unrelated -/// to the file type. -#[test] -#[cfg(target_os = "windows")] -fn test_eq_windows_file_type() { - let tmpdir = tmpdir(); - let file1 = File::create(tmpdir.join("file1")).unwrap(); - let file2 = File::create(tmpdir.join("file2")).unwrap(); - assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); - - // Change the readonly attribute of one file. - let mut perms = file1.metadata().unwrap().permissions(); - perms.set_readonly(true); - file1.set_permissions(perms.clone()).unwrap(); - #[cfg(target_vendor = "win7")] - let _g = ReadonlyGuard { file: &file1, perms }; - assert_eq!(file1.metadata().unwrap().file_type(), file2.metadata().unwrap().file_type()); - - // Reset the attribute before the `TmpDir`'s drop that removes the - // associated directory, which fails with a `PermissionDenied` error when - // running under Windows 7. - #[cfg(target_vendor = "win7")] - struct ReadonlyGuard<'f> { - file: &'f File, - perms: fs::Permissions, - } - #[cfg(target_vendor = "win7")] - impl<'f> Drop for ReadonlyGuard<'f> { - fn drop(&mut self) { - self.perms.set_readonly(false); - let res = self.file.set_permissions(self.perms.clone()); - - if !thread::panicking() { - res.unwrap(); - } - } - } -} - -/// Regression test for https://github.com/rust-lang/rust/issues/50619. -#[test] -#[cfg(target_os = "linux")] -fn test_read_dir_infinite_loop() { - use crate::io::ErrorKind; - use crate::process::Command; - - // Create a zombie child process - let Ok(mut child) = Command::new("echo").spawn() else { return }; - - // Make sure the process is (un)dead - match child.kill() { - // InvalidInput means the child already exited - Err(e) if e.kind() != ErrorKind::InvalidInput => return, - _ => {} - } - - // open() on this path will succeed, but readdir() will fail - let id = child.id(); - let path = format!("/proc/{id}/net"); - - // Skip the test if we can't open the directory in the first place - let Ok(dir) = fs::read_dir(path) else { return }; - - // Check for duplicate errors - assert!(dir.filter(|e| e.is_err()).take(2).count() < 2); -} - -#[test] -fn rename_directory() { - let tmpdir = tmpdir(); - let old_path = tmpdir.join("foo/bar/baz"); - fs::create_dir_all(&old_path).unwrap(); - let test_file = &old_path.join("temp.txt"); - - File::create(test_file).unwrap(); - - let new_path = tmpdir.join("quux/blat"); - fs::create_dir_all(&new_path).unwrap(); - fs::rename(&old_path, &new_path.join("newdir")).unwrap(); - assert!(new_path.join("newdir").is_dir()); - assert!(new_path.join("newdir/temp.txt").exists()); -} - -#[test] -fn test_file_times() { - #[cfg(target_vendor = "apple")] - use crate::os::darwin::fs::FileTimesExt; - #[cfg(windows)] - use crate::os::windows::fs::FileTimesExt; - - let tmp = tmpdir(); - let file = File::create(tmp.join("foo")).unwrap(); - let mut times = FileTimes::new(); - let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); - let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); - times = times.set_accessed(accessed).set_modified(modified); - #[cfg(any(windows, target_vendor = "apple"))] - let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_vendor = "apple"))] - { - times = times.set_created(created); - } - match file.set_times(times) { - // Allow unsupported errors on platforms which don't support setting times. - #[cfg(not(any( - windows, - all( - unix, - not(any( - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )) - ) - )))] - Err(e) if e.kind() == ErrorKind::Unsupported => return, - Err(e) => panic!("error setting file times: {e:?}"), - Ok(_) => {} - } - let metadata = file.metadata().unwrap(); - assert_eq!(metadata.accessed().unwrap(), accessed); - assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_vendor = "apple"))] - { - assert_eq!(metadata.created().unwrap(), created); - } -} - -#[test] -#[cfg(target_vendor = "apple")] -fn test_file_times_pre_epoch_with_nanos() { - use crate::os::darwin::fs::FileTimesExt; - - let tmp = tmpdir(); - let file = File::create(tmp.join("foo")).unwrap(); - - for (accessed, modified, created) in [ - // The first round is to set filetimes to something we know works, but this time - // it's validated with nanoseconds as well which probe the numeric boundary. - ( - SystemTime::UNIX_EPOCH + Duration::new(12345, 1), - SystemTime::UNIX_EPOCH + Duration::new(54321, 100_000_000), - SystemTime::UNIX_EPOCH + Duration::new(32123, 999_999_999), - ), - // The second rounds uses pre-epoch dates along with nanoseconds that probe - // the numeric boundary. - ( - SystemTime::UNIX_EPOCH - Duration::new(1, 1), - SystemTime::UNIX_EPOCH - Duration::new(60, 100_000_000), - SystemTime::UNIX_EPOCH - Duration::new(3600, 999_999_999), - ), - ] { - let mut times = FileTimes::new(); - times = times.set_accessed(accessed).set_modified(modified).set_created(created); - file.set_times(times).unwrap(); - - let metadata = file.metadata().unwrap(); - assert_eq!(metadata.accessed().unwrap(), accessed); - assert_eq!(metadata.modified().unwrap(), modified); - assert_eq!(metadata.created().unwrap(), created); - } -} - -#[test] -#[cfg(windows)] -fn windows_unix_socket_exists() { - use crate::sys::{c, net}; - use crate::{mem, ptr}; - - let tmp = tmpdir(); - let socket_path = tmp.join("socket"); - - // std doesn't currently support Unix sockets on Windows so manually create one here. - net::init(); - unsafe { - let socket = c::WSASocketW( - c::AF_UNIX as i32, - c::SOCK_STREAM, - 0, - ptr::null_mut(), - 0, - c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, - ); - // AF_UNIX is not supported on earlier versions of Windows, - // so skip this test if it's unsupported and we're not in CI. - if socket == c::INVALID_SOCKET { - let error = c::WSAGetLastError(); - if env::var_os("CI").is_none() && error == c::WSAEAFNOSUPPORT { - return; - } else { - panic!("Creating AF_UNIX socket failed (OS error {error})"); - } - } - let mut addr = c::SOCKADDR_UN { sun_family: c::AF_UNIX, sun_path: mem::zeroed() }; - let bytes = socket_path.as_os_str().as_encoded_bytes(); - let bytes = core::slice::from_raw_parts(bytes.as_ptr().cast::(), bytes.len()); - addr.sun_path[..bytes.len()].copy_from_slice(bytes); - let len = size_of_val(&addr) as i32; - let result = c::bind(socket, (&raw const addr).cast::(), len); - c::closesocket(socket); - assert_eq!(result, 0); - } - // Make sure all ways of testing a file exist work for a Unix socket. - assert_eq!(socket_path.exists(), true); - assert_eq!(socket_path.try_exists().unwrap(), true); - assert_eq!(socket_path.metadata().is_ok(), true); -} - -#[cfg(windows)] -#[test] -fn test_hidden_file_truncation() { - // Make sure that File::create works on an existing hidden file. See #115745. - let tmpdir = tmpdir(); - let path = tmpdir.join("hidden_file.txt"); - - // Create a hidden file. - const FILE_ATTRIBUTE_HIDDEN: u32 = 2; - let mut file = OpenOptions::new() - .write(true) - .create_new(true) - .attributes(FILE_ATTRIBUTE_HIDDEN) - .open(&path) - .unwrap(); - file.write("hidden world!".as_bytes()).unwrap(); - file.flush().unwrap(); - drop(file); - - // Create a new file by truncating the existing one. - let file = File::create(&path).unwrap(); - let metadata = file.metadata().unwrap(); - assert_eq!(metadata.len(), 0); -} - -// See https://github.com/rust-lang/rust/pull/131072 for more details about why -// these two tests are disabled under Windows 7 here. -#[cfg(windows)] -#[test] -#[cfg_attr(target_vendor = "win7", ignore = "Unsupported under Windows 7.")] -fn test_rename_file_over_open_file() { - // Make sure that std::fs::rename works if the target file is already opened with FILE_SHARE_DELETE. See #123985. - let tmpdir = tmpdir(); - - // Create source with test data to read. - let source_path = tmpdir.join("source_file.txt"); - fs::write(&source_path, b"source hello world").unwrap(); - - // Create target file with test data to read; - let target_path = tmpdir.join("target_file.txt"); - fs::write(&target_path, b"target hello world").unwrap(); - - // Open target file - let target_file = fs::File::open(&target_path).unwrap(); - - // Rename source - fs::rename(source_path, &target_path).unwrap(); - - core::mem::drop(target_file); - assert_eq!(fs::read(target_path).unwrap(), b"source hello world"); -} - -#[test] -#[cfg(windows)] -#[cfg_attr(target_vendor = "win7", ignore = "Unsupported under Windows 7.")] -fn test_rename_directory_to_non_empty_directory() { - // Renaming a directory over a non-empty existing directory should fail on Windows. - let tmpdir: TempDir = tmpdir(); - - let source_path = tmpdir.join("source_directory"); - let target_path = tmpdir.join("target_directory"); - - fs::create_dir(&source_path).unwrap(); - fs::create_dir(&target_path).unwrap(); - - fs::write(target_path.join("target_file.txt"), b"target hello world").unwrap(); - - error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY -} - -#[test] -fn test_rename_symlink() { - let tmpdir = tmpdir(); - if !got_symlink_permission(&tmpdir) { - return; - }; - - let original = tmpdir.join("original"); - let dest = tmpdir.join("dest"); - let not_exist = Path::new("does not exist"); - - symlink_file(not_exist, &original).unwrap(); - fs::rename(&original, &dest).unwrap(); - // Make sure that renaming `original` to `dest` preserves the symlink. - assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist); -} - -#[test] -#[cfg(windows)] -#[cfg_attr( - all(windows, target_arch = "aarch64"), - ignore = "SymLinks not enabled on Arm64 Windows runners https://github.com/actions/partner-runner-images/issues/94" -)] -fn test_rename_junction() { - let tmpdir = tmpdir(); - let original = tmpdir.join("original"); - let dest = tmpdir.join("dest"); - let not_exist = Path::new("does not exist"); - - junction_point(¬_exist, &original).unwrap(); - fs::rename(&original, &dest).unwrap(); - - // Make sure that renaming `original` to `dest` preserves the junction point. - // Junction links are always absolute so we just check the file name is correct. - assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str())); -} - -#[test] -fn test_open_options_invalid_combinations() { - use crate::fs::OpenOptions as OO; - - let test_cases: &[(fn() -> OO, &str)] = &[ - (|| OO::new().create(true).read(true).clone(), "create without write"), - (|| OO::new().create_new(true).read(true).clone(), "create_new without write"), - (|| OO::new().truncate(true).read(true).clone(), "truncate without write"), - (|| OO::new().truncate(true).append(true).clone(), "truncate with append"), - ]; - - for (make_opts, desc) in test_cases { - let opts = make_opts(); - let result = opts.open("nonexistent.txt"); - assert!(result.is_err(), "{desc} should fail"); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput, "{desc} - wrong error kind"); - assert_eq!( - err.to_string(), - "creating or truncating a file requires write or append access", - "{desc} - wrong error message" - ); - } - - let result = OO::new().open("nonexistent.txt"); - assert!(result.is_err(), "no access mode should fail"); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - assert_eq!(err.to_string(), "must specify at least one of read, write, or append access"); -} - -#[test] -fn test_fs_set_times() { - #[cfg(target_vendor = "apple")] - use crate::os::darwin::fs::FileTimesExt; - #[cfg(windows)] - use crate::os::windows::fs::FileTimesExt; - - let tmp = tmpdir(); - let path = tmp.join("foo"); - File::create(&path).unwrap(); - - let mut times = FileTimes::new(); - let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); - let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); - times = times.set_accessed(accessed).set_modified(modified); - - #[cfg(any(windows, target_vendor = "apple"))] - let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_vendor = "apple"))] - { - times = times.set_created(created); - } - - match fs::set_times(&path, times) { - // Allow unsupported errors on platforms which don't support setting times. - #[cfg(not(any( - windows, - all( - unix, - not(any( - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )) - ) - )))] - Err(e) if e.kind() == ErrorKind::Unsupported => return, - Err(e) => panic!("error setting file times: {e:?}"), - Ok(_) => {} - } - - let metadata = fs::metadata(&path).unwrap(); - assert_eq!(metadata.accessed().unwrap(), accessed); - assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_vendor = "apple"))] - { - assert_eq!(metadata.created().unwrap(), created); - } -} - -#[test] -fn test_fs_set_times_on_dir() { - #[cfg(target_vendor = "apple")] - use crate::os::darwin::fs::FileTimesExt; - #[cfg(windows)] - use crate::os::windows::fs::FileTimesExt; - - let tmp = tmpdir(); - let dir_path = tmp.join("testdir"); - fs::create_dir(&dir_path).unwrap(); - - let mut times = FileTimes::new(); - let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); - let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); - times = times.set_accessed(accessed).set_modified(modified); - - #[cfg(any(windows, target_vendor = "apple"))] - let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_vendor = "apple"))] - { - times = times.set_created(created); - } - - match fs::set_times(&dir_path, times) { - // Allow unsupported errors on platforms which don't support setting times. - #[cfg(not(any( - windows, - all( - unix, - not(any( - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )) - ) - )))] - Err(e) if e.kind() == ErrorKind::Unsupported => return, - Err(e) => panic!("error setting directory times: {e:?}"), - Ok(_) => {} - } - - let metadata = fs::metadata(&dir_path).unwrap(); - assert_eq!(metadata.accessed().unwrap(), accessed); - assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_vendor = "apple"))] - { - assert_eq!(metadata.created().unwrap(), created); - } -} - -#[test] -fn test_fs_set_times_follows_symlink() { - #[cfg(target_vendor = "apple")] - use crate::os::darwin::fs::FileTimesExt; - #[cfg(windows)] - use crate::os::windows::fs::FileTimesExt; - - let tmp = tmpdir(); - - // Create a target file - let target = tmp.join("target"); - File::create(&target).unwrap(); - - // Create a symlink to the target - #[cfg(unix)] - let link = tmp.join("link"); - #[cfg(unix)] - crate::os::unix::fs::symlink(&target, &link).unwrap(); - - #[cfg(windows)] - let link = tmp.join("link.txt"); - #[cfg(windows)] - crate::os::windows::fs::symlink_file(&target, &link).unwrap(); - - // Get the symlink's own modified time BEFORE calling set_times (to compare later) - // We don't check accessed time because reading metadata may update atime on some platforms. - let link_metadata_before = fs::symlink_metadata(&link).unwrap(); - let link_modified_before = link_metadata_before.modified().unwrap(); - - let mut times = FileTimes::new(); - let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); - let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); - times = times.set_accessed(accessed).set_modified(modified); - - #[cfg(any(windows, target_vendor = "apple"))] - let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_vendor = "apple"))] - { - times = times.set_created(created); - } - - // Call fs::set_times on the symlink - it should follow the link and modify the target - match fs::set_times(&link, times) { - // Allow unsupported errors on platforms which don't support setting times. - #[cfg(not(any( - windows, - all( - unix, - not(any( - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )) - ) - )))] - Err(e) if e.kind() == ErrorKind::Unsupported => return, - Err(e) => panic!("error setting file times through symlink: {e:?}"), - Ok(_) => {} - } - - // Verify that the TARGET file's times were changed (following the symlink) - let target_metadata = fs::metadata(&target).unwrap(); - assert_eq!( - target_metadata.accessed().unwrap(), - accessed, - "target file accessed time should match" - ); - assert_eq!( - target_metadata.modified().unwrap(), - modified, - "target file modified time should match" - ); - #[cfg(any(windows, target_vendor = "apple"))] - { - assert_eq!( - target_metadata.created().unwrap(), - created, - "target file created time should match" - ); - } - - // Also verify through the symlink (fs::metadata follows symlinks) - let link_followed_metadata = fs::metadata(&link).unwrap(); - assert_eq!(link_followed_metadata.accessed().unwrap(), accessed); - assert_eq!(link_followed_metadata.modified().unwrap(), modified); - - // Verify that the SYMLINK ITSELF was NOT modified - // Note: We only check modified time, not accessed time, because reading the symlink - // metadata may update its atime on some platforms (e.g., Linux). - let link_metadata_after = fs::symlink_metadata(&link).unwrap(); - assert_eq!( - link_metadata_after.modified().unwrap(), - link_modified_before, - "symlink's own modified time should not change" - ); -} - -#[test] -fn test_fs_set_times_nofollow() { - #[cfg(target_vendor = "apple")] - use crate::os::darwin::fs::FileTimesExt; - #[cfg(windows)] - use crate::os::windows::fs::FileTimesExt; - - let tmp = tmpdir(); - - // Create a target file and a symlink to it - let target = tmp.join("target"); - File::create(&target).unwrap(); - - #[cfg(unix)] - let link = tmp.join("link"); - #[cfg(unix)] - crate::os::unix::fs::symlink(&target, &link).unwrap(); - - #[cfg(windows)] - let link = tmp.join("link.txt"); - #[cfg(windows)] - crate::os::windows::fs::symlink_file(&target, &link).unwrap(); - - let mut times = FileTimes::new(); - let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(11111); - let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(22222); - times = times.set_accessed(accessed).set_modified(modified); - - #[cfg(any(windows, target_vendor = "apple"))] - let created = SystemTime::UNIX_EPOCH + Duration::from_secs(33333); - #[cfg(any(windows, target_vendor = "apple"))] - { - times = times.set_created(created); - } - - // Set times on the symlink itself (not following it) - match fs::set_times_nofollow(&link, times) { - // Allow unsupported errors on platforms which don't support setting times. - #[cfg(not(any( - windows, - all( - unix, - not(any( - target_os = "android", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - )) - ) - )))] - Err(e) if e.kind() == ErrorKind::Unsupported => return, - Err(e) => panic!("error setting symlink times: {e:?}"), - Ok(_) => {} - } - - // Read symlink metadata (without following) - let metadata = fs::symlink_metadata(&link).unwrap(); - assert_eq!(metadata.accessed().unwrap(), accessed); - assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_vendor = "apple"))] - { - assert_eq!(metadata.created().unwrap(), created); - } - - // Verify that the target file's times were NOT changed - let target_metadata = fs::metadata(&target).unwrap(); - assert_ne!(target_metadata.accessed().unwrap(), accessed); - assert_ne!(target_metadata.modified().unwrap(), modified); -} - -#[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] -fn test_dir_smoke_test() { - let tmpdir = tmpdir(); - let dir = Dir::open(tmpdir.path()); - check!(dir); -} - -#[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] -fn test_dir_read_file() { - let tmpdir = tmpdir(); - let mut f = check!(File::create(tmpdir.join("foo.txt"))); - check!(f.write(b"bar")); - check!(f.flush()); - drop(f); - let dir = check!(Dir::open(tmpdir.path())); - let f = check!(dir.open_file("foo.txt")); - let buf = check!(io::read_to_string(f)); - assert_eq!("bar", &buf); - let f = check!(dir.open_file(tmpdir.join("foo.txt"))); - let buf = check!(io::read_to_string(f)); - assert_eq!("bar", &buf); -} diff --git a/crates/std/src/hash/mod.rs b/crates/std/src/hash/mod.rs deleted file mode 100644 index e5ef9e3..0000000 --- a/crates/std/src/hash/mod.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Generic hashing support. -//! -//! This module provides a generic way to compute the [hash] of a value. -//! Hashes are most commonly used with [`HashMap`] and [`HashSet`]. -//! -//! [hash]: https://en.wikipedia.org/wiki/Hash_function -//! [`HashMap`]: ../../std/collections/struct.HashMap.html -//! [`HashSet`]: ../../std/collections/struct.HashSet.html -//! -//! The simplest way to make a type hashable is to use `#[derive(Hash)]`: -//! -//! # Examples -//! -//! ```rust -//! use std::hash::{DefaultHasher, Hash, Hasher}; -//! -//! #[derive(Hash)] -//! struct Person { -//! id: u32, -//! name: String, -//! phone: u64, -//! } -//! -//! let person1 = Person { -//! id: 5, -//! name: "Janet".to_string(), -//! phone: 555_666_7777, -//! }; -//! let person2 = Person { -//! id: 5, -//! name: "Bob".to_string(), -//! phone: 555_666_7777, -//! }; -//! -//! assert!(calculate_hash(&person1) != calculate_hash(&person2)); -//! -//! fn calculate_hash(t: &T) -> u64 { -//! let mut s = DefaultHasher::new(); -//! t.hash(&mut s); -//! s.finish() -//! } -//! ``` -//! -//! If you need more control over how a value is hashed, you need to implement -//! the [`Hash`] trait: -//! -//! ```rust -//! use std::hash::{DefaultHasher, Hash, Hasher}; -//! -//! struct Person { -//! id: u32, -//! # #[allow(dead_code)] -//! name: String, -//! phone: u64, -//! } -//! -//! impl Hash for Person { -//! fn hash(&self, state: &mut H) { -//! self.id.hash(state); -//! self.phone.hash(state); -//! } -//! } -//! -//! let person1 = Person { -//! id: 5, -//! name: "Janet".to_string(), -//! phone: 555_666_7777, -//! }; -//! let person2 = Person { -//! id: 5, -//! name: "Bob".to_string(), -//! phone: 555_666_7777, -//! }; -//! -//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2)); -//! -//! fn calculate_hash(t: &T) -> u64 { -//! let mut s = DefaultHasher::new(); -//! t.hash(&mut s); -//! s.finish() -//! } -//! ``` -#![stable(feature = "rust1", since = "1.0.0")] - -pub(crate) mod random; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::hash::*; - -#[stable(feature = "std_hash_exports", since = "1.76.0")] -pub use self::random::{DefaultHasher, RandomState}; diff --git a/crates/std/src/hash/random.rs b/crates/std/src/hash/random.rs deleted file mode 100644 index 3c1b21e..0000000 --- a/crates/std/src/hash/random.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the -//! [`collections`] module without actually publicly exporting them, so that parts of that -//! implementation can more easily be moved to the [`alloc`] crate. -//! -//! Although its items are public and contain stability attributes, they can't actually be accessed -//! outside this crate. -//! -//! [`collections`]: crate::collections - -use super::{BuildHasher, Hasher, SipHasher13}; -use crate::cell::Cell; -use crate::fmt; -use crate::sys::random::hashmap_random_keys; - -/// `RandomState` is the default state for [`HashMap`] types. -/// -/// A particular instance `RandomState` will create the same instances of -/// [`Hasher`], but the hashers created by two different `RandomState` -/// instances are unlikely to produce the same result for the same values. -/// -/// [`HashMap`]: crate::collections::HashMap -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// use std::hash::RandomState; -/// -/// let s = RandomState::new(); -/// let mut map = HashMap::with_hasher(s); -/// map.insert(1, 2); -/// ``` -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -#[derive(Clone)] -pub struct RandomState { - k0: u64, - k1: u64, -} - -impl RandomState { - /// Constructs a new `RandomState` that is initialized with random keys. - /// - /// # Examples - /// - /// ``` - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// ``` - #[inline] - #[allow(deprecated)] - // rand - #[must_use] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn new() -> RandomState { - // Historically this function did not cache keys from the OS and instead - // simply always called `rand::thread_rng().gen()` twice. In #31356 it - // was discovered, however, that because we re-seed the thread-local RNG - // from the OS periodically that this can cause excessive slowdown when - // many hash maps are created on a thread. To solve this performance - // trap we cache the first set of randomly generated keys per-thread. - // - // Later in #36481 it was discovered that exposing a deterministic - // iteration order allows a form of DOS attack. To counter that we - // increment one of the seeds on every RandomState creation, giving - // every corresponding HashMap a different iteration order. - thread_local!(static KEYS: Cell<(u64, u64)> = { - Cell::new(hashmap_random_keys()) - }); - - KEYS.with(|keys| { - let (k0, k1) = keys.get(); - keys.set((k0.wrapping_add(1), k1)); - RandomState { k0, k1 } - }) - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl BuildHasher for RandomState { - type Hasher = DefaultHasher; - #[inline] - fn build_hasher(&self) -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) - } -} - -/// The default [`Hasher`] used by [`RandomState`]. -/// -/// The internal algorithm is not specified, and so it and its hashes should -/// not be relied upon over releases. -#[derive(Clone, Debug)] -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -pub struct DefaultHasher(SipHasher13); - -impl DefaultHasher { - /// Creates a new `DefaultHasher`. - /// - /// This hasher is not guaranteed to be the same as all other - /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` - /// instances created through `new` or `default`. - #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] - #[inline] - #[rustc_const_unstable(feature = "const_default", issue = "143894")] - #[must_use] - pub const fn new() -> DefaultHasher { - DefaultHasher(SipHasher13::new_with_keys(0, 0)) - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -#[rustc_const_unstable(feature = "const_default", issue = "143894")] -impl const Default for DefaultHasher { - /// Creates a new `DefaultHasher` using [`new`]. - /// See its documentation for more. - /// - /// [`new`]: DefaultHasher::new - #[inline] - fn default() -> DefaultHasher { - DefaultHasher::new() - } -} - -#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Hasher for DefaultHasher { - // The underlying `SipHasher13` doesn't override the other - // `write_*` methods, so it's ok not to forward them here. - - #[inline] - fn write(&mut self, msg: &[u8]) { - self.0.write(msg) - } - - #[inline] - fn write_str(&mut self, s: &str) { - self.0.write_str(s); - } - - #[inline] - fn finish(&self) -> u64 { - self.0.finish() - } -} - -#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] -impl Default for RandomState { - /// Constructs a new `RandomState`. - #[inline] - fn default() -> RandomState { - RandomState::new() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RandomState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RandomState").finish_non_exhaustive() - } -} diff --git a/crates/std/src/io/buffered/bufreader.rs b/crates/std/src/io/buffered/bufreader.rs deleted file mode 100644 index 95876ec..0000000 --- a/crates/std/src/io/buffered/bufreader.rs +++ /dev/null @@ -1,601 +0,0 @@ -mod buffer; - -use buffer::Buffer; - -use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint, - SpecReadByte, uninlined_slow_read_byte, -}; - -/// The `BufReader` struct adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. -/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] -/// results in a system call. A `BufReader` performs large, infrequent reads on -/// the underlying [`Read`] and maintains an in-memory buffer of the results. -/// -/// `BufReader` can improve the speed of programs that make *small* and -/// *repeated* read calls to the same file or network socket. It does not -/// help when reading very large amounts at once, or reading just one or a few -/// times. It also provides no advantage when reading from a source that is -/// already in memory, like a [Vec]\. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be -/// discarded. Creating multiple instances of a `BufReader` on the same -/// stream can cause data loss. Reading from the underlying reader after -/// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause -/// data loss. -/// -/// [`TcpStream::read`]: crate::net::TcpStream::read -/// [`TcpStream`]: crate::net::TcpStream -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufReader; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let f = File::open("log.txt")?; -/// let mut reader = BufReader::new(f); -/// -/// let mut line = String::new(); -/// let len = reader.read_line(&mut line)?; -/// println!("First line is {len} bytes long"); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufReader { - buf: Buffer, - inner: R, -} - -impl BufReader { - /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KiB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::new(f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - pub(crate) fn try_new_buffer() -> io::Result { - Buffer::try_with_capacity(DEFAULT_BUF_SIZE) - } - - pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self { - Self { inner, buf } - } - - /// Creates a new `BufReader` with the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with ten bytes of capacity: - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::with_capacity(10, f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: R) -> BufReader { - BufReader { - inner, - buf: Buffer::with_capacity(capacity), - } - } -} - -impl BufReader { - /// Attempt to look ahead `n` bytes. - /// - /// `n` must be less than or equal to `capacity`. - /// - /// The returned slice may be less than `n` bytes long if - /// end of file is reached. - /// - /// After calling this method, you may call [`consume`](BufRead::consume) - /// with a value less than or equal to `n` to advance over some or all of - /// the returned bytes. - /// - /// ## Examples - /// - /// ```rust - /// #![feature(bufreader_peek)] - /// use std::io::{Read, BufReader}; - /// - /// let mut bytes = &b"oh, hello there"[..]; - /// let mut rdr = BufReader::with_capacity(6, &mut bytes); - /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); - /// let mut buf = [0; 4]; - /// rdr.read(&mut buf[..]).unwrap(); - /// assert_eq!(&buf, b"oh, "); - /// assert_eq!(rdr.peek(5).unwrap(), b"hello"); - /// let mut s = String::new(); - /// rdr.read_to_string(&mut s).unwrap(); - /// assert_eq!(&s, "hello there"); - /// assert_eq!(rdr.peek(1).unwrap().len(), 0); - /// ``` - #[unstable(feature = "bufreader_peek", issue = "128405")] - pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { - assert!(n <= self.capacity()); - while n > self.buf.buffer().len() { - if self.buf.pos() > 0 { - self.buf.backshift(); - } - let new = self.buf.read_more(&mut self.inner)?; - if new == 0 { - // end of file, no more bytes to read - return Ok(&self.buf.buffer()[..]); - } - debug_assert_eq!(self.buf.pos(), 0); - } - Ok(&self.buf.buffer()[..n]) - } -} - -impl BufReader { - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let mut reader = BufReader::new(f1); - /// - /// let f2 = reader.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Returns a reference to the internally buffered data. - /// - /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// assert!(reader.buffer().is_empty()); - /// - /// if reader.fill_buf()?.len() > 0 { - /// assert!(!reader.buffer().is_empty()); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - self.buf.buffer() - } - - /// Returns the number of bytes the internal buffer can hold at once. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// - /// let capacity = reader.capacity(); - /// let buffer = reader.fill_buf()?; - /// assert!(buffer.len() <= capacity); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.capacity() - } - - /// Unwraps this `BufReader`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. Therefore, - /// a following read from the underlying reader may lead to data loss. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> R - where - R: Sized, - { - self.inner - } - - /// Invalidates all data in the internal buffer. - #[inline] - pub(in crate::io) fn discard_buffer(&mut self) { - self.buf.discard_buffer() - } -} - -// This is only used by a test which asserts that the initialization-tracking is correct. -#[cfg(test)] -impl BufReader { - #[allow(missing_docs)] - pub fn initialized(&self) -> usize { - self.buf.initialized() - } -} - -impl BufReader { - /// Seeks relative to the current position. If the new position lies within the buffer, - /// the buffer will not be flushed, allowing for more efficient seeks. - /// This method does not return the location of the underlying reader, so the caller - /// must track this information themselves if it is required. - #[stable(feature = "bufreader_seek_relative", since = "1.53.0")] - pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - let pos = self.buf.pos() as u64; - if offset < 0 { - if let Some(_) = pos.checked_sub((-offset) as u64) { - self.buf.unconsume((-offset) as usize); - return Ok(()); - } - } else if let Some(new_pos) = pos.checked_add(offset as u64) { - if new_pos <= self.buf.filled() as u64 { - self.buf.consume(offset as usize); - return Ok(()); - } - } - - self.seek(SeekFrom::Current(offset)).map(drop) - } -} - -impl SpecReadByte for BufReader -where - Self: Read, -{ - #[inline] - fn spec_read_byte(&mut self) -> Option> { - let mut byte = 0; - if self.buf.consume_with(1, |claimed| byte = claimed[0]) { - return Some(Ok(byte)); - } - - // Fallback case, only reached once per buffer refill. - uninlined_slow_read_byte(self) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for BufReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // If we don't have any buffered data and we're doing a massive read - // (larger than our internal buffer), bypass our internal buffer - // entirely. - if self.buf.pos() == self.buf.filled() && buf.len() >= self.capacity() { - self.discard_buffer(); - return self.inner.read(buf); - } - let mut rem = self.fill_buf()?; - let nread = rem.read(buf)?; - self.consume(nread); - Ok(nread) - } - - fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - // If we don't have any buffered data and we're doing a massive read - // (larger than our internal buffer), bypass our internal buffer - // entirely. - if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() { - self.discard_buffer(); - return self.inner.read_buf(cursor); - } - - let prev = cursor.written(); - - let mut rem = self.fill_buf()?; - rem.read_buf(cursor.reborrow())?; // actually never fails - - self.consume(cursor.written() - prev); //slice impl of read_buf known to never unfill buf - - Ok(()) - } - - // Small read_exacts from a BufReader are extremely common when used with a deserializer. - // The default implementation calls read in a loop, which results in surprisingly poor code - // generation for the common path where the buffer has enough bytes to fill the passed-in - // buffer. - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if self - .buf - .consume_with(buf.len(), |claimed| buf.copy_from_slice(claimed)) - { - return Ok(()); - } - - crate::io::default_read_exact(self, buf) - } - - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - if self - .buf - .consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) - { - return Ok(()); - } - - crate::io::default_read_buf_exact(self, cursor) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() { - self.discard_buffer(); - return self.inner.read_vectored(bufs); - } - let mut rem = self.fill_buf()?; - let nread = rem.read_vectored(bufs)?; - - self.consume(nread); - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - // The inner reader might have an optimized `read_to_end`. Drain our buffer and then - // delegate to the inner implementation. - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let inner_buf = self.buffer(); - buf.try_reserve(inner_buf.len())?; - buf.extend_from_slice(inner_buf); - let nread = inner_buf.len(); - self.discard_buffer(); - Ok(nread + self.inner.read_to_end(buf)?) - } - - // The inner reader might have an optimized `read_to_end`. Drain our buffer and then - // delegate to the inner implementation. - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - // In the general `else` case below we must read bytes into a side buffer, check - // that they are valid UTF-8, and then append them to `buf`. This requires a - // potentially large memcpy. - // - // If `buf` is empty--the most common case--we can leverage `append_to_string` - // to read directly into `buf`'s internal byte buffer, saving an allocation and - // a memcpy. - if buf.is_empty() { - // `append_to_string`'s safety relies on the buffer only being appended to since - // it only checks the UTF-8 validity of new data. If there were existing content in - // `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append - // bytes but also modify existing bytes and render them invalid. On the other hand, - // if `buf` is empty then by definition any writes must be appends and - // `append_to_string` will validate all of the new bytes. - unsafe { crate::io::append_to_string(buf, |b| self.read_to_end(b)) } - } else { - // We cannot append our byte buffer directly onto the `buf` String as there could - // be an incomplete UTF-8 sequence that has only been partially read. We must read - // everything into a side buffer first and then call `from_utf8` on the complete - // buffer. - let mut bytes = Vec::new(); - self.read_to_end(&mut bytes)?; - let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?; - *buf += string; - Ok(string.len()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for BufReader { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - self.buf.fill_buf(&mut self.inner) - } - - fn consume(&mut self, amt: usize) { - self.buf.consume(amt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufReader -where - R: ?Sized + fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufReader") - .field("reader", &&self.inner) - .field( - "buffer", - &format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()), - ) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufReader { - /// Seek to an offset, in bytes, in the underlying reader. - /// - /// The position used for seeking with [SeekFrom::Current]\(_) is the - /// position the underlying reader would be at if the `BufReader` had no - /// internal buffer. - /// - /// Seeking always discards the internal buffer, even if the seek position - /// would otherwise fall within it. This guarantees that calling - /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader - /// at the same position. - /// - /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. - /// - /// See [`std::io::Seek`] for more details. - /// - /// Note: In the edge case where you're seeking with [SeekFrom::Current]\(n) - /// where `n` minus the internal buffer length overflows an `i64`, two - /// seeks will be performed instead of one. If the second seek returns - /// [`Err`], the underlying reader will be left at the same position it would - /// have if you called `seek` with [SeekFrom::Current]\(0). - /// - /// [`std::io::Seek`]: Seek - fn seek(&mut self, pos: SeekFrom) -> io::Result { - let result: u64; - if let SeekFrom::Current(n) = pos { - let remainder = (self.buf.filled() - self.buf.pos()) as i64; - // it should be safe to assume that remainder fits within an i64 as the alternative - // means we managed to allocate 8 exbibytes and that's absurd. - // But it's not out of the realm of possibility for some weird underlying reader to - // support seeking by i64::MIN so we need to handle underflow when subtracting - // remainder. - if let Some(offset) = n.checked_sub(remainder) { - result = self.inner.seek(SeekFrom::Current(offset))?; - } else { - // seek backwards by our remainder, and then by the offset - self.inner.seek(SeekFrom::Current(-remainder))?; - self.discard_buffer(); - result = self.inner.seek(SeekFrom::Current(n))?; - } - } else { - // Seeking with Start/End doesn't care about our buffer length. - result = self.inner.seek(pos)?; - } - self.discard_buffer(); - Ok(result) - } - - /// Returns the current seek position from the start of the stream. - /// - /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))` - /// but does not flush the internal buffer. Due to this optimization the - /// function does not guarantee that calling `.into_inner()` immediately - /// afterwards will yield the underlying reader at the same position. Use - /// [`BufReader::seek`] instead if you require that guarantee. - /// - /// # Panics - /// - /// This function will panic if the position of the inner reader is smaller - /// than the amount of buffered data. That can happen if the inner reader - /// has an incorrect implementation of [`Seek::stream_position`], or if the - /// position has gone out of sync due to calling [`Seek::seek`] directly on - /// the underlying reader. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - fn stream_position(&mut self) -> io::Result { - let remainder = (self.buf.filled() - self.buf.pos()) as u64; - self.inner.stream_position().map(|pos| { - pos.checked_sub(remainder).expect( - "overflow when subtracting remaining buffer size from inner stream position", - ) - }) - } - - /// Seeks relative to the current position. - /// - /// If the new position lies within the buffer, the buffer will not be - /// flushed, allowing for more efficient seeks. This method does not return - /// the location of the underlying reader, so the caller must track this - /// information themselves if it is required. - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - self.seek_relative(offset) - } -} - -impl SizeHint for BufReader { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(self.get_ref()) + self.buffer().len() - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(self.get_ref()).and_then(|up| self.buffer().len().checked_add(up)) - } -} diff --git a/crates/std/src/io/buffered/bufreader/buffer.rs b/crates/std/src/io/buffered/bufreader/buffer.rs deleted file mode 100644 index ad8608b..0000000 --- a/crates/std/src/io/buffered/bufreader/buffer.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! An encapsulation of `BufReader`'s buffer management logic. -//! -//! This module factors out the basic functionality of `BufReader` in order to protect two core -//! invariants: -//! * `filled` bytes of `buf` are always initialized -//! * `pos` is always <= `filled` -//! Since this module encapsulates the buffer management logic, we can ensure that the range -//! `pos..filled` is always a valid index into the initialized region of the buffer. This means -//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so -//! without encountering any runtime bounds checks. - -use crate::cmp; -use crate::io::{self, BorrowedBuf, ErrorKind, Read}; -use crate::mem::MaybeUninit; - -pub struct Buffer { - // The buffer. - buf: Box<[MaybeUninit]>, - // The current seek offset into `buf`, must always be <= `filled`. - pos: usize, - // Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are - // initialized with bytes from a read. - filled: usize, - // This is the max number of bytes returned across all `fill_buf` calls. We track this so that we - // can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its - // defensive initialization as possible. Note that while this often the same as `filled`, it - // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and - // omitting this is a huge perf regression for `Read` impls that do not. - initialized: usize, -} - -impl Buffer { - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - let buf = Box::new_uninit_slice(capacity); - Self { buf, pos: 0, filled: 0, initialized: 0 } - } - - #[inline] - pub fn try_with_capacity(capacity: usize) -> io::Result { - match Box::try_new_uninit_slice(capacity) { - Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), - Err(_) => { - Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) - } - } - } - - #[inline] - pub fn buffer(&self) -> &[u8] { - // SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and - // that region is initialized because those are all invariants of this type. - unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } - } - - #[inline] - pub fn capacity(&self) -> usize { - self.buf.len() - } - - #[inline] - pub fn filled(&self) -> usize { - self.filled - } - - #[inline] - pub fn pos(&self) -> usize { - self.pos - } - - // This is only used by a test which asserts that the initialization-tracking is correct. - #[cfg(test)] - pub fn initialized(&self) -> usize { - self.initialized - } - - #[inline] - pub fn discard_buffer(&mut self) { - self.pos = 0; - self.filled = 0; - } - - #[inline] - pub fn consume(&mut self, amt: usize) { - self.pos = cmp::min(self.pos + amt, self.filled); - } - - /// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to - /// `visitor` and return true. If there are not enough bytes available, return false. - #[inline] - pub fn consume_with(&mut self, amt: usize, mut visitor: V) -> bool - where - V: FnMut(&[u8]), - { - if let Some(claimed) = self.buffer().get(..amt) { - visitor(claimed); - // If the indexing into self.buffer() succeeds, amt must be a valid increment. - self.pos += amt; - true - } else { - false - } - } - - #[inline] - pub fn unconsume(&mut self, amt: usize) { - self.pos = self.pos.saturating_sub(amt); - } - - /// Read more bytes into the buffer without discarding any of its contents - pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { - let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); - let old_init = self.initialized - self.filled; - unsafe { - buf.set_init(old_init); - } - reader.read_buf(buf.unfilled())?; - self.filled += buf.len(); - self.initialized += buf.init_len() - old_init; - Ok(buf.len()) - } - - /// Remove bytes that have already been read from the buffer. - pub fn backshift(&mut self) { - self.buf.copy_within(self.pos..self.filled, 0); - self.filled -= self.pos; - self.pos = 0; - } - - #[inline] - pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { - // If we've reached the end of our internal buffer then we need to fetch - // some more data from the reader. - // Branch using `>=` instead of the more correct `==` - // to tell the compiler that the pos..cap slice is always valid. - if self.pos >= self.filled { - debug_assert!(self.pos == self.filled); - - let mut buf = BorrowedBuf::from(&mut *self.buf); - // SAFETY: `self.filled` bytes will always have been initialized. - unsafe { - buf.set_init(self.initialized); - } - - let result = reader.read_buf(buf.unfilled()); - - self.pos = 0; - self.filled = buf.len(); - self.initialized = buf.init_len(); - - result?; - } - Ok(self.buffer()) - } -} diff --git a/crates/std/src/io/buffered/bufwriter.rs b/crates/std/src/io/buffered/bufwriter.rs deleted file mode 100644 index d569fed..0000000 --- a/crates/std/src/io/buffered/bufwriter.rs +++ /dev/null @@ -1,680 +0,0 @@ -use crate::io::{ - self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, -}; -use crate::mem::{self, ManuallyDrop}; -use crate::{error, fmt, ptr}; - -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A -/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying -/// writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a [Vec]\. -/// -/// It is critical to call [`flush`] before `BufWriter` is dropped. Though -/// dropping will attempt to flush the contents of the buffer, any errors -/// that happen in the process of dropping will be ignored. Calling [`flush`] -/// ensures that the buffer is empty and thus dropping will not even attempt -/// file operations. -/// -/// # Examples -/// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::net::TcpStream; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// stream.flush().unwrap(); -/// ``` -/// -/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped -/// together by the buffer and will all be written out in one system call when -/// the `stream` is flushed. -/// -/// [`TcpStream::write`]: crate::net::TcpStream::write -/// [`TcpStream`]: crate::net::TcpStream -/// [`flush`]: BufWriter::flush -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufWriter { - // The buffer. Avoid using this like a normal `Vec` in common code paths. - // That is, don't use `buf.push`, `buf.extend_from_slice`, or any other - // methods that require bounds checking or the like. This makes an enormous - // difference to performance (we may want to stop using a `Vec` entirely). - buf: Vec, - // #30888: If the inner writer panics in a call to write, we don't want to - // write the buffered data a second time in BufWriter's destructor. This - // flag tells the Drop impl if it should skip the flush. - panicked: bool, - inner: W, -} - -impl BufWriter { - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KiB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - pub(crate) fn try_new_buffer() -> io::Result> { - Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { - io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") - }) - } - - pub(crate) fn with_buffer(inner: W, buf: Vec) -> Self { - Self { inner, buf, panicked: false } - } - - /// Creates a new `BufWriter` with at least the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with a buffer of at least a hundred bytes. - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); - /// let mut buffer = BufWriter::with_capacity(100, stream); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { - BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false } - } - - /// Unwraps this `BufWriter`, returning the underlying writer. - /// - /// The buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // unwrap the TcpStream and flush the buffer - /// let stream = buffer.into_inner().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(mut self) -> Result>> { - match self.flush_buf() { - Err(e) => Err(IntoInnerError::new(self, e)), - Ok(()) => Ok(self.into_parts().0), - } - } - - /// Disassembles this `BufWriter`, returning the underlying writer, and any buffered but - /// unwritten data. - /// - /// If the underlying writer panicked, it is not known what portion of the data was written. - /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer - /// contents can still be recovered). - /// - /// `into_parts` makes no attempt to flush data and cannot fail. - /// - /// # Examples - /// - /// ``` - /// use std::io::{BufWriter, Write}; - /// - /// let mut buffer = [0u8; 10]; - /// let mut stream = BufWriter::new(buffer.as_mut()); - /// write!(stream, "too much data").unwrap(); - /// stream.flush().expect_err("it doesn't fit"); - /// let (recovered_writer, buffered_data) = stream.into_parts(); - /// assert_eq!(recovered_writer.len(), 0); - /// assert_eq!(&buffered_data.unwrap(), b"ata"); - /// ``` - #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] - pub fn into_parts(self) -> (W, Result, WriterPanicked>) { - let mut this = ManuallyDrop::new(self); - let buf = mem::take(&mut this.buf); - let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; - - // SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped - let inner = unsafe { ptr::read(&this.inner) }; - - (inner, buf) - } -} - -impl BufWriter { - /// Send data in our local buffer into the inner writer, looping as - /// necessary until either it's all been sent or an error occurs. - /// - /// Because all the data in the buffer has been reported to our owner as - /// "successfully written" (by returning nonzero success values from - /// `write`), any 0-length writes from `inner` must be reported as i/o - /// errors from this method. - pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> { - /// Helper struct to ensure the buffer is updated after all the writes - /// are complete. It tracks the number of written bytes and drains them - /// all from the front of the buffer when dropped. - struct BufGuard<'a> { - buffer: &'a mut Vec, - written: usize, - } - - impl<'a> BufGuard<'a> { - fn new(buffer: &'a mut Vec) -> Self { - Self { buffer, written: 0 } - } - - /// The unwritten part of the buffer - fn remaining(&self) -> &[u8] { - &self.buffer[self.written..] - } - - /// Flag some bytes as removed from the front of the buffer - fn consume(&mut self, amt: usize) { - self.written += amt; - } - - /// true if all of the bytes have been written - fn done(&self) -> bool { - self.written >= self.buffer.len() - } - } - - impl Drop for BufGuard<'_> { - fn drop(&mut self) { - if self.written > 0 { - self.buffer.drain(..self.written); - } - } - } - - let mut guard = BufGuard::new(&mut self.buf); - while !guard.done() { - self.panicked = true; - let r = self.inner.write(guard.remaining()); - self.panicked = false; - - match r { - Ok(0) => { - return Err(io::const_error!( - ErrorKind::WriteZero, - "failed to write the buffered data", - )); - } - Ok(n) => guard.consume(n), - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Buffer some data without flushing it, regardless of the size of the - /// data. Writes as much as possible without exceeding capacity. Returns - /// the number of bytes written. - pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize { - let available = self.spare_capacity(); - let amt_to_buffer = available.min(buf.len()); - - // SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction. - unsafe { - self.write_to_buffer_unchecked(&buf[..amt_to_buffer]); - } - - amt_to_buffer - } - - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_ref(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - &self.inner - } - - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_mut(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - &mut self.inner - } - - /// Returns a reference to the internally buffered data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // See how many bytes are currently buffered - /// let bytes_buffered = buf_writer.buffer().len(); - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - &self.buf - } - - /// Returns a mutable reference to the internal buffer. - /// - /// This can be used to write data directly into the buffer without triggering writers - /// to the underlying writer. - /// - /// That the buffer is a `Vec` is an implementation detail. - /// Callers should not modify the capacity as there currently is no public API to do so - /// and thus any capacity changes would be unexpected by the user. - pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec { - &mut self.buf - } - - /// Returns the number of bytes the internal buffer can hold without flushing. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // Check the capacity of the inner buffer - /// let capacity = buf_writer.capacity(); - /// // Calculate how many bytes can be written without flushing - /// let without_flush = capacity - buf_writer.buffer().len(); - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.capacity() - } - - // Ensure this function does not get inlined into `write`, so that it - // remains inlineable and its common path remains as short as possible. - // If this function ends up being called frequently relative to `write`, - // it's likely a sign that the client is using an improperly sized buffer - // or their write patterns are somewhat pathological. - #[cold] - #[inline(never)] - fn write_cold(&mut self, buf: &[u8]) -> io::Result { - if buf.len() > self.spare_capacity() { - self.flush_buf()?; - } - - // Why not len > capacity? To avoid a needless trip through the buffer when the input - // exactly fills it. We'd just need to flush it to the underlying writer anyway. - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write(buf); - self.panicked = false; - r - } else { - // Write to the buffer. In this case, we write to the buffer even if it fills it - // exactly. Doing otherwise would mean flushing the buffer, then writing this - // input to the inner writer, which in many cases would be a worse strategy. - - // SAFETY: There was either enough spare capacity already, or there wasn't and we - // flushed the buffer to ensure that there is. In the latter case, we know that there - // is because flushing ensured that our entire buffer is spare capacity, and we entered - // this block because the input buffer length is less than that capacity. In either - // case, it's safe to write the input buffer to our buffer. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - Ok(buf.len()) - } - } - - // Ensure this function does not get inlined into `write_all`, so that it - // remains inlineable and its common path remains as short as possible. - // If this function ends up being called frequently relative to `write_all`, - // it's likely a sign that the client is using an improperly sized buffer - // or their write patterns are somewhat pathological. - #[cold] - #[inline(never)] - fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> { - // Normally, `write_all` just calls `write` in a loop. We can do better - // by calling `self.get_mut().write_all()` directly, which avoids - // round trips through the buffer in the event of a series of partial - // writes in some circumstances. - - if buf.len() > self.spare_capacity() { - self.flush_buf()?; - } - - // Why not len > capacity? To avoid a needless trip through the buffer when the input - // exactly fills it. We'd just need to flush it to the underlying writer anyway. - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write_all(buf); - self.panicked = false; - r - } else { - // Write to the buffer. In this case, we write to the buffer even if it fills it - // exactly. Doing otherwise would mean flushing the buffer, then writing this - // input to the inner writer, which in many cases would be a worse strategy. - - // SAFETY: There was either enough spare capacity already, or there wasn't and we - // flushed the buffer to ensure that there is. In the latter case, we know that there - // is because flushing ensured that our entire buffer is spare capacity, and we entered - // this block because the input buffer length is less than that capacity. In either - // case, it's safe to write the input buffer to our buffer. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - Ok(()) - } - } - - // SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`, - // i.e., that input buffer length is less than or equal to spare capacity. - #[inline] - unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) { - debug_assert!(buf.len() <= self.spare_capacity()); - let old_len = self.buf.len(); - let buf_len = buf.len(); - let src = buf.as_ptr(); - unsafe { - let dst = self.buf.as_mut_ptr().add(old_len); - ptr::copy_nonoverlapping(src, dst, buf_len); - self.buf.set_len(old_len + buf_len); - } - } - - #[inline] - fn spare_capacity(&self) -> usize { - self.buf.capacity() - self.buf.len() - } -} - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying -/// writer has previously panicked. Contains the (possibly partly written) buffered data. -/// -/// # Example -/// -/// ``` -/// use std::io::{self, BufWriter, Write}; -/// use std::panic::{catch_unwind, AssertUnwindSafe}; -/// -/// struct PanickingWriter; -/// impl Write for PanickingWriter { -/// fn write(&mut self, buf: &[u8]) -> io::Result { panic!() } -/// fn flush(&mut self) -> io::Result<()> { panic!() } -/// } -/// -/// let mut stream = BufWriter::new(PanickingWriter); -/// write!(stream, "some data").unwrap(); -/// let result = catch_unwind(AssertUnwindSafe(|| { -/// stream.flush().unwrap() -/// })); -/// assert!(result.is_err()); -/// let (recovered_writer, buffered_data) = stream.into_parts(); -/// assert!(matches!(recovered_writer, PanickingWriter)); -/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data"); -/// ``` -pub struct WriterPanicked { - buf: Vec, -} - -impl WriterPanicked { - /// Returns the perhaps-unwritten data. Some of this data may have been written by the - /// panicking call(s) to the underlying writer, so simply writing it again is not a good idea. - #[must_use = "`self` will be dropped if the result is not used"] - #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] - pub fn into_inner(self) -> Vec { - self.buf - } -} - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -impl error::Error for WriterPanicked {} - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -impl fmt::Display for WriterPanicked { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f) - } -} - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -impl fmt::Debug for WriterPanicked { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WriterPanicked") - .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for BufWriter { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - // Use < instead of <= to avoid a needless trip through the buffer in some cases. - // See `write_cold` for details. - if buf.len() < self.spare_capacity() { - // SAFETY: safe by above conditional. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - Ok(buf.len()) - } else { - self.write_cold(buf) - } - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - // Use < instead of <= to avoid a needless trip through the buffer in some cases. - // See `write_all_cold` for details. - if buf.len() < self.spare_capacity() { - // SAFETY: safe by above conditional. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - Ok(()) - } else { - self.write_all_cold(buf) - } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - // FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied - // to `write` and `write_all`. The performance benefits can be significant. See #79930. - if self.get_ref().is_write_vectored() { - // We have to handle the possibility that the total length of the buffers overflows - // `usize` (even though this can only happen if multiple `IoSlice`s reference the - // same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the - // computation overflows, then surely the input cannot fit in our buffer, so we forward - // to the inner writer's `write_vectored` method to let it handle it appropriately. - let mut saturated_total_len: usize = 0; - - for buf in bufs { - saturated_total_len = saturated_total_len.saturating_add(buf.len()); - - if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() { - // Flush if the total length of the input exceeds our buffer's spare capacity. - // If we would have overflowed, this condition also holds, and we need to flush. - self.flush_buf()?; - } - - if saturated_total_len >= self.buf.capacity() { - // Forward to our inner writer if the total length of the input is greater than or - // equal to our buffer capacity. If we would have overflowed, this condition also - // holds, and we punt to the inner writer. - self.panicked = true; - let r = self.get_mut().write_vectored(bufs); - self.panicked = false; - return r; - } - } - - // `saturated_total_len < self.buf.capacity()` implies that we did not saturate. - - // SAFETY: We checked whether or not the spare capacity was large enough above. If - // it was, then we're safe already. If it wasn't, we flushed, making sufficient - // room for any input <= the buffer size, which includes this input. - unsafe { - bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b)); - }; - - Ok(saturated_total_len) - } else { - let mut iter = bufs.iter(); - let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) { - // This is the first non-empty slice to write, so if it does - // not fit in the buffer, we still get to flush and proceed. - if buf.len() > self.spare_capacity() { - self.flush_buf()?; - } - if buf.len() >= self.buf.capacity() { - // The slice is at least as large as the buffering capacity, - // so it's better to write it directly, bypassing the buffer. - self.panicked = true; - let r = self.get_mut().write(buf); - self.panicked = false; - return r; - } else { - // SAFETY: We checked whether or not the spare capacity was large enough above. - // If it was, then we're safe already. If it wasn't, we flushed, making - // sufficient room for any input <= the buffer size, which includes this input. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - buf.len() - } - } else { - return Ok(0); - }; - debug_assert!(total_written != 0); - for buf in iter { - if buf.len() <= self.spare_capacity() { - // SAFETY: safe by above conditional. - unsafe { - self.write_to_buffer_unchecked(buf); - } - - // This cannot overflow `usize`. If we are here, we've written all of the bytes - // so far to our buffer, and we've ensured that we never exceed the buffer's - // capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`. - total_written += buf.len(); - } else { - break; - } - } - Ok(total_written) - } - } - - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - self.flush_buf().and_then(|()| self.get_mut().flush()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufWriter") - .field("writer", &&self.inner) - .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufWriter { - /// Seek to the offset, in bytes, in the underlying writer. - /// - /// Seeking always writes out the internal buffer before seeking. - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.flush_buf()?; - self.get_mut().seek(pos) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for BufWriter { - fn drop(&mut self) { - if !self.panicked { - // dtors should not panic, so we ignore a failed flush - let _r = self.flush_buf(); - } - } -} diff --git a/crates/std/src/io/buffered/linewriter.rs b/crates/std/src/io/buffered/linewriter.rs deleted file mode 100644 index cc6921b..0000000 --- a/crates/std/src/io/buffered/linewriter.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::fmt; -use crate::io::buffered::LineWriterShim; -use crate::io::{self, BufWriter, IntoInnerError, IoSlice, Write}; - -/// Wraps a writer and buffers output to it, flushing whenever a newline -/// (`0x0a`, `'\n'`) is detected. -/// -/// The [`BufWriter`] struct wraps a writer and buffers its output. -/// But it only does this batched write when it goes out of scope, or when the -/// internal buffer is full. Sometimes, you'd prefer to write each line as it's -/// completed, rather than the entire buffer at once. Enter `LineWriter`. It -/// does exactly that. -/// -/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the -/// `LineWriter` goes out of scope or when its internal buffer is full. -/// -/// If there's still a partial line in the buffer when the `LineWriter` is -/// dropped, it will flush those contents. -/// -/// # Examples -/// -/// We can use `LineWriter` to write one line at a time, significantly -/// reducing the number of actual writes to the file. -/// -/// ```no_run -/// use std::fs::{self, File}; -/// use std::io::prelude::*; -/// use std::io::LineWriter; -/// -/// fn main() -> std::io::Result<()> { -/// let road_not_taken = b"I shall be telling this with a sigh -/// Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference."; -/// -/// let file = File::create("poem.txt")?; -/// let mut file = LineWriter::new(file); -/// -/// file.write_all(b"I shall be telling this with a sigh")?; -/// -/// // No bytes are written until a newline is encountered (or -/// // the internal buffer is filled). -/// assert_eq!(fs::read_to_string("poem.txt")?, ""); -/// file.write_all(b"\n")?; -/// assert_eq!( -/// fs::read_to_string("poem.txt")?, -/// "I shall be telling this with a sigh\n", -/// ); -/// -/// // Write the rest of the poem. -/// file.write_all(b"Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference.")?; -/// -/// // The last line of the poem doesn't end in a newline, so -/// // we have to flush or drop the `LineWriter` to finish -/// // writing. -/// file.flush()?; -/// -/// // Confirm the whole poem was written. -/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct LineWriter { - inner: BufWriter, -} - -impl LineWriter { - /// Creates a new `LineWriter`. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> LineWriter { - // Lines typically aren't that long, don't use a giant buffer - LineWriter::with_capacity(1024, inner) - } - - /// Creates a new `LineWriter` with at least the specified capacity for the - /// internal buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::with_capacity(100, file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { - LineWriter { inner: BufWriter::with_capacity(capacity, inner) } - } - - /// Gets a mutable reference to the underlying writer. - /// - /// Caution must be taken when calling methods on the mutable reference - /// returned as extra writes could corrupt the output stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let mut file = LineWriter::new(file); - /// - /// // we can use reference just like file - /// let reference = file.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - self.inner.get_mut() - } - - /// Unwraps this `LineWriter`, returning the underlying writer. - /// - /// The internal buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// - /// let writer: LineWriter = LineWriter::new(file); - /// - /// let file: File = writer.into_inner()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> Result>> { - self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner })) - } -} - -impl LineWriter { - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// - /// let reference = file.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.get_ref() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for LineWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - LineWriterShim::new(&mut self.inner).write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - LineWriterShim::new(&mut self.inner).write_vectored(bufs) - } - - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_all(buf) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_all_vectored(bufs) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_fmt(fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for LineWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("LineWriter") - .field("writer", &self.get_ref()) - .field( - "buffer", - &format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()), - ) - .finish_non_exhaustive() - } -} diff --git a/crates/std/src/io/buffered/linewritershim.rs b/crates/std/src/io/buffered/linewritershim.rs deleted file mode 100644 index 967e248..0000000 --- a/crates/std/src/io/buffered/linewritershim.rs +++ /dev/null @@ -1,297 +0,0 @@ -use core::slice::memchr; - -use crate::io::{self, BufWriter, IoSlice, Write}; - -/// Private helper struct for implementing the line-buffered writing logic. -/// -/// This shim temporarily wraps a BufWriter, and uses its internals to -/// implement a line-buffered writer (specifically by using the internal -/// methods like write_to_buf and flush_buf). In this way, a more -/// efficient abstraction can be created than one that only had access to -/// `write` and `flush`, without needlessly duplicating a lot of the -/// implementation details of BufWriter. This also allows existing -/// `BufWriters` to be temporarily given line-buffering logic; this is what -/// enables Stdout to be alternately in line-buffered or block-buffered mode. -#[derive(Debug)] -pub struct LineWriterShim<'a, W: ?Sized + Write> { - buffer: &'a mut BufWriter, -} - -impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { - pub fn new(buffer: &'a mut BufWriter) -> Self { - Self { buffer } - } - - /// Gets a reference to the inner writer (that is, the writer - /// wrapped by the BufWriter). - fn inner(&self) -> &W { - self.buffer.get_ref() - } - - /// Gets a mutable reference to the inner writer (that is, the writer - /// wrapped by the BufWriter). Be careful with this writer, as writes to - /// it will bypass the buffer. - fn inner_mut(&mut self) -> &mut W { - self.buffer.get_mut() - } - - /// Gets the content currently buffered in self.buffer - fn buffered(&self) -> &[u8] { - self.buffer.buffer() - } - - /// Flushes the buffer iff the last byte is a newline (indicating that an - /// earlier write only succeeded partially, and we want to retry flushing - /// the buffered line before continuing with a subsequent write). - fn flush_if_completed_line(&mut self) -> io::Result<()> { - match self.buffered().last().copied() { - Some(b'\n') => self.buffer.flush_buf(), - _ => Ok(()), - } - } -} - -impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { - /// Writes some data into this BufWriter with line buffering. - /// - /// This means that, if any newlines are present in the data, the data up to - /// the last newline is sent directly to the underlying writer, and data - /// after it is buffered. Returns the number of bytes written. - /// - /// This function operates on a "best effort basis"; in keeping with the - /// convention of `Write::write`, it makes at most one attempt to write - /// new data to the underlying writer. If that write only reports a partial - /// success, the remaining data will be buffered. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it ends with a - /// newline, even if the incoming data does not contain any newlines. - fn write(&mut self, buf: &[u8]) -> io::Result { - let newline_idx = match memchr::memrchr(b'\n', buf) { - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write (which may flush if - // we exceed the inner buffer's size) - None => { - self.flush_if_completed_line()?; - return self.buffer.write(buf); - } - // Otherwise, arrange for the lines to be written directly to the - // inner writer. - Some(newline_idx) => newline_idx + 1, - }; - - // Flush existing content to prepare for our write. We have to do this - // before attempting to write `buf` in order to maintain consistency; - // if we add `buf` to the buffer then try to flush it all at once, - // we're obligated to return Ok(), which would mean suppressing any - // errors that occur during flush. - self.buffer.flush_buf()?; - - // This is what we're going to try to write directly to the inner - // writer. The rest will be buffered, if nothing goes wrong. - let lines = &buf[..newline_idx]; - - // Write `lines` directly to the inner writer. In keeping with the - // `write` convention, make at most one attempt to add new (unbuffered) - // data. Because this write doesn't touch the BufWriter state directly, - // and the buffer is known to be empty, we don't need to worry about - // self.buffer.panicked here. - let flushed = self.inner_mut().write(lines)?; - - // If buffer returns Ok(0), propagate that to the caller without - // doing additional buffering; otherwise we're just guaranteeing - // an "ErrorKind::WriteZero" later. - if flushed == 0 { - return Ok(0); - } - - // Now that the write has succeeded, buffer the rest (or as much of - // the rest as possible). If there were any unwritten newlines, we - // only buffer out to the last unwritten newline that fits in the - // buffer; this helps prevent flushing partial lines on subsequent - // calls to LineWriterShim::write. - - // Handle the cases in order of most-common to least-common, under - // the presumption that most writes succeed in totality, and that most - // writes are smaller than the buffer. - // - Is this a partial line (ie, no newlines left in the unwritten tail) - // - If not, does the data out to the last unwritten newline fit in - // the buffer? - // - If not, scan for the last newline that *does* fit in the buffer - let tail = if flushed >= newline_idx { - let tail = &buf[flushed..]; - // Avoid unnecessary short writes by not splitting the remaining - // bytes if they're larger than the buffer. - // They can be written in full by the next call to write. - if tail.len() >= self.buffer.capacity() { - return Ok(flushed); - } - tail - } else if newline_idx - flushed <= self.buffer.capacity() { - &buf[flushed..newline_idx] - } else { - let scan_area = &buf[flushed..]; - let scan_area = &scan_area[..self.buffer.capacity()]; - match memchr::memrchr(b'\n', scan_area) { - Some(newline_idx) => &scan_area[..newline_idx + 1], - None => scan_area, - } - }; - - let buffered = self.buffer.write_to_buf(tail); - Ok(flushed + buffered) - } - - fn flush(&mut self) -> io::Result<()> { - self.buffer.flush() - } - - /// Writes some vectored data into this BufWriter with line buffering. - /// - /// This means that, if any newlines are present in the data, the data up to - /// and including the buffer containing the last newline is sent directly to - /// the inner writer, and the data after it is buffered. Returns the number - /// of bytes written. - /// - /// This function operates on a "best effort basis"; in keeping with the - /// convention of `Write::write`, it makes at most one attempt to write - /// new data to the underlying writer. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it contains any - /// newlines. - /// - /// Because sorting through an array of `IoSlice` can be a bit convoluted, - /// This method differs from write in the following ways: - /// - /// - It attempts to write the full content of all the buffers up to and - /// including the one containing the last newline. This means that it - /// may attempt to write a partial line, that buffer has data past the - /// newline. - /// - If the write only reports partial success, it does not attempt to - /// find the precise location of the written bytes and buffer the rest. - /// - /// If the underlying vector doesn't support vectored writing, we instead - /// simply write the first non-empty buffer with `write`. This way, we - /// get the benefits of more granular partial-line handling without losing - /// anything in efficiency - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - // If there's no specialized behavior for write_vectored, just use - // write. This has the benefit of more granular partial-line handling. - if !self.is_write_vectored() { - return match bufs.iter().find(|buf| !buf.is_empty()) { - Some(buf) => self.write(buf), - None => Ok(0), - }; - } - - // Find the buffer containing the last newline - // FIXME: This is overly slow if there are very many bufs and none contain - // newlines. e.g. writev() on Linux only writes up to 1024 slices, so - // scanning the rest is wasted effort. This makes write_all_vectored() - // quadratic. - let last_newline_buf_idx = bufs - .iter() - .enumerate() - .rev() - .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); - - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write - let last_newline_buf_idx = match last_newline_buf_idx { - // No newlines; just do a normal buffered write - None => { - self.flush_if_completed_line()?; - return self.buffer.write_vectored(bufs); - } - Some(i) => i, - }; - - // Flush existing content to prepare for our write - self.buffer.flush_buf()?; - - // This is what we're going to try to write directly to the inner - // writer. The rest will be buffered, if nothing goes wrong. - let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); - - // Write `lines` directly to the inner writer. In keeping with the - // `write` convention, make at most one attempt to add new (unbuffered) - // data. Because this write doesn't touch the BufWriter state directly, - // and the buffer is known to be empty, we don't need to worry about - // self.panicked here. - let flushed = self.inner_mut().write_vectored(lines)?; - - // If inner returns Ok(0), propagate that to the caller without - // doing additional buffering; otherwise we're just guaranteeing - // an "ErrorKind::WriteZero" later. - if flushed == 0 { - return Ok(0); - } - - // Don't try to reconstruct the exact amount written; just bail - // in the event of a partial write - let mut lines_len: usize = 0; - for buf in lines { - // With overlapping/duplicate slices the total length may in theory - // exceed usize::MAX - lines_len = lines_len.saturating_add(buf.len()); - if flushed < lines_len { - return Ok(flushed); - } - } - - // Now that the write has succeeded, buffer the rest (or as much of the - // rest as possible) - let buffered: usize = tail - .iter() - .filter(|buf| !buf.is_empty()) - .map(|buf| self.buffer.write_to_buf(buf)) - .take_while(|&n| n > 0) - .sum(); - - Ok(flushed + buffered) - } - - fn is_write_vectored(&self) -> bool { - self.inner().is_write_vectored() - } - - /// Writes some data into this BufWriter with line buffering. - /// - /// This means that, if any newlines are present in the data, the data up to - /// the last newline is sent directly to the underlying writer, and data - /// after it is buffered. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it contains any - /// newlines, even if the incoming data does not contain any newlines. - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - match memchr::memrchr(b'\n', buf) { - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write (which may flush if - // we exceed the inner buffer's size) - None => { - self.flush_if_completed_line()?; - self.buffer.write_all(buf) - } - Some(newline_idx) => { - let (lines, tail) = buf.split_at(newline_idx + 1); - - if self.buffered().is_empty() { - self.inner_mut().write_all(lines)?; - } else { - // If there is any buffered data, we add the incoming lines - // to that buffer before flushing, which saves us at least - // one write call. We can't really do this with `write`, - // since we can't do this *and* not suppress errors *and* - // report a consistent state to the caller in a return - // value, but here in write_all it's fine. - self.buffer.write_all(lines)?; - self.buffer.flush_buf()?; - } - - self.buffer.write_all(tail) - } - } - } -} diff --git a/crates/std/src/io/buffered/mod.rs b/crates/std/src/io/buffered/mod.rs deleted file mode 100644 index e36f2d9..0000000 --- a/crates/std/src/io/buffered/mod.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! Buffering wrappers for I/O traits - -mod bufreader; -mod bufwriter; -mod linewriter; -mod linewritershim; - -#[cfg(test)] -mod tests; - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use bufwriter::WriterPanicked; -use linewritershim::LineWriterShim; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; -use crate::io::Error; -use crate::{error, fmt}; - -/// An error returned by [`BufWriter::into_inner`] which combines an error that -/// happened while writing out the buffer, and the buffered writer object -/// which may be used to recover from the condition. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// // do stuff with the stream -/// -/// // we want to get our `TcpStream` back, so let's try: -/// -/// let stream = match stream.into_inner() { -/// Ok(s) => s, -/// Err(e) => { -/// // Here, e is an IntoInnerError -/// panic!("An error occurred"); -/// } -/// }; -/// ``` -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoInnerError(W, Error); - -impl IntoInnerError { - /// Constructs a new IntoInnerError - fn new(writer: W, error: Error) -> Self { - Self(writer, error) - } - - /// Helper to construct a new IntoInnerError; intended to help with - /// adapters that wrap other adapters - fn new_wrapped(self, f: impl FnOnce(W) -> W2) -> IntoInnerError { - let Self(writer, error) = self; - IntoInnerError::new(f(writer), error) - } - - /// Returns the error which caused the call to [`BufWriter::into_inner()`] - /// to fail. - /// - /// This error was returned when attempting to write the internal buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's log the inner error. - /// // - /// // We'll just 'log' to stdout for this example. - /// println!("{}", e.error()); - /// - /// panic!("An unexpected error occurred."); - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn error(&self) -> &Error { - &self.1 - } - - /// Returns the buffered writer instance which generated the error. - /// - /// The returned object can be used for error recovery, such as - /// re-inspecting the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's re-examine the buffer: - /// let buffer = e.into_inner(); - /// - /// // do stuff to try to recover - /// - /// // afterwards, let's just return the stream - /// buffer.into_inner().unwrap() - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> W { - self.0 - } - - /// Consumes the [`IntoInnerError`] and returns the error which caused the call to - /// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to - /// obtain ownership of the underlying error. - /// - /// # Example - /// ``` - /// use std::io::{BufWriter, ErrorKind, Write}; - /// - /// let mut not_enough_space = [0u8; 10]; - /// let mut stream = BufWriter::new(not_enough_space.as_mut()); - /// write!(stream, "this cannot be actually written").unwrap(); - /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); - /// let err = into_inner_err.into_error(); - /// assert_eq!(err.kind(), ErrorKind::WriteZero); - /// ``` - #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] - pub fn into_error(self) -> Error { - self.1 - } - - /// Consumes the [`IntoInnerError`] and returns the error which caused the call to - /// [`BufWriter::into_inner()`] to fail, and the underlying writer. - /// - /// This can be used to simply obtain ownership of the underlying error; it can also be used for - /// advanced error recovery. - /// - /// # Example - /// ``` - /// use std::io::{BufWriter, ErrorKind, Write}; - /// - /// let mut not_enough_space = [0u8; 10]; - /// let mut stream = BufWriter::new(not_enough_space.as_mut()); - /// write!(stream, "this cannot be actually written").unwrap(); - /// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small"); - /// let (err, recovered_writer) = into_inner_err.into_parts(); - /// assert_eq!(err.kind(), ErrorKind::WriteZero); - /// assert_eq!(recovered_writer.buffer(), b"t be actually written"); - /// ``` - #[stable(feature = "io_into_inner_error_parts", since = "1.55.0")] - pub fn into_parts(self) -> (Error, W) { - (self.1, self.0) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From> for Error { - fn from(iie: IntoInnerError) -> Error { - iie.1 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for IntoInnerError {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for IntoInnerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.error().fmt(f) - } -} diff --git a/crates/std/src/io/buffered/tests.rs b/crates/std/src/io/buffered/tests.rs deleted file mode 100644 index 497e376..0000000 --- a/crates/std/src/io/buffered/tests.rs +++ /dev/null @@ -1,1087 +0,0 @@ -use crate::io::prelude::*; -use crate::io::{ - self, BorrowedBuf, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom, -}; -use crate::mem::MaybeUninit; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::{panic, thread}; - -/// A dummy reader intended at testing short-reads propagation. -pub struct ShortReader { - lengths: Vec, -} - -// FIXME: rustfmt and tidy disagree about the correct formatting of this -// function. This leads to issues for users with editors configured to -// rustfmt-on-save. -impl Read for ShortReader { - fn read(&mut self, _: &mut [u8]) -> io::Result { - if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) } - } -} - -#[test] -fn test_buffered_reader() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 3); - assert_eq!(buf, [5, 6, 7]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 2); - assert_eq!(buf, [0, 1]); - assert_eq!(reader.buffer(), []); - - let mut buf = [0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [2]); - assert_eq!(reader.buffer(), [3]); - - let mut buf = [0, 0, 0]; - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [3, 0, 0]); - assert_eq!(reader.buffer(), []); - - let nread = reader.read(&mut buf); - assert_eq!(nread.unwrap(), 1); - assert_eq!(buf, [4, 0, 0]); - assert_eq!(reader.buffer(), []); - - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_buffered_reader_read_buf() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, inner); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert_eq!(buf.filled(), [5, 6, 7]); - assert_eq!(reader.buffer(), []); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 2]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert_eq!(buf.filled(), [0, 1]); - assert_eq!(reader.buffer(), []); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert_eq!(buf.filled(), [2]); - assert_eq!(reader.buffer(), [3]); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 3]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert_eq!(buf.filled(), [3]); - assert_eq!(reader.buffer(), []); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert_eq!(buf.filled(), [3, 4]); - assert_eq!(reader.buffer(), []); - - buf.clear(); - - reader.read_buf(buf.unfilled()).unwrap(); - - assert!(buf.filled().is_empty()); -} - -#[test] -fn test_buffered_reader_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); - - assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert_eq!(reader.stream_position().ok(), Some(3)); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); - assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); - reader.consume(1); - assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3)); -} - -#[test] -fn test_buffered_reader_seek_relative() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); - - assert!(reader.seek_relative(3).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(0).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(1).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); - assert!(reader.seek_relative(-1).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(2).is_ok()); - assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); -} - -#[test] -fn test_buffered_reader_stream_position() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); - - assert_eq!(reader.stream_position().ok(), Some(0)); - assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); - assert_eq!(reader.stream_position().ok(), Some(3)); - // relative seeking within the buffer and reading position should keep the buffer - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert!(reader.seek_relative(0).is_ok()); - assert_eq!(reader.stream_position().ok(), Some(3)); - assert_eq!(reader.buffer(), &[0, 1][..]); - assert!(reader.seek_relative(1).is_ok()); - assert_eq!(reader.stream_position().ok(), Some(4)); - assert_eq!(reader.buffer(), &[1][..]); - assert!(reader.seek_relative(-1).is_ok()); - assert_eq!(reader.stream_position().ok(), Some(3)); - assert_eq!(reader.buffer(), &[0, 1][..]); - // relative seeking outside the buffer will discard it - assert!(reader.seek_relative(2).is_ok()); - assert_eq!(reader.stream_position().ok(), Some(5)); - assert_eq!(reader.buffer(), &[][..]); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_buffered_reader_stream_position_panic() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(4, io::Cursor::new(inner)); - - // cause internal buffer to be filled but read only partially - let mut buffer = [0, 0]; - assert!(reader.read_exact(&mut buffer).is_ok()); - // rewinding the internal reader will cause buffer to lose sync - let inner = reader.get_mut(); - assert!(inner.seek(SeekFrom::Start(0)).is_ok()); - // overflow when subtracting the remaining buffer size from current position - let result = panic::catch_unwind(panic::AssertUnwindSafe(|| reader.stream_position().ok())); - assert!(result.is_err()); -} - -#[test] -fn test_buffered_reader_invalidated_after_read() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); - - assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); - reader.consume(3); - - let mut buffer = [0, 0, 0, 0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(5)); - assert_eq!(buffer, [0, 1, 2, 3, 4]); - - assert!(reader.seek_relative(-2).is_ok()); - let mut buffer = [0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(2)); - assert_eq!(buffer, [3, 4]); -} - -#[test] -fn test_buffered_reader_invalidated_after_seek() { - let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; - let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); - - assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); - reader.consume(3); - - assert!(reader.seek(SeekFrom::Current(5)).is_ok()); - - assert!(reader.seek_relative(-2).is_ok()); - let mut buffer = [0, 0]; - assert_eq!(reader.read(&mut buffer).ok(), Some(2)); - assert_eq!(buffer, [3, 4]); -} - -#[test] -fn test_buffered_reader_seek_underflow() { - // gimmick reader that yields its position modulo 256 for each byte - struct PositionReader { - pos: u64, - } - impl Read for PositionReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = buf.len(); - for x in buf { - *x = self.pos as u8; - self.pos = self.pos.wrapping_add(1); - } - Ok(len) - } - } - // note: this implementation of `Seek` is "broken" due to position - // wrapping, so calling `reader.seek(Current(0))` is semantically different - // than `reader.stream_position()` - impl Seek for PositionReader { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - match pos { - SeekFrom::Start(n) => { - self.pos = n; - } - SeekFrom::Current(n) => { - self.pos = self.pos.wrapping_add(n as u64); - } - SeekFrom::End(n) => { - self.pos = u64::MAX.wrapping_add(n as u64); - } - } - Ok(self.pos) - } - } - - let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5)); - assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); - // the following seek will require two underlying seeks - let expected = 9223372036854775802; - assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected)); - assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); - // seeking to 0 should empty the buffer. - assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); - assert_eq!(reader.get_ref().pos, expected); -} - -#[test] -fn test_buffered_reader_seek_underflow_discard_buffer_between_seeks() { - // gimmick reader that returns Err after first seek - struct ErrAfterFirstSeekReader { - first_seek: bool, - } - impl Read for ErrAfterFirstSeekReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - for x in &mut *buf { - *x = 0; - } - Ok(buf.len()) - } - } - impl Seek for ErrAfterFirstSeekReader { - fn seek(&mut self, _: SeekFrom) -> io::Result { - if self.first_seek { - self.first_seek = false; - Ok(0) - } else { - Err(io::Error::new(io::ErrorKind::Other, "oh no!")) - } - } - } - - let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true }); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..])); - - // The following seek will require two underlying seeks. The first will - // succeed but the second will fail. This should still invalidate the - // buffer. - assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err()); - assert_eq!(reader.buffer().len(), 0); -} - -#[test] -fn test_buffered_reader_read_to_end_consumes_buffer() { - let data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = BufReader::with_capacity(3, data); - let mut buf = Vec::new(); - assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2][..])); - assert_eq!(reader.read_to_end(&mut buf).ok(), Some(8)); - assert_eq!(&buf, &[0, 1, 2, 3, 4, 5, 6, 7]); - assert!(reader.buffer().is_empty()); -} - -#[test] -fn test_buffered_reader_read_to_string_consumes_buffer() { - let data: &[u8] = "deadbeef".as_bytes(); - let mut reader = BufReader::with_capacity(3, data); - let mut buf = String::new(); - assert_eq!(reader.fill_buf().ok(), Some("dea".as_bytes())); - assert_eq!(reader.read_to_string(&mut buf).ok(), Some(8)); - assert_eq!(&buf, "deadbeef"); - assert!(reader.buffer().is_empty()); -} - -#[test] -fn test_buffered_writer() { - let inner = Vec::new(); - let mut writer = BufWriter::with_capacity(2, inner); - - writer.write(&[0, 1]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.write(&[2]).unwrap(); - assert_eq!(writer.buffer(), [2]); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.write(&[3]).unwrap(); - assert_eq!(writer.buffer(), [2, 3]); - assert_eq!(*writer.get_ref(), [0, 1]); - - writer.flush().unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - - writer.write(&[4]).unwrap(); - writer.write(&[5]).unwrap(); - assert_eq!(writer.buffer(), [4, 5]); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); - - writer.write(&[6]).unwrap(); - assert_eq!(writer.buffer(), [6]); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); - - writer.write(&[7, 8]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); - - writer.write(&[9, 10, 11]).unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - - writer.flush().unwrap(); - assert_eq!(writer.buffer(), []); - assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); -} - -#[test] -fn test_buffered_writer_inner_flushes() { - let mut w = BufWriter::with_capacity(3, Vec::new()); - w.write(&[0, 1]).unwrap(); - assert_eq!(*w.get_ref(), []); - let w = w.into_inner().unwrap(); - assert_eq!(w, [0, 1]); -} - -#[test] -fn test_buffered_writer_seek() { - let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); - w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); - w.write_all(&[6, 7]).unwrap(); - assert_eq!(w.stream_position().ok(), Some(8)); - assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); - assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); - w.write_all(&[8, 9]).unwrap(); - assert_eq!(&w.into_inner().unwrap().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]); -} - -#[test] -fn test_read_until() { - let inner: &[u8] = &[0, 1, 2, 1, 0]; - let mut reader = BufReader::with_capacity(2, inner); - let mut v = Vec::new(); - reader.read_until(0, &mut v).unwrap(); - assert_eq!(v, [0]); - v.truncate(0); - reader.read_until(2, &mut v).unwrap(); - assert_eq!(v, [1, 2]); - v.truncate(0); - reader.read_until(1, &mut v).unwrap(); - assert_eq!(v, [1]); - v.truncate(0); - reader.read_until(8, &mut v).unwrap(); - assert_eq!(v, [0]); - v.truncate(0); - reader.read_until(9, &mut v).unwrap(); - assert_eq!(v, []); -} - -#[test] -fn test_line_buffer() { - let mut writer = LineWriter::new(Vec::new()); - writer.write(&[0]).unwrap(); - assert_eq!(*writer.get_ref(), []); - writer.write(&[1]).unwrap(); - assert_eq!(*writer.get_ref(), []); - writer.flush().unwrap(); - assert_eq!(*writer.get_ref(), [0, 1]); - writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); - writer.flush().unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); - writer.write(&[3, b'\n']).unwrap(); - assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); -} - -#[test] -fn test_read_line() { - let in_buf: &[u8] = b"a\nb\nc"; - let mut reader = BufReader::with_capacity(2, in_buf); - let mut s = String::new(); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "a\n"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "b\n"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, "c"); - s.truncate(0); - reader.read_line(&mut s).unwrap(); - assert_eq!(s, ""); -} - -#[test] -fn test_lines() { - let in_buf: &[u8] = b"a\nb\nc"; - let reader = BufReader::with_capacity(2, in_buf); - let mut it = reader.lines(); - assert_eq!(it.next().unwrap().unwrap(), "a".to_string()); - assert_eq!(it.next().unwrap().unwrap(), "b".to_string()); - assert_eq!(it.next().unwrap().unwrap(), "c".to_string()); - assert!(it.next().is_none()); -} - -#[test] -fn test_short_reads() { - let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] }; - let mut reader = BufReader::new(inner); - let mut buf = [0, 0]; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.read(&mut buf).unwrap(), 2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -#[should_panic] -fn dont_panic_in_drop_on_panicked_flush() { - struct FailFlushWriter; - - impl Write for FailFlushWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Err(io::Error::last_os_error()) - } - } - - let writer = FailFlushWriter; - let _writer = BufWriter::new(writer); - - // If writer panics *again* due to the flush error then the process will - // abort. - panic!(); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn panic_in_write_doesnt_flush_in_drop() { - static WRITES: AtomicUsize = AtomicUsize::new(0); - - struct PanicWriter; - - impl Write for PanicWriter { - fn write(&mut self, _: &[u8]) -> io::Result { - WRITES.fetch_add(1, Ordering::SeqCst); - panic!(); - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - thread::spawn(|| { - let mut writer = BufWriter::new(PanicWriter); - let _ = writer.write(b"hello world"); - let _ = writer.flush(); - }) - .join() - .unwrap_err(); - - assert_eq!(WRITES.load(Ordering::SeqCst), 1); -} - -#[bench] -fn bench_buffered_reader(b: &mut test::Bencher) { - b.iter(|| BufReader::new(io::empty())); -} - -#[bench] -fn bench_buffered_reader_small_reads(b: &mut test::Bencher) { - let data = (0..u8::MAX).cycle().take(1024 * 4).collect::>(); - b.iter(|| { - let mut reader = BufReader::new(&data[..]); - let mut buf = [0u8; 4]; - for _ in 0..1024 { - reader.read_exact(&mut buf).unwrap(); - core::hint::black_box(&buf); - } - }); -} - -#[bench] -fn bench_buffered_writer(b: &mut test::Bencher) { - b.iter(|| BufWriter::new(io::sink())); -} - -/// A simple `Write` target, designed to be wrapped by `LineWriter` / -/// `BufWriter` / etc, that can have its `write` & `flush` behavior -/// configured -#[derive(Default, Clone)] -struct ProgrammableSink { - // Writes append to this slice - pub buffer: Vec, - - // If true, writes will always be an error - pub always_write_error: bool, - - // If true, flushes will always be an error - pub always_flush_error: bool, - - // If set, only up to this number of bytes will be written in a single - // call to `write` - pub accept_prefix: Option, - - // If set, counts down with each write, and writes return an error - // when it hits 0 - pub max_writes: Option, - - // If set, attempting to write when max_writes == Some(0) will be an - // error; otherwise, it will return Ok(0). - pub error_after_max_writes: bool, -} - -impl Write for ProgrammableSink { - fn write(&mut self, data: &[u8]) -> io::Result { - if self.always_write_error { - return Err(io::Error::new(io::ErrorKind::Other, "test - always_write_error")); - } - - match self.max_writes { - Some(0) if self.error_after_max_writes => { - return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes")); - } - Some(0) => return Ok(0), - Some(ref mut count) => *count -= 1, - None => {} - } - - let len = match self.accept_prefix { - None => data.len(), - Some(prefix) => data.len().min(prefix), - }; - - let data = &data[..len]; - self.buffer.extend_from_slice(data); - - Ok(len) - } - - fn flush(&mut self) -> io::Result<()> { - if self.always_flush_error { - Err(io::Error::new(io::ErrorKind::Other, "test - always_flush_error")) - } else { - Ok(()) - } - } -} - -/// Previously the `LineWriter` could successfully write some bytes but -/// then fail to report that it has done so. Additionally, an erroneous -/// flush after a successful write was permanently ignored. -/// -/// Test that a line writer correctly reports the number of written bytes, -/// and that it attempts to flush buffered lines from previous writes -/// before processing new data -/// -/// Regression test for #37807 -#[test] -fn erroneous_flush_retried() { - let writer = ProgrammableSink { - // Only write up to 4 bytes at a time - accept_prefix: Some(4), - - // Accept the first two writes, then error the others - max_writes: Some(2), - error_after_max_writes: true, - - ..Default::default() - }; - - // This should write the first 4 bytes. The rest will be buffered, out - // to the last newline. - let mut writer = LineWriter::new(writer); - assert_eq!(writer.write(b"a\nb\nc\nd\ne").unwrap(), 8); - - // This write should attempt to flush "c\nd\n", then buffer "e". No - // errors should happen here because no further writes should be - // attempted against `writer`. - assert_eq!(writer.write(b"e").unwrap(), 1); - assert_eq!(&writer.get_ref().buffer, b"a\nb\nc\nd\n"); -} - -#[test] -fn line_vectored() { - let mut a = LineWriter::new(Vec::new()); - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(b"\n"), - IoSlice::new(&[]), - IoSlice::new(b"a"), - ]) - .unwrap(), - 2, - ); - assert_eq!(a.get_ref(), b"\n"); - - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(b"b"), - IoSlice::new(&[]), - IoSlice::new(b"a"), - IoSlice::new(&[]), - IoSlice::new(b"c"), - ]) - .unwrap(), - 3, - ); - assert_eq!(a.get_ref(), b"\n"); - a.flush().unwrap(); - assert_eq!(a.get_ref(), b"\nabac"); - assert_eq!(a.write_vectored(&[]).unwrap(), 0); - assert_eq!( - a.write_vectored(&[ - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - ]) - .unwrap(), - 0, - ); - assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3); - assert_eq!(a.get_ref(), b"\nabaca\nb"); -} - -#[test] -fn line_vectored_partial_and_errors() { - use alloc_crate::collections::VecDeque; - - enum Call { - Write { inputs: Vec<&'static [u8]>, output: io::Result }, - Flush { output: io::Result<()> }, - } - - #[derive(Default)] - struct Writer { - calls: VecDeque, - } - - impl Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result { - match self.calls.pop_front().expect("unexpected call to write") { - Call::Write { inputs, output } => { - assert_eq!(inputs, buf.iter().map(|b| &**b).collect::>()); - output - } - Call::Flush { .. } => panic!("unexpected call to write; expected a flush"), - } - } - - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - match self.calls.pop_front().expect("Unexpected call to flush") { - Call::Flush { output } => output, - Call::Write { .. } => panic!("unexpected call to flush; expected a write"), - } - } - } - - impl Drop for Writer { - fn drop(&mut self) { - if !thread::panicking() { - assert_eq!(self.calls.len(), 0); - } - } - } - - // partial writes keep going - let mut a = LineWriter::new(Writer::default()); - a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap(); - - a.get_mut().calls.push_back(Call::Write { inputs: vec![b"abc"], output: Ok(1) }); - a.get_mut().calls.push_back(Call::Write { inputs: vec![b"bc"], output: Ok(2) }); - a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\n"], output: Ok(2) }); - - a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap(); - - a.get_mut().calls.push_back(Call::Flush { output: Ok(()) }); - a.flush().unwrap(); - - // erroneous writes stop and don't write more - a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\na"], output: Err(err()) }); - a.get_mut().calls.push_back(Call::Flush { output: Ok(()) }); - assert!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).is_err()); - a.flush().unwrap(); - - fn err() -> io::Error { - io::Error::new(io::ErrorKind::Other, "x") - } -} - -/// Test that, in cases where vectored writing is not enabled, the -/// LineWriter uses the normal `write` call, which more-correctly handles -/// partial lines -#[test] -fn line_vectored_ignored() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::new(writer); - - let content = [ - IoSlice::new(&[]), - IoSlice::new(b"Line 1\nLine"), - IoSlice::new(b" 2\nLine 3\nL"), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(b"ine 4"), - IoSlice::new(b"\nLine 5\n"), - ]; - - let count = writer.write_vectored(&content).unwrap(); - assert_eq!(count, 11); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); - - let count = writer.write_vectored(&content[2..]).unwrap(); - assert_eq!(count, 11); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); - - let count = writer.write_vectored(&content[5..]).unwrap(); - assert_eq!(count, 5); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); - - let count = writer.write_vectored(&content[6..]).unwrap(); - assert_eq!(count, 8); - assert_eq!( - writer.get_ref().buffer.as_slice(), - b"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n".as_ref() - ); -} - -/// Test that, given this input: -/// -/// Line 1\n -/// Line 2\n -/// Line 3\n -/// Line 4 -/// -/// And given a result that only writes to midway through Line 2 -/// -/// That only up to the end of Line 3 is buffered -/// -/// This behavior is desirable because it prevents flushing partial lines -#[test] -fn partial_write_buffers_line() { - let writer = ProgrammableSink { accept_prefix: Some(13), ..Default::default() }; - let mut writer = LineWriter::new(writer); - - assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3\nLine4").unwrap(), 21); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2"); - - assert_eq!(writer.write(b"Line 4").unwrap(), 6); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n"); -} - -/// Test that, given this input: -/// -/// Line 1\n -/// Line 2\n -/// Line 3 -/// -/// And given that the full write of lines 1 and 2 was successful -/// That data up to Line 3 is buffered -#[test] -fn partial_line_buffered_after_line_write() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::new(writer); - - assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3").unwrap(), 20); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\n"); - - assert!(writer.flush().is_ok()); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3"); -} - -/// Test that for calls to LineBuffer::write where the passed bytes do not contain -/// a newline and on their own are greater in length than the internal buffer, the -/// passed bytes are immediately written to the inner writer. -#[test] -fn long_line_flushed() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::with_capacity(5, writer); - - assert_eq!(writer.write(b"0123456789").unwrap(), 10); - assert_eq!(&writer.get_ref().buffer, b"0123456789"); -} - -/// Test that, given a very long partial line *after* successfully -/// flushing a complete line, no additional writes take place. This assures -/// the property that `write` should make at-most-one attempt to write -/// new data. -#[test] -fn line_long_tail_not_flushed() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::with_capacity(5, writer); - - // Assert that Line 1\n is flushed and the long tail isn't. - let bytes = b"Line 1\n0123456789"; - writer.write(bytes).unwrap(); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); -} - -// Test that appending to a full buffer emits a single write, flushing the buffer. -#[test] -fn line_full_buffer_flushed() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::with_capacity(5, writer); - assert_eq!(writer.write(b"01234").unwrap(), 5); - - // Because the buffer is full, this subsequent write will flush it - assert_eq!(writer.write(b"5").unwrap(), 1); - assert_eq!(&writer.get_ref().buffer, b"01234"); -} - -/// Test that, if an attempt to pre-flush buffered data returns Ok(0), -/// this is propagated as an error. -#[test] -fn line_buffer_write0_error() { - let writer = ProgrammableSink { - // Accept one write, then return Ok(0) on subsequent ones - max_writes: Some(1), - - ..Default::default() - }; - let mut writer = LineWriter::new(writer); - - // This should write "Line 1\n" and buffer "Partial" - assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); - - // This will attempt to flush "partial", which will return Ok(0), which - // needs to be an error, because we've already informed the client - // that we accepted the write. - let err = writer.write(b" Line End\n").unwrap_err(); - assert_eq!(err.kind(), ErrorKind::WriteZero); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); -} - -/// Test that, if a write returns Ok(0) after a successful pre-flush, this -/// is propagated as Ok(0) -#[test] -fn line_buffer_write0_normal() { - let writer = ProgrammableSink { - // Accept two writes, then return Ok(0) on subsequent ones - max_writes: Some(2), - - ..Default::default() - }; - let mut writer = LineWriter::new(writer); - - // This should write "Line 1\n" and buffer "Partial" - assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); - - // This will flush partial, which will succeed, but then return Ok(0) - // when flushing " Line End\n" - assert_eq!(writer.write(b" Line End\n").unwrap(), 0); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nPartial"); -} - -/// LineWriter has a custom `write_all`; make sure it works correctly -#[test] -fn line_write_all() { - let writer = ProgrammableSink { - // Only write 5 bytes at a time - accept_prefix: Some(5), - ..Default::default() - }; - let mut writer = LineWriter::new(writer); - - writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial").unwrap(); - assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\nLine 4\n"); - writer.write_all(b" Line 5\n").unwrap(); - assert_eq!( - writer.get_ref().buffer.as_slice(), - b"Line 1\nLine 2\nLine 3\nLine 4\nPartial Line 5\n".as_ref(), - ); -} - -#[test] -fn line_write_all_error() { - let writer = ProgrammableSink { - // Only accept up to 3 writes of up to 5 bytes each - accept_prefix: Some(5), - max_writes: Some(3), - ..Default::default() - }; - - let mut writer = LineWriter::new(writer); - let res = writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial"); - assert!(res.is_err()); - // An error from write_all leaves everything in an indeterminate state, - // so there's nothing else to test here -} - -/// Under certain circumstances, the old implementation of LineWriter -/// would try to buffer "to the last newline" but be forced to buffer -/// less than that, leading to inappropriate partial line writes. -/// Regression test for that issue. -#[test] -fn partial_multiline_buffering() { - let writer = ProgrammableSink { - // Write only up to 5 bytes at a time - accept_prefix: Some(5), - ..Default::default() - }; - - let mut writer = LineWriter::with_capacity(10, writer); - - let content = b"AAAAABBBBB\nCCCCDDDDDD\nEEE"; - - // When content is written, LineWriter will try to write blocks A, B, - // C, and D. Only block A will succeed. Under the old behavior, LineWriter - // would then try to buffer B, C and D, but because its capacity is 10, - // it will only be able to buffer B and C. We don't want to buffer - // partial lines concurrent with whole lines, so the correct behavior - // is to buffer only block B (out to the newline) - assert_eq!(writer.write(content).unwrap(), 11); - assert_eq!(writer.get_ref().buffer, *b"AAAAA"); - - writer.flush().unwrap(); - assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB\n"); -} - -/// Same as test_partial_multiline_buffering, but in the event NO full lines -/// fit in the buffer, just buffer as much as possible -#[test] -fn partial_multiline_buffering_without_full_line() { - let writer = ProgrammableSink { - // Write only up to 5 bytes at a time - accept_prefix: Some(5), - ..Default::default() - }; - - let mut writer = LineWriter::with_capacity(5, writer); - - let content = b"AAAAABBBBBBBBBB\nCCCCC\nDDDDD"; - - // When content is written, LineWriter will try to write blocks A, B, - // and C. Only block A will succeed. Under the old behavior, LineWriter - // would then try to buffer B and C, but because its capacity is 5, - // it will only be able to buffer part of B. Because it's not possible - // for it to buffer any complete lines, it should buffer as much of B as - // possible - assert_eq!(writer.write(content).unwrap(), 10); - assert_eq!(writer.get_ref().buffer, *b"AAAAA"); - - writer.flush().unwrap(); - assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB"); -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum RecordedEvent { - Write(String), - Flush, -} - -#[derive(Debug, Clone, Default)] -struct WriteRecorder { - pub events: Vec, -} - -impl Write for WriteRecorder { - fn write(&mut self, buf: &[u8]) -> io::Result { - use crate::str::from_utf8; - - self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string())); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - self.events.push(RecordedEvent::Flush); - Ok(()) - } -} - -/// Test that a normal, formatted writeln only results in a single write -/// call to the underlying writer. A naive implementation of -/// LineWriter::write_all results in two writes: one of the buffered data, -/// and another of the final substring in the formatted set -#[test] -fn single_formatted_write() { - let writer = WriteRecorder::default(); - let mut writer = LineWriter::new(writer); - - // Under a naive implementation of LineWriter, this will result in two - // writes: "hello, world" and "!\n", because write() has to flush the - // buffer before attempting to write the last "!\n". write_all shouldn't - // have this limitation. - writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap(); - assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]); -} - -#[test] -fn bufreader_full_initialize() { - struct OneByteReader; - impl Read for OneByteReader { - fn read(&mut self, buf: &mut [u8]) -> crate::io::Result { - if buf.len() > 0 { - buf[0] = 0; - Ok(1) - } else { - Ok(0) - } - } - } - let mut reader = BufReader::new(OneByteReader); - // Nothing is initialized yet. - assert_eq!(reader.initialized(), 0); - - let buf = reader.fill_buf().unwrap(); - // We read one byte... - assert_eq!(buf.len(), 1); - // But we initialized the whole buffer! - assert_eq!(reader.initialized(), reader.capacity()); -} - -/// This is a regression test for https://github.com/rust-lang/rust/issues/127584. -#[test] -fn bufwriter_aliasing() { - use crate::io::{BufWriter, Cursor}; - let mut v = vec![0; 1024]; - let c = Cursor::new(&mut v); - let w = BufWriter::new(Box::new(c)); - let _ = w.into_parts(); -} diff --git a/crates/std/src/io/copy.rs b/crates/std/src/io/copy.rs deleted file mode 100644 index b28fea8..0000000 --- a/crates/std/src/io/copy.rs +++ /dev/null @@ -1,297 +0,0 @@ -use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write}; -use crate::alloc::Allocator; -use crate::cmp; -use alloc_crate::collections::VecDeque; -use crate::io::IoSlice; -use crate::mem::MaybeUninit; -use crate::sys::io::{CopyState, kernel_copy}; - -#[cfg(test)] -mod tests; - -/// Copies the entire contents of a reader into a writer. -/// -/// This function will continuously read data from `reader` and then -/// write it into `writer` in a streaming fashion until `reader` -/// returns EOF. -/// -/// On success, the total number of bytes that were copied from -/// `reader` to `writer` is returned. -/// -/// If you want to copy the contents of one file to another and you’re -/// working with filesystem paths, see the [`fs::copy`] function. -/// -/// [`fs::copy`]: crate::fs::copy -/// -/// # Errors -/// -/// This function will return an error immediately if any call to [`read`] or -/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are -/// handled by this function and the underlying operation is retried. -/// -/// [`read`]: Read::read -/// [`write`]: Write::write -/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted -/// -/// # Examples -/// -/// ``` -/// use std::io; -/// -/// fn main() -> io::Result<()> { -/// let mut reader: &[u8] = b"hello"; -/// let mut writer: Vec = vec![]; -/// -/// io::copy(&mut reader, &mut writer)?; -/// -/// assert_eq!(&b"hello"[..], &writer[..]); -/// Ok(()) -/// } -/// ``` -/// -/// # Platform-specific behavior -/// -/// On Linux (including Android), this function uses `copy_file_range(2)`, -/// `sendfile(2)` or `splice(2)` syscalls to move data directly between file -/// descriptors if possible. -/// -/// Note that platform-specific behavior [may change in the future][changes]. -/// -/// [changes]: crate::io#platform-specific-behavior -#[stable(feature = "rust1", since = "1.0.0")] -pub fn copy(reader: &mut R, writer: &mut W) -> Result -where - R: Read, - W: Write, -{ - match kernel_copy(reader, writer)? { - CopyState::Ended(copied) => Ok(copied), - CopyState::Fallback(copied) => { - generic_copy(reader, writer).map(|additional| copied + additional) - } - } -} - -/// The userspace read-write-loop implementation of `io::copy` that is used when -/// OS-specific specializations for copy offloading are not available or not applicable. -fn generic_copy(reader: &mut R, writer: &mut W) -> Result -where - R: Read, - W: Write, -{ - let read_buf = BufferedReaderSpec::buffer_size(reader); - let write_buf = BufferedWriterSpec::buffer_size(writer); - - if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { - return BufferedReaderSpec::copy_to(reader, writer); - } - - BufferedWriterSpec::copy_from(writer, reader) -} - -/// Specialization of the read-write loop that reuses the internal -/// buffer of a BufReader. If there's no buffer then the writer side -/// should be used instead. -trait BufferedReaderSpec { - fn buffer_size(&self) -> usize; - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result; -} - -impl BufferedReaderSpec for T -where - Self: Read, - T: ?Sized, -{ - #[inline] - default fn buffer_size(&self) -> usize { - 0 - } - - default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { - unreachable!("only called from specializations") - } -} - -impl BufferedReaderSpec for &[u8] { - fn buffer_size(&self) -> usize { - // prefer this specialization since the source "buffer" is all we'll ever need, - // even if it's small - usize::MAX - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let len = self.len(); - to.write_all(self)?; - *self = &self[len..]; - Ok(len as u64) - } -} - -impl BufferedReaderSpec for VecDeque { - fn buffer_size(&self) -> usize { - // prefer this specialization since the source "buffer" is all we'll ever need, - // even if it's small - usize::MAX - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let len = self.len(); - let (front, back) = self.as_slices(); - let bufs = &mut [IoSlice::new(front), IoSlice::new(back)]; - to.write_all_vectored(bufs)?; - self.clear(); - Ok(len as u64) - } -} - -impl BufferedReaderSpec for BufReader -where - Self: Read, - I: ?Sized, -{ - fn buffer_size(&self) -> usize { - self.capacity() - } - - fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { - let mut len = 0; - - loop { - // Hack: this relies on `impl Read for BufReader` always calling fill_buf - // if the buffer is empty, even for empty slices. - // It can't be called directly here since specialization prevents us - // from adding I: Read - match self.read(&mut []) { - Ok(_) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - let buf = self.buffer(); - if self.buffer().len() == 0 { - return Ok(len); - } - - // In case the writer side is a BufWriter then its write_all - // implements an optimization that passes through large - // buffers to the underlying writer. That code path is #[cold] - // but we're still avoiding redundant memcopies when doing - // a copy between buffered inputs and outputs. - to.write_all(buf)?; - len += buf.len() as u64; - self.discard_buffer(); - } - } -} - -/// Specialization of the read-write loop that either uses a stack buffer -/// or reuses the internal buffer of a BufWriter -trait BufferedWriterSpec: Write { - fn buffer_size(&self) -> usize; - - fn copy_from(&mut self, reader: &mut R) -> Result; -} - -impl BufferedWriterSpec for W { - #[inline] - default fn buffer_size(&self) -> usize { - 0 - } - - default fn copy_from(&mut self, reader: &mut R) -> Result { - stack_buffer_copy(reader, self) - } -} - -impl BufferedWriterSpec for BufWriter { - fn buffer_size(&self) -> usize { - self.capacity() - } - - fn copy_from(&mut self, reader: &mut R) -> Result { - if self.capacity() < DEFAULT_BUF_SIZE { - return stack_buffer_copy(reader, self); - } - - let mut len = 0; - let mut init = 0; - - loop { - let buf = self.buffer_mut(); - let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); - - unsafe { - // SAFETY: init is either 0 or the init_len from the previous iteration. - read_buf.set_init(init); - } - - if read_buf.capacity() >= DEFAULT_BUF_SIZE { - let mut cursor = read_buf.unfilled(); - match reader.read_buf(cursor.reborrow()) { - Ok(()) => { - let bytes_read = cursor.written(); - - if bytes_read == 0 { - return Ok(len); - } - - init = read_buf.init_len() - bytes_read; - len += bytes_read as u64; - - // SAFETY: BorrowedBuf guarantees all of its filled bytes are init - unsafe { buf.set_len(buf.len() + bytes_read) }; - - // Read again if the buffer still has enough capacity, as BufWriter itself would do - // This will occur if the reader returns short reads - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } else { - // All the bytes that were already in the buffer are initialized, - // treat them as such when the buffer is flushed. - init += buf.len(); - - self.flush_buf()?; - } - } - } -} - -impl BufferedWriterSpec for Vec { - fn buffer_size(&self) -> usize { - cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) - } - - fn copy_from(&mut self, reader: &mut R) -> Result { - reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64")) - } -} - -fn stack_buffer_copy( - reader: &mut R, - writer: &mut W, -) -> Result { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - let mut len = 0; - - loop { - match reader.read_buf(buf.unfilled()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - }; - - if buf.filled().is_empty() { - break; - } - - len += buf.filled().len() as u64; - writer.write_all(buf.filled())?; - buf.clear(); - } - - Ok(len) -} diff --git a/crates/std/src/io/copy/tests.rs b/crates/std/src/io/copy/tests.rs deleted file mode 100644 index 87f8703..0000000 --- a/crates/std/src/io/copy/tests.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::cmp::{max, min}; -use alloc_crate::collections::VecDeque; -use crate::io; -use crate::io::*; - -#[test] -fn copy_copies() { - let mut r = repeat(0).take(4); - let mut w = sink(); - assert_eq!(copy(&mut r, &mut w).unwrap(), 4); - - let mut r = repeat(0).take(1 << 17); - assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); -} - -struct ShortReader { - cap: usize, - read_size: usize, - observed_buffer: usize, -} - -impl Read for ShortReader { - fn read(&mut self, buf: &mut [u8]) -> Result { - let bytes = min(self.cap, self.read_size).min(buf.len()); - self.cap -= bytes; - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(bytes) - } -} - -struct WriteObserver { - observed_buffer: usize, -} - -impl Write for WriteObserver { - fn write(&mut self, buf: &[u8]) -> Result { - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(buf.len()) - } - - fn flush(&mut self) -> Result<()> { - Ok(()) - } -} - -#[test] -fn copy_specializes_bufwriter() { - let cap = 117 * 1024; - let buf_sz = 16 * 1024; - let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; - let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); - assert_eq!( - copy(&mut r, &mut w).unwrap(), - cap as u64, - "expected the whole capacity to be copied" - ); - assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); - assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); -} - -#[test] -fn copy_specializes_bufreader() { - let mut source = vec![0; 768 * 1024]; - source[1] = 42; - let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); - - let mut sink = Vec::new(); - assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); - assert_eq!(source.as_slice(), sink.as_slice()); - - let buf_sz = 71 * 1024; - assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition"); - - let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); - let mut sink = WriteObserver { observed_buffer: 0 }; - assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); - assert_eq!( - sink.observed_buffer, buf_sz, - "expected a large buffer to be provided to the writer" - ); -} - -#[test] -fn copy_specializes_to_vec() { - let cap = DEFAULT_BUF_SIZE * 10; - let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE }; - let mut sink = Vec::new(); - let copied = io::copy(&mut source, &mut sink).unwrap(); - assert_eq!(cap as u64, copied); - assert_eq!(sink.len() as u64, copied); - assert!( - source.observed_buffer > DEFAULT_BUF_SIZE, - "expected a large buffer to be provided to the reader, got {}", - source.observed_buffer - ); -} - -#[test] -fn copy_specializes_from_vecdeque() { - let mut source = VecDeque::with_capacity(100 * 1024); - for _ in 0..20 * 1024 { - source.push_front(0); - } - for _ in 0..20 * 1024 { - source.push_back(0); - } - let mut sink = WriteObserver { observed_buffer: 0 }; - assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); - assert_eq!(20 * 1024, sink.observed_buffer); -} - -#[test] -fn copy_specializes_from_slice() { - let mut source = [1; 60 * 1024].as_slice(); - let mut sink = WriteObserver { observed_buffer: 0 }; - assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); - assert_eq!(60 * 1024, sink.observed_buffer); -} - -#[cfg(unix)] -mod io_benches { - use test::Bencher; - - use crate::fs::{File, OpenOptions}; - use crate::io::BufReader; - use crate::io::prelude::*; - - #[bench] - #[cfg_attr(target_os = "emscripten", ignore)] // no /dev - fn bench_copy_buf_reader(b: &mut Bencher) { - let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); - // use dyn to avoid specializations unrelated to readbuf - let dyn_in = &mut file_in as &mut dyn Read; - let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0)); - let mut writer = - OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); - - const BYTES: u64 = 1024 * 1024; - - b.bytes = BYTES; - - b.iter(|| { - reader.get_mut().set_limit(BYTES); - crate::io::copy(&mut reader, &mut writer).unwrap() - }); - } -} diff --git a/crates/std/src/io/cursor.rs b/crates/std/src/io/cursor.rs deleted file mode 100644 index d7131e2..0000000 --- a/crates/std/src/io/cursor.rs +++ /dev/null @@ -1,757 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::alloc::Allocator; -use crate::cmp; -use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; - -/// A `Cursor` wraps an in-memory buffer and provides it with a -/// [`Seek`] implementation. -/// -/// `Cursor`s are used with in-memory buffers, anything implementing -/// [AsRef]<\[u8]>, to allow them to implement [`Read`] and/or [`Write`], -/// allowing these buffers to be used anywhere you might use a reader or writer -/// that does actual I/O. -/// -/// The standard library implements some I/O traits on various types which -/// are commonly used as a buffer, like Cursor<[Vec]\> and -/// Cursor<[&\[u8\]][bytes]>. -/// -/// # Examples -/// -/// We may want to write bytes to a [`File`] in our production -/// code, but use an in-memory buffer in our tests. We can do this with -/// `Cursor`: -/// -/// [bytes]: crate::slice "slice" -/// [`File`]: crate::fs::File -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::{self, SeekFrom}; -/// use std::fs::File; -/// -/// // a library function we've written -/// fn write_ten_bytes_at_end(mut writer: W) -> io::Result<()> { -/// writer.seek(SeekFrom::End(-10))?; -/// -/// for i in 0..10 { -/// writer.write(&[i])?; -/// } -/// -/// // all went well -/// Ok(()) -/// } -/// -/// # fn foo() -> io::Result<()> { -/// // Here's some code that uses this library function. -/// // -/// // We might want to use a BufReader here for efficiency, but let's -/// // keep this example focused. -/// let mut file = File::create("foo.txt")?; -/// // First, we need to allocate 10 bytes to be able to write into. -/// file.set_len(10)?; -/// -/// write_ten_bytes_at_end(&mut file)?; -/// # Ok(()) -/// # } -/// -/// // now let's write a test -/// #[test] -/// fn test_writes_bytes() { -/// // setting up a real File is much slower than an in-memory buffer, -/// // let's use a cursor instead -/// use std::io::Cursor; -/// let mut buff = Cursor::new(vec![0; 15]); -/// -/// write_ten_bytes_at_end(&mut buff).unwrap(); -/// -/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Default, Eq, PartialEq)] -pub struct Cursor { - inner: T, - pos: u64, -} - -impl Cursor { - /// Creates a new cursor wrapping the provided underlying in-memory buffer. - /// - /// Cursor initial position is `0` even if underlying buffer (e.g., [`Vec`]) - /// is not empty. So writing to cursor starts with overwriting [`Vec`] - /// content, not with appending to it. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] - pub const fn new(inner: T) -> Cursor { - Cursor { pos: 0, inner } - } - - /// Consumes this cursor, returning the underlying value. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let vec = buff.into_inner(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> T { - self.inner - } - - /// Gets a reference to the underlying value in this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let reference = buff.get_ref(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] - pub const fn get_ref(&self) -> &T { - &self.inner - } - - /// Gets a mutable reference to the underlying value in this cursor. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying value as it may corrupt this cursor's position. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(Vec::new()); - /// # fn force_inference(_: &Cursor>) {} - /// # force_inference(&buff); - /// - /// let reference = buff.get_mut(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")] - pub const fn get_mut(&mut self) -> &mut T { - &mut self.inner - } - - /// Returns the current position of this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// use std::io::prelude::*; - /// use std::io::SeekFrom; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.position(), 0); - /// - /// buff.seek(SeekFrom::Current(2)).unwrap(); - /// assert_eq!(buff.position(), 2); - /// - /// buff.seek(SeekFrom::Current(-1)).unwrap(); - /// assert_eq!(buff.position(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] - pub const fn position(&self) -> u64 { - self.pos - } - - /// Sets the position of this cursor. - /// - /// # Examples - /// - /// ``` - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.position(), 0); - /// - /// buff.set_position(2); - /// assert_eq!(buff.position(), 2); - /// - /// buff.set_position(4); - /// assert_eq!(buff.position(), 4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")] - pub const fn set_position(&mut self, pos: u64) { - self.pos = pos; - } -} - -impl Cursor -where - T: AsRef<[u8]>, -{ - /// Splits the underlying slice at the cursor position and returns them. - /// - /// # Examples - /// - /// ``` - /// #![feature(cursor_split)] - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice())); - /// - /// buff.set_position(2); - /// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice())); - /// - /// buff.set_position(6); - /// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice())); - /// ``` - #[unstable(feature = "cursor_split", issue = "86369")] - pub fn split(&self) -> (&[u8], &[u8]) { - let slice = self.inner.as_ref(); - let pos = self.pos.min(slice.len() as u64); - slice.split_at(pos as usize) - } -} - -impl Cursor -where - T: AsMut<[u8]>, -{ - /// Splits the underlying slice at the cursor position and returns them - /// mutably. - /// - /// # Examples - /// - /// ``` - /// #![feature(cursor_split)] - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice())); - /// - /// buff.set_position(2); - /// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice())); - /// - /// buff.set_position(6); - /// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice())); - /// ``` - #[unstable(feature = "cursor_split", issue = "86369")] - pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) { - let slice = self.inner.as_mut(); - let pos = self.pos.min(slice.len() as u64); - slice.split_at_mut(pos as usize) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Cursor -where - T: Clone, -{ - #[inline] - fn clone(&self) -> Self { - Cursor { inner: self.inner.clone(), pos: self.pos } - } - - #[inline] - fn clone_from(&mut self, other: &Self) { - self.inner.clone_from(&other.inner); - self.pos = other.pos; - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl io::Seek for Cursor -where - T: AsRef<[u8]>, -{ - fn seek(&mut self, style: SeekFrom) -> io::Result { - let (base_pos, offset) = match style { - SeekFrom::Start(n) => { - self.pos = n; - return Ok(n); - } - SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), - SeekFrom::Current(n) => (self.pos, n), - }; - match base_pos.checked_add_signed(offset) { - Some(n) => { - self.pos = n; - Ok(self.pos) - } - None => Err(io::const_error!( - ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", - )), - } - } - - fn stream_len(&mut self) -> io::Result { - Ok(self.inner.as_ref().len() as u64) - } - - fn stream_position(&mut self) -> io::Result { - Ok(self.pos) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Cursor -where - T: AsRef<[u8]>, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = Read::read(&mut Cursor::split(self).1, buf)?; - self.pos += n as u64; - Ok(n) - } - - fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let prev_written = cursor.written(); - - Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; - - self.pos += (cursor.written() - prev_written) as u64; - - Ok(()) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nread = 0; - for buf in bufs { - let n = self.read(buf)?; - nread += n; - if n < buf.len() { - break; - } - } - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - true - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let result = Read::read_exact(&mut Cursor::split(self).1, buf); - - match result { - Ok(_) => self.pos += buf.len() as u64, - // The only possible error condition is EOF, so place the cursor at "EOF" - Err(_) => self.pos = self.inner.as_ref().len() as u64, - } - - result - } - - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let prev_written = cursor.written(); - - let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); - self.pos += (cursor.written() - prev_written) as u64; - - result - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let content = Cursor::split(self).1; - let len = content.len(); - buf.try_reserve(len)?; - buf.extend_from_slice(content); - self.pos += len as u64; - - Ok(len) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - let content = - crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; - let len = content.len(); - buf.try_reserve(len)?; - buf.push_str(content); - self.pos += len as u64; - - Ok(len) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Cursor -where - T: AsRef<[u8]>, -{ - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(Cursor::split(self).1) - } - fn consume(&mut self, amt: usize) { - self.pos += amt as u64; - } -} - -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) -} - -#[inline] -fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } -} - -#[inline] -fn slice_write_all_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result<()> { - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) -} - -/// Reserves the required space, and pads the vec with 0s if necessary. -fn reserve_and_pad( - pos_mut: &mut u64, - vec: &mut Vec, - buf_len: usize, -) -> io::Result { - let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_error!( - ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", - ) - })?; - - // For safety reasons, we don't want these numbers to overflow - // otherwise our allocation won't be enough - let desired_cap = pos.saturating_add(buf_len); - if desired_cap > vec.capacity() { - // We want our vec's total capacity - // to have room for (pos+buf_len) bytes. Reserve allocates - // based on additional elements from the length, so we need to - // reserve the difference - vec.reserve(desired_cap - vec.len()); - } - // Pad if pos is above the current len. - if pos > vec.len() { - let diff = pos - vec.len(); - // Unfortunately, `resize()` would suffice but the optimiser does not - // realise the `reserve` it does can be eliminated. So we do it manually - // to eliminate that extra branch - let spare = vec.spare_capacity_mut(); - debug_assert!(spare.len() >= diff); - // Safety: we have allocated enough capacity for this. - // And we are only writing, not reading - unsafe { - spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); - vec.set_len(pos); - } - } - - Ok(pos) -} - -/// Writes the slice to the vec without allocating. -/// -/// # Safety -/// -/// `vec` must have `buf.len()` spare capacity. -unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize -where - A: Allocator, -{ - debug_assert!(vec.capacity() >= pos + buf.len()); - unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; - pos + buf.len() -} - -/// Resizing `write_all` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result -where - A: Allocator, -{ - let buf_len = buf.len(); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to pos - unsafe { - pos = vec_write_all_unchecked(pos, vec, buf); - if pos > vec.len() { - vec.set_len(pos); - } - }; - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -/// Resizing `write_all_vectored` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all_vectored( - pos_mut: &mut u64, - vec: &mut Vec, - bufs: &[IoSlice<'_>], -) -> io::Result -where - A: Allocator, -{ - // For safety reasons, we don't want this sum to overflow ever. - // If this saturates, the reserve should panic to avoid any unsound writing. - let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to the last pos - unsafe { - for buf in bufs { - pos = vec_write_all_unchecked(pos, vec, buf); - } - if pos > vec.len() { - vec.set_len(pos); - } - } - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor<&mut [u8]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - slice_write(&mut self.pos, self.inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - slice_write_vectored(&mut self.pos, self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - slice_write_all(&mut self.pos, self.inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - slice_write_all_vectored(&mut self.pos, self.inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> -where - A: Allocator, -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write_all(&mut self.pos, self.inner, buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - vec_write_all_vectored(&mut self.pos, self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - vec_write_all(&mut self.pos, self.inner, buf)?; - Ok(()) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - vec_write_all_vectored(&mut self.pos, self.inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> -where - A: Allocator, -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - vec_write_all(&mut self.pos, &mut self.inner, buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - vec_write_all(&mut self.pos, &mut self.inner, buf)?; - Ok(()) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> -where - A: Allocator, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - slice_write(&mut self.pos, &mut self.inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - slice_write_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - slice_write_all(&mut self.pos, &mut self.inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_array", since = "1.61.0")] -impl Write for Cursor<[u8; N]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - slice_write(&mut self.pos, &mut self.inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - slice_write_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - slice_write_all(&mut self.pos, &mut self.inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/crates/std/src/io/cursor/tests.rs b/crates/std/src/io/cursor/tests.rs deleted file mode 100644 index d7c203c..0000000 --- a/crates/std/src/io/cursor/tests.rs +++ /dev/null @@ -1,567 +0,0 @@ -use crate::io::prelude::*; -use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom}; - -#[test] -fn test_vec_writer() { - let mut writer = Vec::new(); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(writer, b); -} - -#[test] -fn test_mem_writer() { - let mut writer = Cursor::new(Vec::new()); - writer.set_position(10); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(&writer.get_ref()[..10], &[0; 10]); - assert_eq!(&writer.get_ref()[10..], b); -} - -#[test] -fn test_mem_writer_preallocated() { - let mut writer = Cursor::new(vec![0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10]); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(&writer.get_ref()[..], b); -} - -#[test] -fn test_mem_mut_writer() { - let mut vec = Vec::new(); - let mut writer = Cursor::new(&mut vec); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],) - .unwrap(), - 3 - ); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(&writer.get_ref()[..], b); -} - -fn test_slice_writer(writer: &mut Cursor) -where - T: AsRef<[u8]>, - Cursor: Write, -{ - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write(&[8, 9]).unwrap(), 1); - assert_eq!(writer.write(&[10]).unwrap(), 0); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(writer.get_ref().as_ref(), b); -} - -fn test_slice_writer_vectored(writer: &mut Cursor) -where - T: AsRef<[u8]>, - Cursor: Write, -{ - assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!( - writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(), - 7, - ); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(writer.get_ref().as_ref(), b); -} - -#[test] -fn test_box_slice_writer() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); - test_slice_writer(&mut writer); -} - -#[test] -fn test_box_slice_writer_vectored() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); - test_slice_writer_vectored(&mut writer); -} - -#[test] -fn test_array_writer() { - let mut writer = Cursor::new([0u8; 9]); - test_slice_writer(&mut writer); -} - -#[test] -fn test_array_writer_vectored() { - let mut writer = Cursor::new([0u8; 9]); - test_slice_writer_vectored(&mut writer); -} - -#[test] -fn test_buf_writer() { - let mut buf = [0 as u8; 9]; - let mut writer = Cursor::new(&mut buf[..]); - test_slice_writer(&mut writer); -} - -#[test] -fn test_buf_writer_vectored() { - let mut buf = [0 as u8; 9]; - let mut writer = Cursor::new(&mut buf[..]); - test_slice_writer_vectored(&mut writer); -} - -#[test] -fn test_buf_writer_seek() { - let mut buf = [0 as u8; 8]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[1]).unwrap(), 1); - assert_eq!(writer.position(), 1); - - assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); - assert_eq!(writer.position(), 2); - assert_eq!(writer.write(&[2]).unwrap(), 1); - assert_eq!(writer.position(), 3); - - assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[3]).unwrap(), 1); - assert_eq!(writer.position(), 2); - - assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); - assert_eq!(writer.position(), 7); - assert_eq!(writer.write(&[4]).unwrap(), 1); - assert_eq!(writer.position(), 8); - } - let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; - assert_eq!(buf, b); -} - -#[test] -fn test_buf_writer_error() { - let mut buf = [0 as u8; 2]; - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.write(&[0, 0]).unwrap(), 1); - assert_eq!(writer.write(&[0, 0]).unwrap(), 0); -} - -#[test] -fn test_mem_reader() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_mem_reader_vectored() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!( - reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), - 1, - ); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),]) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_boxed_slice_reader() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_boxed_slice_reader_vectored() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!( - reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), - 1, - ); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn read_to_end() { - let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); - let mut v = Vec::new(); - reader.read_to_end(&mut v).unwrap(); - assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); -} - -#[test] -fn test_slice_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.len(), 7); - let b: &[_] = &[0]; - assert_eq!(&buf[..], b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.len(), 3); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(&buf[..], b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_slice_reader_vectored() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); - let mut buf = [0]; - assert_eq!( - reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(), - 1, - ); - assert_eq!(reader.len(), 7); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf1 = [0; 4]; - let mut buf2 = [0; 4]; - assert_eq!( - reader - .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) - .unwrap(), - 7, - ); - let b1: &[_] = &[1, 2, 3, 4]; - let b2: &[_] = &[5, 6, 7]; - assert_eq!(buf1, b1); - assert_eq!(&buf2[..3], b2); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn test_read_exact() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let reader = &mut &in_buf[..]; - let mut buf = []; - assert!(reader.read_exact(&mut buf).is_ok()); - let mut buf = [8]; - assert!(reader.read_exact(&mut buf).is_ok()); - assert_eq!(buf[0], 0); - assert_eq!(reader.len(), 7); - let mut buf = [0, 0, 0, 0, 0, 0, 0]; - assert!(reader.read_exact(&mut buf).is_ok()); - assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); - assert_eq!(reader.len(), 0); - let mut buf = [0]; - assert!(reader.read_exact(&mut buf).is_err()); -} - -#[test] -fn test_buf_reader() { - let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = Cursor::new(&in_buf[..]); - let mut buf = []; - assert_eq!(reader.read(&mut buf).unwrap(), 0); - assert_eq!(reader.position(), 0); - let mut buf = [0]; - assert_eq!(reader.read(&mut buf).unwrap(), 1); - assert_eq!(reader.position(), 1); - let b: &[_] = &[0]; - assert_eq!(buf, b); - let mut buf = [0; 4]; - assert_eq!(reader.read(&mut buf).unwrap(), 4); - assert_eq!(reader.position(), 5); - let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf, b); - assert_eq!(reader.read(&mut buf).unwrap(), 3); - let b: &[_] = &[5, 6, 7]; - assert_eq!(&buf[..3], b); - assert_eq!(reader.read(&mut buf).unwrap(), 0); -} - -#[test] -fn seek_past_end() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.read(&mut [0]).unwrap(), 0); - - let mut r = Cursor::new(vec![10]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.read(&mut [0]).unwrap(), 0); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 0); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 0); -} - -#[test] -fn seek_past_i64() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut r = Cursor::new(vec![10]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); - assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6); - assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); - assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); - assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); - assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); -} - -#[test] -fn seek_before_0() { - let buf = [0xff]; - let mut r = Cursor::new(&buf[..]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut r = Cursor::new(vec![10]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut buf = [0]; - let mut r = Cursor::new(&mut buf[..]); - assert!(r.seek(SeekFrom::End(-2)).is_err()); - - let mut r = Cursor::new(vec![10].into_boxed_slice()); - assert!(r.seek(SeekFrom::End(-2)).is_err()); -} - -#[test] -fn test_seekable_mem_writer() { - let mut writer = Cursor::new(Vec::::new()); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[3, 4]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); - assert_eq!(writer.write(&[0, 1]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); - assert_eq!(writer.write(&[1, 2]).unwrap(), 2); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; - assert_eq!(&writer.get_ref()[..], b); - - assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); - assert_eq!(writer.write(&[1]).unwrap(), 1); - let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; - assert_eq!(&writer.get_ref()[..], b); -} - -#[test] -fn vec_seek_past_end() { - let mut r = Cursor::new(Vec::new()); - assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); - assert_eq!(r.write(&[3]).unwrap(), 1); -} - -#[test] -fn vec_seek_before_0() { - let mut r = Cursor::new(Vec::new()); - assert!(r.seek(SeekFrom::End(-2)).is_err()); -} - -#[test] -#[cfg(target_pointer_width = "32")] -fn vec_seek_and_write_past_usize_max() { - let mut c = Cursor::new(Vec::new()); - c.set_position(usize::MAX as u64 + 1); - assert!(c.write_all(&[1, 2, 3]).is_err()); -} - -#[test] -fn test_partial_eq() { - assert_eq!(Cursor::new(Vec::::new()), Cursor::new(Vec::::new())); -} - -#[test] -fn test_eq() { - struct AssertEq(pub T); - - let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); -} - -#[allow(dead_code)] -fn const_cursor() { - const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]); - const _: &&[u8] = CURSOR.get_ref(); - const _: u64 = CURSOR.position(); -} - -#[bench] -fn bench_write_vec(b: &mut test::Bencher) { - let slice = &[1; 128]; - - b.iter(|| { - let mut buf = b"some random data to overwrite".to_vec(); - let mut cursor = Cursor::new(&mut buf); - - let _ = cursor.write_all(slice); - test::black_box(&cursor); - }) -} - -#[bench] -fn bench_write_vec_vectored(b: &mut test::Bencher) { - let slices = [ - IoSlice::new(&[1; 128]), - IoSlice::new(&[2; 256]), - IoSlice::new(&[3; 512]), - IoSlice::new(&[4; 1024]), - IoSlice::new(&[5; 2048]), - IoSlice::new(&[6; 4096]), - IoSlice::new(&[7; 8192]), - IoSlice::new(&[8; 8192 * 2]), - ]; - - b.iter(|| { - let mut buf = b"some random data to overwrite".to_vec(); - let mut cursor = Cursor::new(&mut buf); - - let mut slices = slices; - let _ = cursor.write_all_vectored(&mut slices); - test::black_box(&cursor); - }) -} diff --git a/crates/std/src/io/error.rs b/crates/std/src/io/error.rs deleted file mode 100644 index bf8b615..0000000 --- a/crates/std/src/io/error.rs +++ /dev/null @@ -1,1085 +0,0 @@ -#[cfg(test)] -mod tests; - -// On 64-bit platforms, `io::Error` may use a bit-packed representation to -// reduce size. However, this representation assumes that error codes are -// always 32-bit wide. -// -// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. -// Therefore, the packed representation is explicitly disabled for UEFI -// targets, and the unpacked representation must be used instead. -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -mod repr_bitpacked; -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -use repr_bitpacked::Repr; - -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; - -use crate::{error, fmt, result, sys}; - -/// A specialized [`Result`] type for I/O operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This type alias is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`std::result::Result`][`Result`]. -/// -/// [`std::io`]: crate::io -/// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -/// Common errors constants for use in std -#[allow(dead_code)] -impl Error { - pub(crate) const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - pub(crate) const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( - ErrorKind::NotFound, - "the number of hardware threads is not known for the target platform", - ); - - pub(crate) const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - pub(crate) const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - pub(crate) const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - pub(crate) const NO_ADDRESSES: Self = - const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc_crate::ffi::NulError`] into a [`Error`]. - fn from(_: alloc_crate::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") - } -} - -#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { - /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. - /// - /// `TryReserveError` won't be available as the error `source()`, - /// but this may change in the future. - fn from(_: alloc_crate::collections::TryReserveError) -> Error { - // ErrorData::Custom allocates, which isn't great for handling OOM errors. - ErrorKind::OutOfMemory.into() - } -} - -// Only derive debug in tests, to make sure it -// doesn't accidentally get printed. -#[cfg_attr(test, derive(Debug))] -enum ErrorData { - Os(RawOsError), - Simple(ErrorKind), - SimpleMessage(&'static SimpleMessage), - Custom(C), -} - -/// The type of raw OS error codes returned by [`Error::raw_os_error`]. -/// -/// This is an [`i32`] on all currently supported platforms, but platforms -/// added in the future (such as UEFI) may use a different primitive type like -/// [`usize`]. Use `as`or [`into`] conversions where applicable to ensure maximum -/// portability. -/// -/// [`into`]: Into::into -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = sys::io::RawOsError; - -// `#[repr(align(4))]` is probably redundant, it should have that value or -// higher already. We include it just because repr_bitpacked.rs's encoding -// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the -// alignment required by the struct, only increase it). -// -// If we add more variants to ErrorData, this can be increased to 8, but it -// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or -// whatever cfg we're using to enable the `repr_bitpacked` code, since only the -// that version needs the alignment, and 8 is higher than the alignment we'll -// have on 32 bit platforms. -// -// (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't -// matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -#[repr(align(4))] -#[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, -} - -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semiopaque"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) -} - -// As with `SimpleMessage`: `#[repr(align(4))]` here is just because -// repr_bitpacked's encoding requires it. In practice it almost certainly be -// already be this high or higher. -#[derive(Debug)] -#[repr(align(4))] -struct Custom { - kind: ErrorKind, - error: Box, -} - -/// A list specifying general categories of I/O error. -/// -/// This list is intended to grow over time and it is not recommended to -/// exhaustively match against it. -/// -/// It is used with the [`io::Error`] type. -/// -/// [`io::Error`]: Error -/// -/// # Handling errors and matching on `ErrorKind` -/// -/// In application code, use `match` for the `ErrorKind` values you are -/// expecting; use `_` to match "all other errors". -/// -/// In comprehensive and thorough tests that want to verify that a test doesn't -/// return any known incorrect error kind, you may want to cut-and-paste the -/// current full list of errors from here into your test code, and then match -/// `_` as the correct case. This seems counterintuitive, but it will make your -/// tests more robust. In particular, if you want to verify that your code does -/// produce an unrecognized error kind, the robust solution is to check for all -/// the recognized error kinds and fail in those cases. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "io_errorkind")] -#[allow(deprecated)] -#[non_exhaustive] -pub enum ErrorKind { - /// An entity was not found, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - NotFound, - /// The operation lacked the necessary privileges to complete. - #[stable(feature = "rust1", since = "1.0.0")] - PermissionDenied, - /// The connection was refused by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionRefused, - /// The connection was reset by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionReset, - /// The remote host is not reachable. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - HostUnreachable, - /// The network containing the remote host is not reachable. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NetworkUnreachable, - /// The connection was aborted (terminated) by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionAborted, - /// The network operation failed because it was not connected yet. - #[stable(feature = "rust1", since = "1.0.0")] - NotConnected, - /// A socket address could not be bound because the address is already in - /// use elsewhere. - #[stable(feature = "rust1", since = "1.0.0")] - AddrInUse, - /// A nonexistent interface was requested or the requested address was not - /// local. - #[stable(feature = "rust1", since = "1.0.0")] - AddrNotAvailable, - /// The system's networking is down. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NetworkDown, - /// The operation failed because a pipe was closed. - #[stable(feature = "rust1", since = "1.0.0")] - BrokenPipe, - /// An entity already exists, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - AlreadyExists, - /// The operation needs to block to complete, but the blocking operation was - /// requested to not occur. - #[stable(feature = "rust1", since = "1.0.0")] - WouldBlock, - /// A filesystem object is, unexpectedly, not a directory. - /// - /// For example, a filesystem path was specified where one of the intermediate directory - /// components was, in fact, a plain file. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NotADirectory, - /// The filesystem object is, unexpectedly, a directory. - /// - /// A directory was specified when a non-directory was expected. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - IsADirectory, - /// A non-empty directory was specified where an empty directory was expected. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - DirectoryNotEmpty, - /// The filesystem or storage medium is read-only, but a write operation was attempted. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ReadOnlyFilesystem, - /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. - /// - /// There was a loop (or excessively long chain) resolving a filesystem object - /// or file IO object. - /// - /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the - /// system-specific limit on the depth of symlink traversal. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemLoop, - /// Stale network file handle. - /// - /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated - /// by problems with the network or server. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - StaleNetworkFileHandle, - /// A parameter was incorrect. - #[stable(feature = "rust1", since = "1.0.0")] - InvalidInput, - /// Data not valid for the operation were encountered. - /// - /// Unlike [`InvalidInput`], this typically means that the operation - /// parameters were valid, however the error was caused by malformed - /// input data. - /// - /// For example, a function that reads a file into a string will error with - /// `InvalidData` if the file's contents are not valid UTF-8. - /// - /// [`InvalidInput`]: ErrorKind::InvalidInput - #[stable(feature = "io_invalid_data", since = "1.2.0")] - InvalidData, - /// The I/O operation's timeout expired, causing it to be canceled. - #[stable(feature = "rust1", since = "1.0.0")] - TimedOut, - /// An error returned when an operation could not be completed because a - /// call to [`write`] returned [`Ok(0)`]. - /// - /// This typically means that an operation could only succeed if it wrote a - /// particular number of bytes but only a smaller number of bytes could be - /// written. - /// - /// [`write`]: crate::io::Write::write - /// [`Ok(0)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - WriteZero, - /// The underlying storage (typically, a filesystem) is full. - /// - /// This does not include out of quota errors. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - StorageFull, - /// Seek on unseekable file. - /// - /// Seeking was attempted on an open file handle which is not suitable for seeking - for - /// example, on Unix, a named pipe opened with `File::open`. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - NotSeekable, - /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] - QuotaExceeded, - /// File larger than allowed or supported. - /// - /// This might arise from a hard limit of the underlying filesystem or file access API, or from - /// an administratively imposed resource limitation. Simple disk full, and out of quota, have - /// their own errors. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - FileTooLarge, - /// Resource is busy. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ResourceBusy, - /// Executable file is busy. - /// - /// An attempt was made to write to a file which is also in use as a running program. (Not all - /// operating systems detect this situation.) - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ExecutableFileBusy, - /// Deadlock (avoided). - /// - /// A file locking operation would result in deadlock. This situation is typically detected, if - /// at all, on a best-effort basis. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - Deadlock, - /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] - CrossesDevices, - /// Too many (hard) links to the same filesystem object. - /// - /// The filesystem does not support making so many hardlinks to the same file. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - TooManyLinks, - /// A filename was invalid. - /// - /// This error can also occur if a length limit for a name was exceeded. - #[stable(feature = "io_error_invalid_filename", since = "1.87.0")] - InvalidFilename, - /// Program argument list too long. - /// - /// When trying to run an external program, a system or process limit on the size of the - /// arguments would have been exceeded. - #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] - ArgumentListTooLong, - /// This operation was interrupted. - /// - /// Interrupted operations can typically be retried. - #[stable(feature = "rust1", since = "1.0.0")] - Interrupted, - - /// This operation is unsupported on this platform. - /// - /// This means that the operation can never succeed. - #[stable(feature = "unsupported_error", since = "1.53.0")] - Unsupported, - - // ErrorKinds which are primarily categorisations for OS error - // codes should be added above. - // - /// An error returned when an operation could not be completed because an - /// "end of file" was reached prematurely. - /// - /// This typically means that an operation could only succeed if it read a - /// particular number of bytes but only a smaller number of bytes could be - /// read. - #[stable(feature = "read_exact", since = "1.6.0")] - UnexpectedEof, - - /// An operation could not be completed, because it failed - /// to allocate enough memory. - #[stable(feature = "out_of_memory_error", since = "1.54.0")] - OutOfMemory, - - /// The operation was partially successful and needs to be checked - /// later on due to not blocking. - #[unstable(feature = "io_error_inprogress", issue = "130840")] - InProgress, - - // "Unusual" error kinds which do not correspond simply to (sets - // of) OS error codes, should be added just above this comment. - // `Other` and `Uncategorized` should remain at the end: - // - /// A custom error that does not fall under any other I/O error kind. - /// - /// This can be used to construct your own [`Error`]s that do not match any - /// [`ErrorKind`]. - /// - /// This [`ErrorKind`] is not used by the standard library. - /// - /// Errors from the standard library that do not fall under any of the I/O - /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. - /// New [`ErrorKind`]s might be added in the future for some of those. - #[stable(feature = "rust1", since = "1.0.0")] - Other, - - /// Any I/O error from the standard library that's not part of this list. - /// - /// Errors that are `Uncategorized` now may move to a different or a new - /// [`ErrorKind`] variant in the future. It is not recommended to match - /// an error against `Uncategorized`; use a wildcard match (`_`) instead. - #[unstable(feature = "io_error_uncategorized", issue = "none")] - #[doc(hidden)] - Uncategorized, -} - -impl ErrorKind { - pub(crate) fn as_str(&self) -> &'static str { - use ErrorKind::*; - match *self { - // tidy-alphabetical-start - AddrInUse => "address in use", - AddrNotAvailable => "address not available", - AlreadyExists => "entity already exists", - ArgumentListTooLong => "argument list too long", - BrokenPipe => "broken pipe", - ConnectionAborted => "connection aborted", - ConnectionRefused => "connection refused", - ConnectionReset => "connection reset", - CrossesDevices => "cross-device link or rename", - Deadlock => "deadlock", - DirectoryNotEmpty => "directory not empty", - ExecutableFileBusy => "executable file busy", - FileTooLarge => "file too large", - FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", - HostUnreachable => "host unreachable", - InProgress => "in progress", - Interrupted => "operation interrupted", - InvalidData => "invalid data", - InvalidFilename => "invalid filename", - InvalidInput => "invalid input parameter", - IsADirectory => "is a directory", - NetworkDown => "network down", - NetworkUnreachable => "network unreachable", - NotADirectory => "not a directory", - NotConnected => "not connected", - NotFound => "entity not found", - NotSeekable => "seek on unseekable file", - Other => "other error", - OutOfMemory => "out of memory", - PermissionDenied => "permission denied", - QuotaExceeded => "quota exceeded", - ReadOnlyFilesystem => "read-only filesystem or storage medium", - ResourceBusy => "resource busy", - StaleNetworkFileHandle => "stale network file handle", - StorageFull => "no storage space", - TimedOut => "timed out", - TooManyLinks => "too many links", - Uncategorized => "uncategorized error", - UnexpectedEof => "unexpected end of file", - Unsupported => "unsupported", - WouldBlock => "operation would block", - WriteZero => "write zero", - // tidy-alphabetical-end - } - } -} - -#[stable(feature = "io_errorkind_display", since = "1.60.0")] -impl fmt::Display for ErrorKind { - /// Shows a human-readable description of the `ErrorKind`. - /// - /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. - /// - /// # Examples - /// ``` - /// use std::io::ErrorKind; - /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); - /// ``` - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.as_str()) - } -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion creates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{error}")); - /// ``` - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::new_simple(kind) } - } -} - -impl Error { - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this [`Error`]. - /// - /// Note that this function allocates memory on the heap. - /// If no extra payload is required, use the `From` conversion from - /// `ErrorKind`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// - /// // creating an error without payload (and without memory allocation) - /// let eof_error = Error::from(ErrorKind::UnexpectedEof); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] - #[inline(never)] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self::_new(kind, error.into()) - } - - /// Creates a new I/O error from an arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`] - /// with [`ErrorKind::Other`]. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// // errors can be created from strings - /// let custom_error = Error::other("oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::other(custom_error); - /// ``` - #[stable(feature = "io_error_other", since = "1.74.0")] - pub fn other(error: E) -> Error - where - E: Into>, - { - Self::_new(ErrorKind::Other, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } - } - - /// Creates a new I/O error from a known kind of error as well as a constant - /// message. - /// - /// This function does not allocate. - /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. - /// - /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. - #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { - Self { repr: Repr::new_simple_message(msg) } - } - - /// Returns an error representing the last OS error which occurred. - /// - /// This function reads the value of `errno` for the target platform (e.g. - /// `GetLastError` on Windows) and will return a corresponding instance of - /// [`Error`] for the error code. - /// - /// This should be called immediately after a call to a platform function, - /// otherwise the state of the error value is indeterminate. In particular, - /// other standard library functions may call platform functions that may - /// (or may not) reset the error value even if they succeed. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// let os_error = Error::last_os_error(); - /// println!("last OS error: {os_error:?}"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[doc(alias = "GetLastError")] - #[doc(alias = "errno")] - #[must_use] - #[inline] - pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) - } - - /// Creates a new instance of an [`Error`] from a particular OS error code. - /// - /// # Examples - /// - /// On Linux: - /// - /// ``` - /// # if cfg!(target_os = "linux") { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(22); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - /// - /// On Windows: - /// - /// ``` - /// # if cfg!(windows) { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(10022); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn from_raw_os_error(code: RawOsError) -> Error { - Error { repr: Repr::new_os(code) } - } - - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn raw_os_error(&self) -> Option { - match self.repr.data() { - ErrorData::Os(i) => Some(i), - ErrorData::Custom(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - } - } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err:?}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&*c.error), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr.data_mut() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&mut *c.error), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`] or [`other`], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// [`other`]: Error::other - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - pub fn into_inner(self) -> Option> { - match self.repr.into_data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error), - } - } - - /// Attempts to downcast the custom boxed error to `E`. - /// - /// If this [`Error`] contains a custom boxed error, - /// then it would attempt downcasting on the boxed error, - /// otherwise it will return [`Err`]. - /// - /// If the custom boxed error has the same type as `E`, it will return [`Ok`], - /// otherwise it will also return [`Err`]. - /// - /// This method is meant to be a convenience routine for calling - /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`]. - /// - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// use std::io; - /// use std::error::Error; - /// - /// #[derive(Debug)] - /// enum E { - /// Io(io::Error), - /// SomeOtherVariant, - /// } - /// - /// impl fmt::Display for E { - /// // ... - /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// # todo!() - /// # } - /// } - /// impl Error for E {} - /// - /// impl From for E { - /// fn from(err: io::Error) -> E { - /// err.downcast::() - /// .unwrap_or_else(E::Io) - /// } - /// } - /// - /// impl From for io::Error { - /// fn from(err: E) -> io::Error { - /// match err { - /// E::Io(io_error) => io_error, - /// e => io::Error::new(io::ErrorKind::Other, e), - /// } - /// } - /// } - /// - /// # fn main() { - /// let e = E::SomeOtherVariant; - /// // Convert it to an io::Error - /// let io_error = io::Error::from(e); - /// // Cast it back to the original variant - /// let e = E::from(io_error); - /// assert!(matches!(e, E::SomeOtherVariant)); - /// - /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); - /// // Convert it to E - /// let e = E::from(io_error); - /// // Cast it back to the original variant - /// let io_error = io::Error::from(e); - /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); - /// assert!(io_error.get_ref().is_none()); - /// assert!(io_error.raw_os_error().is_none()); - /// # } - /// ``` - #[stable(feature = "io_error_downcast", since = "1.79.0")] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() - { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } - } - } else { - Err(self) - } - } - - /// Returns the corresponding [`ErrorKind`] for this error. - /// - /// This may be a value set by Rust code constructing custom `io::Error`s, - /// or if this `io::Error` was sourced from the operating system, - /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. - /// - /// [`last_os_error`]: Error::last_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // As no error has (visibly) occurred, this may print anything! - /// // It likely prints a placeholder for unidentified (non-)errors. - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn kind(&self) -> ErrorKind { - match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[inline] - pub(crate) fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), - ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, - ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, - ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data() { - ErrorData::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) - .finish(), - ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), - ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - ErrorData::SimpleMessage(msg) => fmt - .debug_struct("Error") - .field("kind", &msg.kind) - .field("message", &msg.message) - .finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr.data() { - ErrorData::Os(code) => { - let detail = sys::io::error_string(code); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(ref c) => c.error.fmt(fmt), - ErrorData::Simple(kind) => write!(fmt, "{}", kind.as_str()), - ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); -} diff --git a/crates/std/src/io/error/repr_bitpacked.rs b/crates/std/src/io/error/repr_bitpacked.rs deleted file mode 100644 index 7353816..0000000 --- a/crates/std/src/io/error/repr_bitpacked.rs +++ /dev/null @@ -1,411 +0,0 @@ -//! This is a densely packed error representation which is used on targets with -//! 64-bit pointers. -//! -//! (Note that `bitpacked` vs `unpacked` here has no relationship to -//! `#[repr(packed)]`, it just refers to attempting to use any available bits in -//! a more clever manner than `rustc`'s default layout algorithm would). -//! -//! Conceptually, it stores the same data as the "unpacked" equivalent we use on -//! other targets. Specifically, you can imagine it as an optimized version of -//! the following enum (which is roughly equivalent to what's stored by -//! `repr_unpacked::Repr`, e.g. `super::ErrorData>`): -//! -//! ```ignore (exposition-only) -//! enum ErrorData { -//! Os(i32), -//! Simple(ErrorKind), -//! SimpleMessage(&'static SimpleMessage), -//! Custom(Box), -//! } -//! ``` -//! -//! However, it packs this data into a 64bit non-zero value. -//! -//! This optimization not only allows `io::Error` to occupy a single pointer, -//! but improves `io::Result` as well, especially for situations like -//! `io::Result<()>` (which is now 64 bits) or `io::Result` (which is now -//! 128 bits), which are quite common. -//! -//! # Layout -//! Tagged values are 64 bits, with the 2 least significant bits used for the -//! tag. This means there are 4 "variants": -//! -//! - **Tag 0b00**: The first variant is equivalent to -//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly. -//! -//! `SimpleMessage` has an alignment >= 4 (which is requested with -//! `#[repr(align)]` and checked statically at the bottom of this file), which -//! means every `&'static SimpleMessage` should have the both tag bits as 0, -//! meaning its tagged and untagged representation are equivalent. -//! -//! This means we can skip tagging it, which is necessary as this variant can -//! be constructed from a `const fn`, which probably cannot tag pointers (or -//! at least it would be difficult). -//! -//! - **Tag 0b01**: The other pointer variant holds the data for -//! `ErrorData::Custom` and the remaining 62 bits are used to store a -//! `Box`. `Custom` also has alignment >= 4, so the bottom two bits -//! are free to use for the tag. -//! -//! The only important thing to note is that `ptr::wrapping_add` and -//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise -//! operations. This should preserve the pointer's provenance, which would -//! otherwise be lost. -//! -//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32` -//! in the pointer's most significant 32 bits, and don't use the bits `2..32` -//! for anything. Using the top 32 bits is just to let us easily recover the -//! `i32` code with the correct sign. -//! -//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This -//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't -//! occupy nearly that many. Most of the bits are unused here, but it's not -//! like we need them for anything else yet. -//! -//! # Use of `NonNull<()>` -//! -//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a -//! purpose. -//! -//! Conceptually you might think of this more like: -//! -//! ```ignore (exposition-only) -//! union Repr { -//! // holds integer (Simple/Os) variants, and -//! // provides access to the tag bits. -//! bits: NonZero, -//! // Tag is 0, so this is stored untagged. -//! msg: &'static SimpleMessage, -//! // Tagged (offset) `Box` pointer. -//! tagged_custom: NonNull<()>, -//! } -//! ``` -//! -//! But there are a few problems with this: -//! -//! 1. Union access is equivalent to a transmute, so this representation would -//! require we transmute between integers and pointers in at least one -//! direction, which may be UB (and even if not, it is likely harder for a -//! compiler to reason about than explicit ptr->int operations). -//! -//! 2. Even if all fields of a union have a niche, the union itself doesn't, -//! although this may change in the future. This would make things like -//! `io::Result<()>` and `io::Result` larger, which defeats part of -//! the motivation of this bitpacking. -//! -//! Storing everything in a `NonZero` (or some other integer) would be a -//! bit more traditional for pointer tagging, but it would lose provenance -//! information, couldn't be constructed from a `const fn`, and would probably -//! run into other issues as well. -//! -//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd -//! to use a pointer type to store something that may hold an integer, some of -//! the time. - -use core::marker::PhantomData; -use core::num::NonZeroUsize; -use core::ptr::NonNull; - -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; - -// The 2 least-significant bits are used as tag. -const TAG_MASK: usize = 0b11; -const TAG_SIMPLE_MESSAGE: usize = 0b00; -const TAG_CUSTOM: usize = 0b01; -const TAG_OS: usize = 0b10; -const TAG_SIMPLE: usize = 0b11; - -/// The internal representation. -/// -/// See the module docs for more, this is just a way to hack in a check that we -/// indeed are not unwind-safe. -/// -/// ```compile_fail,E0277 -/// fn is_unwind_safe() {} -/// is_unwind_safe::(); -/// ``` -#[repr(transparent)] -#[rustc_insignificant_dtor] -pub(super) struct Repr(NonNull<()>, PhantomData>>); - -// All the types `Repr` stores internally are Send + Sync, and so is it. -unsafe impl Send for Repr {} -unsafe impl Sync for Repr {} - -impl Repr { - pub(super) fn new_custom(b: Box) -> Self { - let p = Box::into_raw(b).cast::(); - // Should only be possible if an allocator handed out a pointer with - // wrong alignment. - debug_assert_eq!(p.addr() & TAG_MASK, 0); - // Note: We know `TAG_CUSTOM <= size_of::()` (static_assert at - // end of file), and both the start and end of the expression must be - // valid without address space wraparound due to `Box`'s semantics. - // - // This means it would be correct to implement this using `ptr::add` - // (rather than `ptr::wrapping_add`), but it's unclear this would give - // any benefit, so we just use `wrapping_add` instead. - let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); - // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, - // because `p`'s alignment means it isn't allowed to have any of the - // `TAG_BITS` set (you can verify that addition and bitwise-or are the - // same when the operands have no bits in common using a truth table). - // - // Then, `TAG_CUSTOM | p` is not zero, as that would require - // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a - // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, - // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the - // `new_unchecked` is safe. - let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); - // quickly smoke-check we encoded the right thing (This generally will - // only run in std's tests, unless the user uses -Zbuild-std) - debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); - res - } - - #[inline] - pub(super) fn new_os(code: RawOsError) -> Self { - let utagged = ((code as usize) << 32) | TAG_OS; - // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); - // quickly smoke-check we encoded the right thing (This generally will - // only run in std's tests, unless the user uses -Zbuild-std) - debug_assert!( - matches!(res.data(), ErrorData::Os(c) if c == code), - "repr(os) encoding failed for {code}" - ); - res - } - - #[inline] - pub(super) fn new_simple(kind: ErrorKind) -> Self { - let utagged = ((kind as usize) << 32) | TAG_SIMPLE; - // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); - // quickly smoke-check we encoded the right thing (This generally will - // only run in std's tests, unless the user uses -Zbuild-std) - debug_assert!( - matches!(res.data(), ErrorData::Simple(k) if k == kind), - "repr(simple) encoding failed {:?}", - kind, - ); - res - } - - #[inline] - pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - // Safety: References are never null. - Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) - } - - #[inline] - pub(super) fn data(&self) -> ErrorData<&Custom> { - // Safety: We're a Repr, decode_repr is fine. - unsafe { decode_repr(self.0, |c| &*c) } - } - - #[inline] - pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - // Safety: We're a Repr, decode_repr is fine. - unsafe { decode_repr(self.0, |c| &mut *c) } - } - - #[inline] - pub(super) fn into_data(self) -> ErrorData> { - let this = core::mem::ManuallyDrop::new(self); - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is - // safe because we prevent double-drop using `ManuallyDrop`. - unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } - } -} - -impl Drop for Repr { - #[inline] - fn drop(&mut self) { - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is - // safe because we're being dropped. - unsafe { - let _ = decode_repr(self.0, |p| Box::::from_raw(p)); - } - } -} - -// Shared helper to decode a `Repr`'s internal pointer into an ErrorData. -// -// Safety: `ptr`'s bits should be encoded as described in the document at the -// top (it should `some_repr.0`) -#[inline] -unsafe fn decode_repr(ptr: NonNull<()>, make_custom: F) -> ErrorData -where - F: FnOnce(*mut Custom) -> C, -{ - let bits = ptr.as_ptr().addr(); - match bits & TAG_MASK { - TAG_OS => { - let code = ((bits as i64) >> 32) as RawOsError; - ErrorData::Os(code) - } - TAG_SIMPLE => { - let kind_bits = (bits >> 32) as u32; - let kind = kind_from_prim(kind_bits).unwrap_or_else(|| { - debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); - // This means the `ptr` passed in was not valid, which violates - // the unsafe contract of `decode_repr`. - // - // Using this rather than unwrap meaningfully improves the code - // for callers which only care about one variant (usually - // `Custom`) - unsafe { core::hint::unreachable_unchecked() }; - }); - ErrorData::Simple(kind) - } - TAG_SIMPLE_MESSAGE => { - // SAFETY: per tag - unsafe { ErrorData::SimpleMessage(&*ptr.cast::().as_ptr()) } - } - TAG_CUSTOM => { - // It would be correct for us to use `ptr::byte_sub` here (see the - // comment above the `wrapping_add` call in `new_custom` for why), - // but it isn't clear that it makes a difference, so we don't. - let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::(); - ErrorData::Custom(make_custom(custom)) - } - _ => { - // Can't happen, and compiler can tell - unreachable!(); - } - } -} - -// This compiles to the same code as the check+transmute, but doesn't require -// unsafe, or to hard-code max ErrorKind or its size in a way the compiler -// couldn't verify. -#[inline] -fn kind_from_prim(ek: u32) -> Option { - macro_rules! from_prim { - ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ - // Force a compile error if the list gets out of date. - const _: fn(e: $Enum) = |e: $Enum| match e { - $($Enum::$Variant => ()),* - }; - match $prim { - $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* - _ => None, - } - }} - } - from_prim!(ek => ErrorKind { - NotFound, - PermissionDenied, - ConnectionRefused, - ConnectionReset, - HostUnreachable, - NetworkUnreachable, - ConnectionAborted, - NotConnected, - AddrInUse, - AddrNotAvailable, - NetworkDown, - BrokenPipe, - AlreadyExists, - WouldBlock, - NotADirectory, - IsADirectory, - DirectoryNotEmpty, - ReadOnlyFilesystem, - FilesystemLoop, - StaleNetworkFileHandle, - InvalidInput, - InvalidData, - TimedOut, - WriteZero, - StorageFull, - NotSeekable, - QuotaExceeded, - FileTooLarge, - ResourceBusy, - ExecutableFileBusy, - Deadlock, - CrossesDevices, - TooManyLinks, - InvalidFilename, - ArgumentListTooLong, - Interrupted, - Other, - UnexpectedEof, - Unsupported, - OutOfMemory, - InProgress, - Uncategorized, - }) -} - -// Some static checking to alert us if a change breaks any of the assumptions -// that our encoding relies on for correctness and soundness. (Some of these are -// a bit overly thorough/cautious, admittedly) -// -// If any of these are hit on a platform that std supports, we should likely -// just use `repr_unpacked.rs` there instead (unless the fix is easy). -macro_rules! static_assert { - ($condition:expr) => { - const _: () = assert!($condition); - }; - (@usize_eq: $lhs:expr, $rhs:expr) => { - const _: [(); $lhs] = [(); $rhs]; - }; -} - -// The bitpacking we use requires pointers be exactly 64 bits. -static_assert!(@usize_eq: size_of::>(), 8); - -// We also require pointers and usize be the same size. -static_assert!(@usize_eq: size_of::>(), size_of::()); - -// `Custom` and `SimpleMessage` need to be thin pointers. -static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); - -static_assert!((TAG_MASK + 1).is_power_of_two()); -// And they must have sufficient alignment. -static_assert!(align_of::() >= TAG_MASK + 1); -static_assert!(align_of::() >= TAG_MASK + 1); - -static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE); -static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM); -static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS); -static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE); - -// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we -// offset a pointer by this value, and expect it to both be within the same -// object, and to not wrap around the address space. See the comment in that -// function for further details. -// -// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this -// check isn't needed for that one, although the assertion that we don't -// actually wrap around in that wrapping_add does simplify the safety reasoning -// elsewhere considerably. -static_assert!(size_of::() >= TAG_CUSTOM); - -// These two store a payload which is allowed to be zero, so they must be -// non-zero to preserve the `NonNull`'s range invariant. -static_assert!(TAG_OS != 0); -static_assert!(TAG_SIMPLE != 0); -// We can't tag `SimpleMessage`s, the tag must be 0. -static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0); - -// Check that the point of all of this still holds. -// -// We'd check against `io::Error`, but *technically* it's allowed to vary, -// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but -// the `#[repr()]` would show up in rustdoc, which might be seen as a stable -// commitment. -static_assert!(@usize_eq: size_of::(), 8); -static_assert!(@usize_eq: size_of::>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); -static_assert!(@usize_eq: size_of::>(), 16); diff --git a/crates/std/src/io/error/repr_unpacked.rs b/crates/std/src/io/error/repr_unpacked.rs deleted file mode 100644 index b3e7b5f..0000000 --- a/crates/std/src/io/error/repr_unpacked.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! This is a fairly simple unpacked error representation that's used on -//! non-64bit targets, where the packed 64 bit representation wouldn't work, and -//! would have no benefit. - -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; - -type Inner = ErrorData>; - -pub(super) struct Repr(Inner); - -impl Repr { - #[inline] - pub(super) fn new_custom(b: Box) -> Self { - Self(Inner::Custom(b)) - } - #[inline] - pub(super) fn new_os(code: RawOsError) -> Self { - Self(Inner::Os(code)) - } - #[inline] - pub(super) fn new_simple(kind: ErrorKind) -> Self { - Self(Inner::Simple(kind)) - } - #[inline] - pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - Self(Inner::SimpleMessage(m)) - } - #[inline] - pub(super) fn into_data(self) -> ErrorData> { - self.0 - } - #[inline] - pub(super) fn data(&self) -> ErrorData<&Custom> { - match &self.0 { - Inner::Os(c) => ErrorData::Os(*c), - Inner::Simple(k) => ErrorData::Simple(*k), - Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&*m), - } - } - #[inline] - pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - match &mut self.0 { - Inner::Os(c) => ErrorData::Os(*c), - Inner::Simple(k) => ErrorData::Simple(*k), - Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&mut *m), - } - } -} diff --git a/crates/std/src/io/error/tests.rs b/crates/std/src/io/error/tests.rs deleted file mode 100644 index a8eef06..0000000 --- a/crates/std/src/io/error/tests.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; -use crate::sys::io::{decode_error_kind, error_string}; -use crate::{assert_matches, error, fmt}; - -#[test] -fn test_size() { - assert!(size_of::() <= size_of::<[usize; 2]>()); -} - -#[test] -fn test_debug_error() { - let code = 6; - let msg = error_string(code); - let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; - let expected = format!( - "Custom {{ \ - kind: InvalidInput, \ - error: Os {{ \ - code: {:?}, \ - kind: {:?}, \ - message: {:?} \ - }} \ - }}", - code, kind, msg - ); - assert_eq!(format!("{err:?}"), expected); -} - -#[test] -fn test_downcasting() { - #[derive(Debug)] - struct TestError; - - impl fmt::Display for TestError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("asdf") - } - } - - impl error::Error for TestError {} - - // we have to call all of these UFCS style right now since method - // resolution won't implicitly drop the Send+Sync bounds - let mut err = Error::new(ErrorKind::Other, TestError); - assert!(err.get_ref().unwrap().is::()); - assert_eq!("asdf", err.get_ref().unwrap().to_string()); - assert!(err.get_mut().unwrap().is::()); - let extracted = err.into_inner().unwrap(); - extracted.downcast::().unwrap(); -} - -#[test] -fn test_const() { - const E: Error = const_error!(ErrorKind::NotFound, "hello"); - - assert_eq!(E.kind(), ErrorKind::NotFound); - assert_eq!(E.to_string(), "hello"); - assert!(format!("{E:?}").contains("\"hello\"")); - assert!(format!("{E:?}").contains("NotFound")); -} - -#[test] -fn test_os_packing() { - for code in -20..20 { - let e = Error::from_raw_os_error(code); - assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); - } -} - -#[test] -fn test_errorkind_packing() { - assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); - assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); - assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); -} - -#[test] -fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; - macro_rules! check_simple_msg { - ($err:expr, $kind:ident, $msg:literal) => {{ - let e = &$err; - // Check that the public api is right. - assert_eq!(e.kind(), $kind); - assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); - }}; - } - - let not_static = const_error!(Uncategorized, "not a constant!"); - check_simple_msg!(not_static, Uncategorized, "not a constant!"); - - const CONST: Error = const_error!(NotFound, "definitely a constant!"); - check_simple_msg!(CONST, NotFound, "definitely a constant!"); - - static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!"); - check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); -} - -#[derive(Debug, PartialEq)] -struct Bojji(bool); -impl error::Error for Bojji {} -impl fmt::Display for Bojji { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ah! {:?}", self) - } -} - -#[test] -fn test_custom_error_packing() { - use super::Custom; - let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); - assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), - ); -} - -#[derive(Debug)] -struct E; - -impl fmt::Display for E { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -impl error::Error for E {} - -#[test] -fn test_std_io_error_downcast() { - // Case 1: custom error, downcast succeeds - let io_error = Error::new(ErrorKind::Other, Bojji(true)); - let e: Bojji = io_error.downcast().unwrap(); - assert!(e.0); - - // Case 2: custom error, downcast fails - let io_error = Error::new(ErrorKind::Other, Bojji(true)); - let io_error = io_error.downcast::().unwrap_err(); - - // ensures that the custom error is intact - assert_eq!(ErrorKind::Other, io_error.kind()); - let e: Bojji = io_error.downcast().unwrap(); - assert!(e.0); - - // Case 3: os error - let errno = 20; - let io_error = Error::from_raw_os_error(errno); - let io_error = io_error.downcast::().unwrap_err(); - - assert_eq!(errno, io_error.raw_os_error().unwrap()); - - // Case 4: simple - let kind = ErrorKind::OutOfMemory; - let io_error: Error = kind.into(); - let io_error = io_error.downcast::().unwrap_err(); - - assert_eq!(kind, io_error.kind()); - - // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); - let io_error = io_error.downcast::().unwrap_err(); - - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); -} diff --git a/crates/std/src/io/impls.rs b/crates/std/src/io/impls.rs deleted file mode 100644 index 980e915..0000000 --- a/crates/std/src/io/impls.rs +++ /dev/null @@ -1,717 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::alloc::Allocator; -use alloc_crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; -use crate::{cmp, fmt, mem, str}; - -// ============================================================================= -// Forwarding implementations - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &mut R { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (**self).read_exact(buf) - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf_exact(cursor) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut W { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &mut S { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &mut B { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Box { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf(cursor) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - (**self).is_read_vectored() - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (**self).read_exact(buf) - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - (**self).read_buf_exact(cursor) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Box { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for Box { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Box { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn has_data_left(&mut self) -> io::Result { - (**self).has_data_left() - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn skip_until(&mut self, byte: u8) -> io::Result { - (**self).skip_until(byte) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -// ============================================================================= -// In-memory buffer implementations - -/// Read is implemented for `&[u8]` by copying from the slice. -/// -/// Note that reading updates the slice to point to the yet unread part. -/// The slice will be empty when EOF is reached. -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &[u8] { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.len()); - let (a, b) = self.split_at(amt); - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if amt == 1 { - buf[0] = a[0]; - } else { - buf[..amt].copy_from_slice(a); - } - - *self = b; - Ok(amt) - } - - #[inline] - fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let amt = cmp::min(cursor.capacity(), self.len()); - let (a, b) = self.split_at(amt); - - cursor.append(a); - - *self = b; - Ok(()) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nread = 0; - for buf in bufs { - nread += self.read(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nread) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if buf.len() > self.len() { - // `read_exact` makes no promise about the content of `buf` if it - // fails so don't bother about that. - *self = &self[self.len()..]; - return Err(io::Error::READ_EXACT_EOF); - } - let (a, b) = self.split_at(buf.len()); - - // First check if the amount of bytes we want to read is small: - // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. - if buf.len() == 1 { - buf[0] = a[0]; - } else { - buf.copy_from_slice(a); - } - - *self = b; - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - if cursor.capacity() > self.len() { - // Append everything we can to the cursor. - cursor.append(*self); - *self = &self[self.len()..]; - return Err(io::Error::READ_EXACT_EOF); - } - let (a, b) = self.split_at(cursor.capacity()); - - cursor.append(a); - - *self = b; - Ok(()) - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let len = self.len(); - buf.try_reserve(len)?; - buf.extend_from_slice(*self); - *self = &self[len..]; - Ok(len) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?; - let len = self.len(); - buf.try_reserve(len)?; - buf.push_str(content); - *self = &self[len..]; - Ok(len) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for &[u8] { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(*self) - } - - #[inline] - fn consume(&mut self, amt: usize) { - *self = &self[amt..]; - } -} - -/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting -/// its data. -/// -/// Note that writing updates the slice to point to the yet unwritten part. -/// The slice will be empty when it has been completely overwritten. -/// -/// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut [u8] { - #[inline] - fn write(&mut self, data: &[u8]) -> io::Result { - let amt = cmp::min(data.len(), self.len()); - let (a, b) = mem::take(self).split_at_mut(amt); - a.copy_from_slice(&data[..amt]); - *self = b; - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.write(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Write is implemented for `Vec` by appending to the vector. -/// The vector will grow as needed. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend_from_slice(buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Read for VecDeque { - /// Fill `buf` with the contents of the "front" slice as returned by - /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are - /// discontiguous, multiple calls to `read` will be needed to read the entire content. - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let (ref mut front, _) = self.as_slices(); - let n = Read::read(front, buf)?; - self.drain(..n); - Ok(n) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let (front, back) = self.as_slices(); - - // Use only the front buffer if it is big enough to fill `buf`, else use - // the back buffer too. - match buf.split_at_mut_checked(front.len()) { - None => buf.copy_from_slice(&front[..buf.len()]), - Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { - Some((back, _)) => { - buf_front.copy_from_slice(front); - buf_back.copy_from_slice(back); - } - None => { - self.clear(); - return Err(io::Error::READ_EXACT_EOF); - } - }, - } - - self.drain(..buf.len()); - Ok(()) - } - - #[inline] - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let (ref mut front, _) = self.as_slices(); - let n = cmp::min(cursor.capacity(), front.len()); - Read::read_buf(front, cursor)?; - self.drain(..n); - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { - let len = cursor.capacity(); - let (front, back) = self.as_slices(); - - match front.split_at_checked(cursor.capacity()) { - Some((front, _)) => cursor.append(front), - None => { - cursor.append(front); - match back.split_at_checked(cursor.capacity()) { - Some((back, _)) => cursor.append(back), - None => { - cursor.append(back); - self.clear(); - return Err(io::Error::READ_EXACT_EOF); - } - } - } - } - - self.drain(..len); - Ok(()) - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - // The total len is known upfront so we can reserve it in a single call. - let len = self.len(); - buf.try_reserve(len)?; - - let (front, back) = self.as_slices(); - buf.extend_from_slice(front); - buf.extend_from_slice(back); - self.clear(); - Ok(len) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - // SAFETY: We only append to the buffer - unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) } - } -} - -/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. -#[stable(feature = "vecdeque_buf_read", since = "1.75.0")] -impl BufRead for VecDeque { - /// Returns the contents of the "front" slice as returned by - /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are - /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let (front, _) = self.as_slices(); - Ok(front) - } - - #[inline] - fn consume(&mut self, amt: usize) { - self.drain(..amt); - } -} - -/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Write for VecDeque { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend(&**buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[unstable(feature = "read_buf", issue = "78485")] -impl<'a> io::Write for core::io::BorrowedCursor<'a> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.capacity()); - self.append(&buf[..amt]); - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = self.write(buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/crates/std/src/io/impls/tests.rs b/crates/std/src/io/impls/tests.rs deleted file mode 100644 index d1cd84a..0000000 --- a/crates/std/src/io/impls/tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::io::prelude::*; - -#[bench] -fn bench_read_slice(b: &mut test::Bencher) { - let buf = [5; 1024]; - let mut dst = [0; 128]; - - b.iter(|| { - let mut rd = &buf[..]; - for _ in 0..8 { - let _ = rd.read(&mut dst); - test::black_box(&dst); - } - }) -} - -#[bench] -fn bench_write_slice(b: &mut test::Bencher) { - let mut buf = [0; 1024]; - let src = [5; 128]; - - b.iter(|| { - let mut wr = &mut buf[..]; - for _ in 0..8 { - let _ = wr.write_all(&src); - test::black_box(&wr); - } - }) -} - -#[bench] -fn bench_read_vec(b: &mut test::Bencher) { - let buf = vec![5; 1024]; - let mut dst = [0; 128]; - - b.iter(|| { - let mut rd = &buf[..]; - for _ in 0..8 { - let _ = rd.read(&mut dst); - test::black_box(&dst); - } - }) -} - -#[bench] -fn bench_write_vec(b: &mut test::Bencher) { - let mut buf = Vec::with_capacity(1024); - let src = [5; 128]; - - b.iter(|| { - let mut wr = &mut buf[..]; - for _ in 0..8 { - let _ = wr.write_all(&src); - test::black_box(&wr); - } - }) -} diff --git a/crates/std/src/io/mod.rs b/crates/std/src/io/mod.rs deleted file mode 100644 index 623c34c..0000000 --- a/crates/std/src/io/mod.rs +++ /dev/null @@ -1,3408 +0,0 @@ -//! Traits, helpers, and type definitions for core I/O functionality. -//! -//! The `std::io` module contains a number of common things you'll need -//! when doing input and output. The most core part of this module is -//! the [`Read`] and [`Write`] traits, which provide the -//! most general interface for reading and writing input and output. -//! -//! ## Read and Write -//! -//! Because they are traits, [`Read`] and [`Write`] are implemented by a number -//! of other types, and you can implement them for your types too. As such, -//! you'll see a few different types of I/O throughout the documentation in -//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For -//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on -//! [`File`]s: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let mut f = File::open("foo.txt")?; -//! let mut buffer = [0; 10]; -//! -//! // read up to 10 bytes -//! let n = f.read(&mut buffer)?; -//! -//! println!("The bytes: {:?}", &buffer[..n]); -//! Ok(()) -//! } -//! ``` -//! -//! [`Read`] and [`Write`] are so important, implementors of the two traits have a -//! nickname: readers and writers. So you'll sometimes see 'a reader' instead -//! of 'a type that implements the [`Read`] trait'. Much easier! -//! -//! ## Seek and BufRead -//! -//! Beyond that, there are two important traits that are provided: [`Seek`] -//! and [`BufRead`]. Both of these build on top of a reader to control -//! how the reading happens. [`Seek`] lets you control where the next byte is -//! coming from: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::SeekFrom; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let mut f = File::open("foo.txt")?; -//! let mut buffer = [0; 10]; -//! -//! // skip to the last 10 bytes of the file -//! f.seek(SeekFrom::End(-10))?; -//! -//! // read up to 10 bytes -//! let n = f.read(&mut buffer)?; -//! -//! println!("The bytes: {:?}", &buffer[..n]); -//! Ok(()) -//! } -//! ``` -//! -//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but -//! to show it off, we'll need to talk about buffers in general. Keep reading! -//! -//! ## BufReader and BufWriter -//! -//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be -//! making near-constant calls to the operating system. To help with this, -//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap -//! readers and writers. The wrapper uses a buffer, reducing the number of -//! calls and providing nicer methods for accessing exactly what you want. -//! -//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra -//! methods to any reader: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufReader; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::open("foo.txt")?; -//! let mut reader = BufReader::new(f); -//! let mut buffer = String::new(); -//! -//! // read a line into buffer -//! reader.read_line(&mut buffer)?; -//! -//! println!("{buffer}"); -//! Ok(()) -//! } -//! ``` -//! -//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call -//! to [`write`][`Write::write`]: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufWriter; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::create("foo.txt")?; -//! { -//! let mut writer = BufWriter::new(f); -//! -//! // write a byte to the buffer -//! writer.write(&[42])?; -//! -//! } // the buffer is flushed once writer goes out of scope -//! -//! Ok(()) -//! } -//! ``` -//! -//! ## Standard input and output -//! -//! A very common source of input is standard input: -//! -//! ```no_run -//! use std::io; -//! -//! fn main() -> io::Result<()> { -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input)?; -//! -//! println!("You typed: {}", input.trim()); -//! Ok(()) -//! } -//! ``` -//! -//! Note that you cannot use the [`?` operator] in functions that do not return -//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] -//! or `match` on the return value to catch any possible errors: -//! -//! ```no_run -//! use std::io; -//! -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input).unwrap(); -//! ``` -//! -//! And a very common source of output is standard output: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! -//! fn main() -> io::Result<()> { -//! io::stdout().write(&[42])?; -//! Ok(()) -//! } -//! ``` -//! -//! Of course, using [`io::stdout`] directly is less common than something like -//! [`println!`]. -//! -//! ## Iterator types -//! -//! A large number of the structures provided by `std::io` are for various -//! ways of iterating over I/O. For example, [`Lines`] is used to split over -//! lines: -//! -//! ```no_run -//! use std::io; -//! use std::io::prelude::*; -//! use std::io::BufReader; -//! use std::fs::File; -//! -//! fn main() -> io::Result<()> { -//! let f = File::open("foo.txt")?; -//! let reader = BufReader::new(f); -//! -//! for line in reader.lines() { -//! println!("{}", line?); -//! } -//! Ok(()) -//! } -//! ``` -//! -//! ## Functions -//! -//! There are a number of [functions][functions-list] that offer access to various -//! features. For example, we can use three of these functions to copy everything -//! from standard input to standard output: -//! -//! ```no_run -//! use std::io; -//! -//! fn main() -> io::Result<()> { -//! io::copy(&mut io::stdin(), &mut io::stdout())?; -//! Ok(()) -//! } -//! ``` -//! -//! [functions-list]: #functions-1 -//! -//! ## io::Result -//! -//! Last, but certainly not least, is [`io::Result`]. This type is used -//! as the return type of many `std::io` functions that can cause an error, and -//! can be returned from your own functions as well. Many of the examples in this -//! module use the [`?` operator]: -//! -//! ``` -//! use std::io; -//! -//! fn read_input() -> io::Result<()> { -//! let mut input = String::new(); -//! -//! io::stdin().read_line(&mut input)?; -//! -//! println!("You typed: {}", input.trim()); -//! -//! Ok(()) -//! } -//! ``` -//! -//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very -//! common type for functions which don't have a 'real' return value, but do want to -//! return errors if they happen. In this case, the only purpose of this function is -//! to read the line and print it, so we use `()`. -//! -//! ## Platform-specific behavior -//! -//! Many I/O functions throughout the standard library are documented to indicate -//! what various library or syscalls they are delegated to. This is done to help -//! applications both understand what's happening under the hood as well as investigate -//! any possibly unclear semantics. Note, however, that this is informative, not a binding -//! contract. The implementation of many of these functions are subject to change over -//! time and may call fewer or more syscalls/library functions. -//! -//! ## I/O Safety -//! -//! Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This -//! means that file descriptors can be *exclusively owned*. (Here, "file descriptor" is meant to -//! subsume similar concepts that exist across a wide range of operating systems even if they might -//! use a different name, such as "handle".) An exclusively owned file descriptor is one that no -//! other code is allowed to access in any way, but the owner is allowed to access and even close -//! it any time. A type that owns its file descriptor should usually close it in its `drop` -//! function. Types like [`File`] own their file descriptor. Similarly, file descriptors -//! can be *borrowed*, granting the temporary right to perform operations on this file descriptor. -//! This indicates that the file descriptor will not be closed for the lifetime of the borrow, but -//! it does *not* imply any right to close this file descriptor, since it will likely be owned by -//! someone else. -//! -//! The platform-specific parts of the Rust standard library expose types that reflect these -//! concepts, see [`os::unix`] and [`os::windows`]. -//! -//! To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or -//! borrow, and no code closes file descriptors it does not own. In other words, a safe function -//! that takes a regular integer, treats it as a file descriptor, and acts on it, is *unsound*. -//! -//! Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to -//! misbehavior and even Undefined Behavior in code that relies on ownership of its file -//! descriptors: a closed file descriptor could be re-allocated, so the original owner of that file -//! descriptor is now working on the wrong file. Some code might even rely on fully encapsulating -//! its file descriptors with no operations being performed by any other part of the program. -//! -//! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the -//! underlying kernel object that the file descriptor references (also called "open file description" on -//! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned -//! file descriptor, you cannot know whether there are any other file descriptors that reference the -//! same kernel object. However, when you create a new kernel object, you know that you are holding -//! the only reference to it. Just be careful not to lend it to anyone, since they can obtain a -//! clone and then you can no longer know what the reference count is! In that sense, [`OwnedFd`] is -//! like `Arc` and [`BorrowedFd<'a>`] is like `&'a Arc` (and similar for the Windows types). In -//! particular, given a `BorrowedFd<'a>`, you are not allowed to close the file descriptor -- just -//! like how, given a `&'a Arc`, you are not allowed to decrement the reference count and -//! potentially free the underlying object. There is no equivalent to `Box` for file descriptors in -//! the standard library (that would be a type that guarantees that the reference count is `1`), -//! however, it would be possible for a crate to define a type with those semantics. -//! -//! [`File`]: crate::fs::File -//! [`TcpStream`]: crate::net::TcpStream -//! [`io::stdout`]: stdout -//! [`io::Result`]: self::Result -//! [`?` operator]: ../../book/appendix-02-operators.html -//! [`Result`]: crate::result::Result -//! [`.unwrap()`]: crate::result::Result::unwrap -//! [`os::unix`]: ../os/unix/io/index.html -//! [`os::windows`]: ../os/windows/io/index.html -//! [`OwnedFd`]: ../os/fd/struct.OwnedFd.html -//! [`BorrowedFd<'a>`]: ../os/fd/struct.BorrowedFd.html -//! [`Arc`]: crate::sync::Arc - -#![stable(feature = "rust1", since = "1.0.0")] - -#[cfg(test)] -mod tests; - -#[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; -use core::slice::memchr; - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use self::buffered::WriterPanicked; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; -#[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -pub use self::pipe::{PipeReader, PipeWriter, pipe}; -#[stable(feature = "is_terminal", since = "1.70.0")] -pub use self::stdio::IsTerminal; -pub(crate) use self::stdio::attempt_print_to_stderr; -#[unstable(feature = "print_internals", issue = "none")] -#[doc(hidden)] -pub use self::stdio::{_eprint, _print}; -#[unstable(feature = "internal_output_capture", issue = "none")] -#[doc(no_inline, hidden)] -pub use self::stdio::{set_output_capture, try_set_output_capture}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::{ - buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, - copy::copy, - cursor::Cursor, - error::{Error, ErrorKind, Result}, - stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, - util::{Empty, Repeat, Sink, empty, repeat, sink}, -}; -use crate::mem::{MaybeUninit, take}; -use crate::ops::{Deref, DerefMut}; -use crate::{cmp, fmt, slice, str, sys}; - -mod buffered; -pub(crate) mod copy; -mod cursor; -mod error; -mod impls; -mod pipe; -pub mod prelude; -mod stdio; -mod util; - -const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - -pub(crate) use stdio::cleanup; - -struct Guard<'a> { - buf: &'a mut Vec, - len: usize, -} - -impl Drop for Guard<'_> { - fn drop(&mut self) { - unsafe { - self.buf.set_len(self.len); - } - } -} - -// Several `read_to_string` and `read_line` methods in the standard library will -// append data into a `String` buffer, but we need to be pretty careful when -// doing this. The implementation will just call `.as_mut_vec()` and then -// delegate to a byte-oriented reading method, but we must ensure that when -// returning we never leave `buf` in a state such that it contains invalid UTF-8 -// in its bounds. -// -// To this end, we use an RAII guard (to protect against panics) which updates -// the length of the string when it is dropped. This guard initially truncates -// the string to the prior length and only after we've validated that the -// new contents are valid UTF-8 do we allow it to set a longer length. -// -// The unsafety in this function is twofold: -// -// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 -// checks. -// 2. We're passing a raw buffer to the function `f`, and it is expected that -// the function only *appends* bytes to the buffer. We'll get undefined -// behavior if existing bytes are overwritten to have non-UTF-8 data. -pub(crate) unsafe fn append_to_string(buf: &mut String, f: F) -> Result -where - F: FnOnce(&mut Vec) -> Result, -{ - let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; - let ret = f(g.buf); - - // SAFETY: the caller promises to only append data to `buf` - let appended = unsafe { g.buf.get_unchecked(g.len..) }; - if str::from_utf8(appended).is_err() { - ret.and_then(|_| Err(Error::INVALID_UTF8)) - } else { - g.len = g.buf.len(); - ret - } -} - -// Here we must serve many masters with conflicting goals: -// -// - avoid allocating unless necessary -// - avoid overallocating if we know the exact size (#89165) -// - avoid passing large buffers to readers that always initialize the free capacity if they perform short reads (#23815, #23820) -// - pass large buffers to readers that do not initialize the spare capacity. this can amortize per-call overheads -// - and finally pass not-too-small and not-too-large buffers to Windows read APIs because they manage to suffer from both problems -// at the same time, i.e. small reads suffer from syscall overhead, all reads incur costs proportional to buffer size (#110650) -// -pub(crate) fn default_read_to_end( - r: &mut R, - buf: &mut Vec, - size_hint: Option, -) -> Result { - let start_len = buf.len(); - let start_cap = buf.capacity(); - // Optionally limit the maximum bytes read on each iteration. - // This adds an arbitrary fiddle factor to allow for more data than we expect. - let mut max_read_size = size_hint - .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) - .unwrap_or(DEFAULT_BUF_SIZE); - - let mut initialized = 0; // Extra initialized bytes from previous loop iteration - - const PROBE_SIZE: usize = 32; - - fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { - let mut probe = [0u8; PROBE_SIZE]; - - loop { - match r.read(&mut probe) { - Ok(n) => { - // there is no way to recover from allocation failure here - // because the data has already been read. - buf.extend_from_slice(&probe[..n]); - return Ok(n); - } - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - } - } - - // avoid inflating empty/small vecs before we have determined that there's anything to read - if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE { - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(0); - } - } - - let mut consecutive_short_reads = 0; - - loop { - if buf.len() == buf.capacity() && buf.capacity() == start_cap { - // The buffer might be an exact fit. Let's read into a probe buffer - // and see if it returns `Ok(0)`. If so, we've avoided an - // unnecessary doubling of the capacity. But if not, append the - // probe buffer to the primary buffer and let its capacity grow. - let read = small_probe_read(r, buf)?; - - if read == 0 { - return Ok(buf.len() - start_len); - } - } - - if buf.len() == buf.capacity() { - // buf is full, need more space - buf.try_reserve(PROBE_SIZE)?; - } - - let mut spare = buf.spare_capacity_mut(); - let buf_len = cmp::min(spare.len(), max_read_size); - spare = &mut spare[..buf_len]; - let mut read_buf: BorrowedBuf<'_> = spare.into(); - - // SAFETY: These bytes were initialized but not filled in the previous loop - unsafe { - read_buf.set_init(initialized); - } - - let mut cursor = read_buf.unfilled(); - let result = loop { - match r.read_buf(cursor.reborrow()) { - Err(e) if e.is_interrupted() => continue, - // Do not stop now in case of error: we might have received both data - // and an error - res => break res, - } - }; - - let unfilled_but_initialized = cursor.init_mut().len(); - let bytes_read = cursor.written(); - let was_fully_initialized = read_buf.init_len() == buf_len; - - // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. - unsafe { - let new_len = bytes_read + buf.len(); - buf.set_len(new_len); - } - - // Now that all data is pushed to the vector, we can fail without data loss - result?; - - if bytes_read == 0 { - return Ok(buf.len() - start_len); - } - - if bytes_read < buf_len { - consecutive_short_reads += 1; - } else { - consecutive_short_reads = 0; - } - - // store how much was initialized but not filled - initialized = unfilled_but_initialized; - - // Use heuristics to determine the max read size if no initial size hint was provided - if size_hint.is_none() { - // The reader is returning short reads but it doesn't call ensure_init(). - // In that case we no longer need to restrict read sizes to avoid - // initialization costs. - // When reading from disk we usually don't get any short reads except at EOF. - // So we wait for at least 2 short reads before uncapping the read buffer; - // this helps with the Windows issue. - if !was_fully_initialized && consecutive_short_reads > 1 { - max_read_size = usize::MAX; - } - - // we have passed a larger buffer than previously and the - // reader still hasn't returned a short read - if buf_len >= max_read_size && bytes_read == buf_len { - max_read_size = max_read_size.saturating_mul(2); - } - } - } -} - -pub(crate) fn default_read_to_string( - r: &mut R, - buf: &mut String, - size_hint: Option, -) -> Result { - // Note that we do *not* call `r.read_to_end()` here. We are passing - // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` - // method to fill it up. An arbitrary implementation could overwrite the - // entire contents of the vector, not just append to it (which is what - // we are expecting). - // - // To prevent extraneously checking the UTF-8-ness of the entire buffer - // we pass it to our hardcoded `default_read_to_end` implementation which - // we know is guaranteed to only read data into the end of the buffer. - unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } -} - -pub(crate) fn default_read_vectored(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result -where - F: FnOnce(&mut [u8]) -> Result, -{ - let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b); - read(buf) -} - -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - -pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { - while !buf.is_empty() { - match this.read(buf) { - Ok(0) => break, - Ok(n) => { - buf = &mut buf[n..]; - } - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) } -} - -pub(crate) fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> -where - F: FnOnce(&mut [u8]) -> Result, -{ - let n = read(cursor.ensure_init().init_mut())?; - cursor.advance(n); - Ok(()) -} - -pub(crate) fn default_read_buf_exact( - this: &mut R, - mut cursor: BorrowedCursor<'_>, -) -> Result<()> { - while cursor.capacity() > 0 { - let prev_written = cursor.written(); - match this.read_buf(cursor.reborrow()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - - if cursor.written() == prev_written { - return Err(Error::READ_EXACT_EOF); - } - } - - Ok(()) -} - -pub(crate) fn default_write_fmt( - this: &mut W, - args: fmt::Arguments<'_>, -) -> Result<()> { - // Create a shim which translates a `Write` to a `fmt::Write` and saves off - // I/O errors, instead of discarding them. - struct Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adapter { inner: this, error: Ok(()) }; - match fmt::write(&mut output, args) { - Ok(()) => Ok(()), - Err(..) => { - // Check whether the error came from the underlying `Write`. - if output.error.is_err() { - output.error - } else { - // This shouldn't happen: the underlying stream did not error, - // but somehow the formatter still errored? - panic!( - "a formatting trait implementation returned an error when the underlying stream did not" - ); - } - } - } -} - -/// The `Read` trait allows for reading bytes from a source. -/// -/// Implementors of the `Read` trait are called 'readers'. -/// -/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] -/// will attempt to pull bytes from this source into a provided buffer. A -/// number of other methods are implemented in terms of [`read()`], giving -/// implementors a number of ways to read bytes while only needing to implement -/// a single method. -/// -/// Readers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Read` -/// trait. -/// -/// Please note that each call to [`read()`] may involve a system call, and -/// therefore, using something that implements [`BufRead`], such as -/// [`BufReader`], will be more efficient. -/// -/// Repeated calls to the reader use the same cursor, so for example -/// calling `read_to_end` twice on a [`File`] will only return the file's -/// contents once. It's recommended to first call `rewind()` in that case. -/// -/// # Examples -/// -/// [`File`]s implement `Read`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// let mut buffer = [0; 10]; -/// -/// // read up to 10 bytes -/// f.read(&mut buffer)?; -/// -/// let mut buffer = Vec::new(); -/// // read the whole file -/// f.read_to_end(&mut buffer)?; -/// -/// // read into a String, so that you don't need to do the conversion. -/// let mut buffer = String::new(); -/// f.read_to_string(&mut buffer)?; -/// -/// // and more! See the other methods for more details. -/// Ok(()) -/// } -/// ``` -/// -/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: -/// -/// ```no_run -/// # use std::io; -/// use std::io::prelude::*; -/// -/// fn main() -> io::Result<()> { -/// let mut b = "This string will be read".as_bytes(); -/// let mut buffer = [0; 10]; -/// -/// // read up to 10 bytes -/// b.read(&mut buffer)?; -/// -/// // etc... it works exactly as a File does! -/// Ok(()) -/// } -/// ``` -/// -/// [`read()`]: Read::read -/// [`&str`]: prim@str -/// [`std::io`]: self -/// [`File`]: crate::fs::File -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] -pub trait Read { - /// Pull some bytes from this source into the specified buffer, returning - /// how many bytes were read. - /// - /// This function does not provide any guarantees about whether it blocks - /// waiting for data, but if an object needs to block for a read and cannot, - /// it will typically signal this via an [`Err`] return value. - /// - /// If the return value of this method is [`Ok(n)`], then implementations must - /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates - /// that the buffer `buf` has been filled in with `n` bytes of data from this - /// source. If `n` is `0`, then it can indicate one of two scenarios: - /// - /// 1. This reader has reached its "end of file" and will likely no longer - /// be able to produce bytes. Note that this does not mean that the - /// reader will *always* no longer be able to produce bytes. As an example, - /// on Linux, this method will call the `recv` syscall for a [`TcpStream`], - /// where returning zero indicates the connection was shut down correctly. While - /// for [`File`], it is possible to reach the end of file and get zero as result, - /// but if more data is appended to the file, future calls to `read` will return - /// more data. - /// 2. The buffer specified was 0 bytes in length. - /// - /// It is not an error if the returned value `n` is smaller than the buffer size, - /// even when the reader is not at the end of the stream yet. - /// This may happen for example because fewer bytes are actually available right now - /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. - /// - /// As this trait is safe to implement, callers in unsafe code cannot rely on - /// `n <= buf.len()` for safety. - /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. - /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if - /// `n > buf.len()`. - /// - /// *Implementations* of this method can make no assumptions about the contents of `buf` when - /// this function is called. It is recommended that implementations only write data to `buf` - /// instead of reading its contents. - /// - /// Correspondingly, however, *callers* of this method in unsafe code must not assume - /// any guarantees about how the implementation uses `buf`. The trait is safe to implement, - /// so it is possible that the code that's supposed to write to the buffer might also read - /// from it. It is your responsibility to make sure that `buf` is initialized - /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one - /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. - /// - /// [`MaybeUninit`]: crate::mem::MaybeUninit - /// - /// # Errors - /// - /// If this function encounters any form of I/O or other error, an error - /// variant will be returned. If an error is returned then it must be - /// guaranteed that no bytes were read. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read - /// operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`Ok(n)`]: Ok - /// [`File`]: crate::fs::File - /// [`TcpStream`]: crate::net::TcpStream - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = [0; 10]; - /// - /// // read up to 10 bytes - /// let n = f.read(&mut buffer[..])?; - /// - /// println!("The bytes: {:?}", &buffer[..n]); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read(&mut self, buf: &mut [u8]) -> Result; - - /// Like `read`, except that it reads into a slice of buffers. - /// - /// Data is copied to fill each buffer in order, with the final buffer - /// written to possibly being only partially filled. This method must - /// behave equivalently to a single call to `read` with concatenated - /// buffers. - /// - /// The default implementation calls `read` with either the first nonempty - /// buffer provided, or an empty one if none exists. - #[stable(feature = "iovec", since = "1.36.0")] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - default_read_vectored(|b| self.read(b), bufs) - } - - /// Determines if this `Read`er has an efficient `read_vectored` - /// implementation. - /// - /// If a `Read`er does not override the default `read_vectored` - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - #[unstable(feature = "can_vector", issue = "69941")] - fn is_read_vectored(&self) -> bool { - false - } - - /// Reads all bytes until EOF in this source, placing them into `buf`. - /// - /// All bytes read from this source will be appended to the specified buffer - /// `buf`. This function will continuously call [`read()`] to append more data to - /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of - /// non-[`ErrorKind::Interrupted`] kind. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If any other read error is encountered then this function immediately - /// returns. Any bytes which have already been read will be appended to - /// `buf`. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read()`]: Read::read - /// [`Ok(0)`]: Ok - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = Vec::new(); - /// - /// // read the whole file - /// f.read_to_end(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read`] convenience function for reading from a - /// file.) - /// - /// [`std::fs::read`]: crate::fs::read - /// - /// ## Implementing `read_to_end` - /// - /// When implementing the `io::Read` trait, it is recommended to allocate - /// memory using [`Vec::try_reserve`]. However, this behavior is not guaranteed - /// by all implementations, and `read_to_end` may not handle out-of-memory - /// situations gracefully. - /// - /// ```no_run - /// # use std::io::{self, BufRead}; - /// # struct Example { example_datasource: io::Empty } impl Example { - /// # fn get_some_data_for_the_example(&self) -> &'static [u8] { &[] } - /// fn read_to_end(&mut self, dest_vec: &mut Vec) -> io::Result { - /// let initial_vec_len = dest_vec.len(); - /// loop { - /// let src_buf = self.example_datasource.fill_buf()?; - /// if src_buf.is_empty() { - /// break; - /// } - /// dest_vec.try_reserve(src_buf.len())?; - /// dest_vec.extend_from_slice(src_buf); - /// - /// // Any irreversible side effects should happen after `try_reserve` succeeds, - /// // to avoid losing data on allocation error. - /// let read = src_buf.len(); - /// self.example_datasource.consume(read); - /// } - /// Ok(dest_vec.len() - initial_vec_len) - /// } - /// # } - /// ``` - /// - /// # Usage Notes - /// - /// `read_to_end` attempts to read a source until EOF, but many sources are continuous streams - /// that do not send EOF. In these cases, `read_to_end` will block indefinitely. Standard input - /// is one such stream which may be finite if piped, but is typically continuous. For example, - /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. - /// Reading user input or running programs that remain open indefinitely will never terminate - /// the stream with `EOF` (e.g. `yes | my-rust-program`). - /// - /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution - /// - ///[`read`]: Read::read - /// - /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - default_read_to_end(self, buf, None) - } - - /// Reads all bytes until EOF in this source, appending them to `buf`. - /// - /// If successful, this function returns the number of bytes which were read - /// and appended to `buf`. - /// - /// # Errors - /// - /// If the data in this stream is *not* valid UTF-8 then an error is - /// returned and `buf` is unchanged. - /// - /// See [`read_to_end`] for other error semantics. - /// - /// [`read_to_end`]: Read::read_to_end - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = String::new(); - /// - /// f.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - /// - /// (See also the [`std::fs::read_to_string`] convenience function for - /// reading from a file.) - /// - /// # Usage Notes - /// - /// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams - /// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input - /// is one such stream which may be finite if piped, but is typically continuous. For example, - /// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. - /// Reading user input or running programs that remain open indefinitely will never terminate - /// the stream with `EOF` (e.g. `yes | my-rust-program`). - /// - /// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution - /// - ///[`read`]: Read::read - /// - /// [`std::fs::read_to_string`]: crate::fs::read_to_string - #[stable(feature = "rust1", since = "1.0.0")] - fn read_to_string(&mut self, buf: &mut String) -> Result { - default_read_to_string(self, buf, None) - } - - /// Reads the exact number of bytes required to fill `buf`. - /// - /// This function reads as many bytes as necessary to completely fill the - /// specified buffer `buf`. - /// - /// *Implementations* of this method can make no assumptions about the contents of `buf` when - /// this function is called. It is recommended that implementations only write data to `buf` - /// instead of reading its contents. The documentation on [`read`] has a more detailed - /// explanation of this subject. - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`read`]: Read::read - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = [0; 10]; - /// - /// // read exactly 10 bytes - /// f.read_exact(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "read_exact", since = "1.6.0")] - fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { - default_read_exact(self, buf) - } - - /// Pull some bytes from this source into the specified buffer. - /// - /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use - /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`. - /// - /// The default implementation delegates to `read`. - /// - /// This method makes it possible to return both data and an error but it is advised against. - #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - default_read_buf(|b| self.read(b), buf) - } - - /// Reads the exact number of bytes required to fill `cursor`. - /// - /// This is similar to the [`read_exact`](Read::read_exact) method, except - /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use - /// with uninitialized buffers. - /// - /// # Errors - /// - /// If this function encounters an error of the kind [`ErrorKind::Interrupted`] - /// then the error is ignored and the operation will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// - /// If any other read error is encountered then this function immediately - /// returns. - /// - /// If this function returns an error, all bytes read will be appended to `cursor`. - #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { - default_read_buf_exact(self, cursor) - } - - /// Creates a "by reference" adapter for this instance of `Read`. - /// - /// The returned adapter also implements `Read` and will simply borrow this - /// current reader. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::Read; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// let mut buffer = Vec::new(); - /// let mut other_buffer = Vec::new(); - /// - /// { - /// let reference = f.by_ref(); - /// - /// // read at most 5 bytes - /// reference.take(5).read_to_end(&mut buffer)?; - /// - /// } // drop our &mut reference so we can use f again - /// - /// // original file still usable, read the rest - /// f.read_to_end(&mut other_buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } - - /// Transforms this `Read` instance to an [`Iterator`] over its bytes. - /// - /// The returned type implements [`Iterator`] where the [`Item`] is - /// [Result]<[u8], [io::Error]>. - /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] - /// otherwise. EOF is mapped to returning [`None`] from this iterator. - /// - /// The default implementation calls `read` for each byte, - /// which can be very inefficient for data that's not in memory, - /// such as [`File`]. Consider using a [`BufReader`] in such cases. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`Item`]: Iterator::Item - /// [`File`]: crate::fs::File "fs::File" - /// [Result]: crate::result::Result "Result" - /// [io::Error]: self::Error "io::Error" - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = BufReader::new(File::open("foo.txt")?); - /// - /// for byte in f.bytes() { - /// println!("{}", byte?); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn bytes(self) -> Bytes - where - Self: Sized, - { - Bytes { inner: self } - } - - /// Creates an adapter which will chain this stream with another. - /// - /// The returned `Read` instance will first read all bytes from this object - /// until EOF is encountered. Afterwards the output is equivalent to the - /// output of `next`. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f1 = File::open("foo.txt")?; - /// let f2 = File::open("bar.txt")?; - /// - /// let mut handle = f1.chain(f2); - /// let mut buffer = String::new(); - /// - /// // read the value into a String. We could use any Read method here, - /// // this is just one example. - /// handle.read_to_string(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn chain(self, next: R) -> Chain - where - Self: Sized, - { - Chain { first: self, second: next, done_first: false } - } - - /// Creates an adapter which will read at most `limit` bytes from it. - /// - /// This function returns a new instance of `Read` which will read at most - /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any - /// read errors will not count towards the number of bytes read and future - /// calls to [`read()`] may succeed. - /// - /// # Examples - /// - /// [`File`]s implement `Read`: - /// - /// [`File`]: crate::fs::File - /// [`Ok(0)`]: Ok - /// [`read()`]: Read::read - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// let mut buffer = [0; 5]; - /// - /// // read at most five bytes - /// let mut handle = f.take(5); - /// - /// handle.read(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn take(self, limit: u64) -> Take - where - Self: Sized, - { - Take { inner: self, len: limit, limit } - } - - /// Read and return a fixed array of bytes from this source. - /// - /// This function uses an array sized based on a const generic size known at compile time. You - /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference - /// determine the number of bytes needed based on how the return value gets used. For instance, - /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of - /// bytes into an integer of the same size. - /// - /// Like `read_exact`, if this function encounters an "end of file" before reading the desired - /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// - /// ``` - /// #![feature(read_array)] - /// use std::io::Cursor; - /// use std::io::prelude::*; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); - /// let x = u64::from_le_bytes(buf.read_array()?); - /// let y = u32::from_be_bytes(buf.read_array()?); - /// let z = u16::from_be_bytes(buf.read_array()?); - /// assert_eq!(x, 0x807060504030201); - /// assert_eq!(y, 0x9080706); - /// assert_eq!(z, 0x504); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "read_array", issue = "148848")] - fn read_array(&mut self) -> Result<[u8; N]> - where - Self: Sized, - { - let mut buf = [MaybeUninit::uninit(); N]; - let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); - self.read_buf_exact(borrowed_buf.unfilled())?; - // Guard against incorrect `read_buf_exact` implementations. - assert_eq!(borrowed_buf.len(), N); - Ok(unsafe { MaybeUninit::array_assume_init(buf) }) - } -} - -/// Reads all bytes from a [reader][Read] into a new [`String`]. -/// -/// This is a convenience function for [`Read::read_to_string`]. Using this -/// function avoids having to create a variable first and provides more type -/// safety since you can only get the buffer out if there were no errors. (If you -/// use [`Read::read_to_string`] you have to remember to check whether the read -/// succeeded because otherwise your buffer will be empty or only partially full.) -/// -/// # Performance -/// -/// The downside of this function's increased ease of use and type safety is -/// that it gives you less control over performance. For example, you can't -/// pre-allocate memory like you can using [`String::with_capacity`] and -/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error -/// occurs while reading. -/// -/// In many cases, this function's performance will be adequate and the ease of use -/// and type safety tradeoffs will be worth it. However, there are cases where you -/// need more control over performance, and in those cases you should definitely use -/// [`Read::read_to_string`] directly. -/// -/// Note that in some special cases, such as when reading files, this function will -/// pre-allocate memory based on the size of the input it is reading. In those -/// cases, the performance should be as good as if you had used -/// [`Read::read_to_string`] with a manually pre-allocated buffer. -/// -/// # Errors -/// -/// This function forces you to handle errors because the output (the `String`) -/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors -/// that can occur. If any error occurs, you will get an [`Err`], so you -/// don't have to worry about your buffer being empty or partially full. -/// -/// # Examples -/// -/// ```no_run -/// # use std::io; -/// fn main() -> io::Result<()> { -/// let stdin = io::read_to_string(io::stdin())?; -/// println!("Stdin was:"); -/// println!("{stdin}"); -/// Ok(()) -/// } -/// ``` -/// -/// # Usage Notes -/// -/// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams -/// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input -/// is one such stream which may be finite if piped, but is typically continuous. For example, -/// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat. -/// Reading user input or running programs that remain open indefinitely will never terminate -/// the stream with `EOF` (e.g. `yes | my-rust-program`). -/// -/// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution -/// -///[`read`]: Read::read -/// -#[stable(feature = "io_read_to_string", since = "1.65.0")] -pub fn read_to_string(mut reader: R) -> Result { - let mut buf = String::new(); - reader.read_to_string(&mut buf)?; - Ok(buf) -} - -/// A buffer type used with `Read::read_vectored`. -/// -/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[repr(transparent)] -pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSliceMut<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSliceMut<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSliceMut<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSliceMut<'a> { - /// Creates a new `IoSliceMut` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(sys::io::IoSliceMut::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of - /// multiple buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut data = [1; 8]; - /// let mut buf = IoSliceMut::new(&mut data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes, - /// the result will only include the second `IoSliceMut`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), - /// ][..]; - /// - /// // Mark 10 bytes as read. - /// IoSliceMut::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a mutable slice with the original lifetime. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSliceMut; - /// - /// let mut data = *b"abcdef"; - /// let io_slice = IoSliceMut::new(&mut data); - /// io_slice.into_slice()[0] = b'A'; - /// - /// assert_eq!(&data, b"Abcdef"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn into_slice(self) -> &'a mut [u8] { - self.0.into_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSliceMut<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> DerefMut for IoSliceMut<'a> { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - self.0.as_mut_slice() - } -} - -/// A buffer type used with `Write::write_vectored`. -/// -/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a>(sys::io::IoSlice<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSlice<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSlice<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSlice<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSlice<'a> { - /// Creates a new `IoSlice` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[must_use] - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice(sys::io::IoSlice::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple - /// buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let data = [1; 8]; - /// let mut buf = IoSlice::new(&data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSlice`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes, - /// the result will only include the second `IoSlice`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let buf1 = [1; 8]; - /// let buf2 = [2; 16]; - /// let buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSlice::new(&buf1), - /// IoSlice::new(&buf2), - /// IoSlice::new(&buf3), - /// ][..]; - /// - /// // Mark 10 bytes as written. - /// IoSlice::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. This prevents overflow - // that could happen if the length of slices in `bufs` were instead - // accumulated. Those slice may be aliased and, if they are large - // enough, their added length may overflow a `usize`. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a slice with the original lifetime. - /// - /// This doesn't borrow from `self`, so is less restrictive than calling - /// `.deref()`, which does. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSlice; - /// - /// let data = b"abcdef"; - /// - /// let mut io_slice = IoSlice::new(data); - /// let tail = &io_slice.as_slice()[3..]; - /// - /// // This works because `tail` doesn't borrow `io_slice` - /// io_slice = IoSlice::new(tail); - /// - /// assert_eq!(io_slice.as_slice(), b"def"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn as_slice(self) -> &'a [u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSlice<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -/// A trait for objects which are byte-oriented sinks. -/// -/// Implementors of the `Write` trait are sometimes called 'writers'. -/// -/// Writers are defined by two required methods, [`write`] and [`flush`]: -/// -/// * The [`write`] method will attempt to write some data into the object, -/// returning how many bytes were successfully written. -/// -/// * The [`flush`] method is useful for adapters and explicit buffers -/// themselves for ensuring that all buffered data has been pushed out to the -/// 'true sink'. -/// -/// Writers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Write` -/// trait. -/// -/// [`write`]: Write::write -/// [`flush`]: Write::flush -/// [`std::io`]: self -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let data = b"some bytes"; -/// -/// let mut pos = 0; -/// let mut buffer = File::create("foo.txt")?; -/// -/// while pos < data.len() { -/// let bytes_written = buffer.write(&data[pos..])?; -/// pos += bytes_written; -/// } -/// Ok(()) -/// } -/// ``` -/// -/// The trait also provides convenience methods like [`write_all`], which calls -/// `write` in a loop until its entire input has been written. -/// -/// [`write_all`]: Write::write_all -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] -pub trait Write { - /// Writes a buffer into this writer, returning how many bytes were written. - /// - /// This function will attempt to write the entire contents of `buf`, but - /// the entire write might not succeed, or the write may also generate an - /// error. Typically, a call to `write` represents one attempt to write to - /// any wrapped object. - /// - /// Calls to `write` are not guaranteed to block waiting for data to be - /// written, and a write which would otherwise block can be indicated through - /// an [`Err`] variant. - /// - /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. - /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. - /// A return value of `Ok(0)` typically means that the underlying object is - /// no longer able to accept bytes and will likely not be able to in the - /// future as well, or that the buffer provided is empty. - /// - /// # Errors - /// - /// Each call to `write` may generate an I/O error indicating that the - /// operation could not be completed. If an error is returned then no bytes - /// in the buffer were written to this writer. - /// - /// It is **not** considered an error if the entire buffer could not be - /// written to this writer. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the - /// write operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - /// - /// [`Ok(n)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, buf: &[u8]) -> Result; - - /// Like [`write`], except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer - /// read from possibly being only partially consumed. This method must - /// behave as a call to [`write`] with the buffers concatenated would. - /// - /// The default implementation calls [`write`] with either the first nonempty - /// buffer provided, or an empty one if none exists. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::IoSlice; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let data1 = [1; 8]; - /// let data2 = [15; 8]; - /// let io_slice1 = IoSlice::new(&data1); - /// let io_slice2 = IoSlice::new(&data2); - /// - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write_vectored(&[io_slice1, io_slice2])?; - /// Ok(()) - /// } - /// ``` - /// - /// [`write`]: Write::write - #[stable(feature = "iovec", since = "1.36.0")] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - default_write_vectored(|b| self.write(b), bufs) - } - - /// Determines if this `Write`r has an efficient [`write_vectored`] - /// implementation. - /// - /// If a `Write`r does not override the default [`write_vectored`] - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - /// - /// [`write_vectored`]: Write::write_vectored - #[unstable(feature = "can_vector", issue = "69941")] - fn is_write_vectored(&self) -> bool { - false - } - - /// Flushes this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// # Errors - /// - /// It is considered an error if not all bytes could be written due to - /// I/O errors or EOF being reached. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::io::BufWriter; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = BufWriter::new(File::create("foo.txt")?); - /// - /// buffer.write_all(b"some bytes")?; - /// buffer.flush()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn flush(&mut self) -> Result<()>; - - /// Attempts to write an entire buffer into this writer. - /// - /// This method will continuously call [`write`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// If the buffer contains no data, this will never call [`write`]. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. - /// - /// [`write`]: Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// buffer.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => buf = &buf[n..], - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Attempts to write multiple buffers into this writer. - /// - /// This method will continuously call [`write_vectored`] until there is no - /// more data to be written or an error of non-[`ErrorKind::Interrupted`] - /// kind is returned. This method will not return until all buffers have - /// been successfully written or such an error occurs. The first error that - /// is not of [`ErrorKind::Interrupted`] kind generated from this method - /// will be returned. - /// - /// If the buffer contains no data, this will never call [`write_vectored`]. - /// - /// # Notes - /// - /// Unlike [`write_vectored`], this takes a *mutable* reference to - /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to - /// modify the slice to keep track of the bytes already written. - /// - /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to [`write_vectored`] were necessary. It is - /// best to understand this function as taking ownership of `bufs` and to - /// not use `bufs` afterwards. The underlying buffers, to which the - /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and - /// can be reused. - /// - /// [`write_vectored`]: Write::write_vectored - /// - /// # Examples - /// - /// ``` - /// #![feature(write_all_vectored)] - /// # fn main() -> std::io::Result<()> { - /// - /// use std::io::{Write, IoSlice}; - /// - /// let mut writer = Vec::new(); - /// let bufs = &mut [ - /// IoSlice::new(&[1]), - /// IoSlice::new(&[2, 3]), - /// IoSlice::new(&[4, 5, 6]), - /// ]; - /// - /// writer.write_all_vectored(bufs)?; - /// // Note: the contents of `bufs` is now undefined, see the Notes section. - /// - /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); - /// # Ok(()) } - /// ``` - #[unstable(feature = "write_all_vectored", issue = "70436")] - fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { - // Guarantee that bufs is empty if it contains no data, - // to avoid calling write_vectored if there is no data to be written. - IoSlice::advance_slices(&mut bufs, 0); - while !bufs.is_empty() { - match self.write_vectored(bufs) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => IoSlice::advance_slices(&mut bufs, n), - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the - /// [`format_args!()`] macro, and it is rare that this should - /// explicitly be called. The [`write!()`] macro should be favored to - /// invoke this method instead. - /// - /// This function internally uses the [`write_all`] method on - /// this trait and hence will continuously write data so long as no errors - /// are received. This also means that partial writes are not indicated in - /// this signature. - /// - /// [`write_all`]: Write::write_all - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // this call - /// write!(buffer, "{:.*}", 2, 1.234567)?; - /// // turns into this: - /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { - if let Some(s) = args.as_statically_known_str() { - self.write_all(s.as_bytes()) - } else { - default_write_fmt(self, args) - } - } - - /// Creates a "by reference" adapter for this instance of `Write`. - /// - /// The returned adapter also implements `Write` and will simply borrow this - /// current writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::Write; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// let reference = buffer.by_ref(); - /// - /// // we can use reference just like our original buffer - /// reference.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -/// -/// The stream typically has a fixed size, allowing seeking relative to either -/// end or the current offset. -/// -/// # Examples -/// -/// [`File`]s implement `Seek`: -/// -/// [`File`]: crate::fs::File -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// use std::io::SeekFrom; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// -/// // move the cursor 42 bytes from the start of the file -/// f.seek(SeekFrom::Start(42))?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// If the seek operation completed successfully, - /// this method returns the new position from the start of the stream. - /// That position can be used later with [`SeekFrom::Start`]. - /// - /// # Errors - /// - /// Seeking can fail, for example because it might involve flushing a buffer. - /// - /// Seeking to a negative offset is considered an error. - #[stable(feature = "rust1", since = "1.0.0")] - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Rewind to the beginning of a stream. - /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. - /// - /// # Errors - /// - /// Rewinding can fail, for example because it might involve flushing a buffer. - /// - /// # Example - /// - /// ```no_run - /// use std::io::{Read, Seek, Write}; - /// use std::fs::OpenOptions; - /// - /// let mut f = OpenOptions::new() - /// .write(true) - /// .read(true) - /// .create(true) - /// .open("foo.txt")?; - /// - /// let hello = "Hello!\n"; - /// write!(f, "{hello}")?; - /// f.rewind()?; - /// - /// let mut buf = String::new(); - /// f.read_to_string(&mut buf)?; - /// assert_eq!(&buf, hello); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "seek_rewind", since = "1.55.0")] - fn rewind(&mut self) -> Result<()> { - self.seek(SeekFrom::Start(0))?; - Ok(()) - } - - /// Returns the length of this stream (in bytes). - /// - /// The default implementation uses up to three seek operations. If this - /// method returns successfully, the seek position is unchanged (i.e. the - /// position before calling this method is the same as afterwards). - /// However, if this method returns an error, the seek position is - /// unspecified. - /// - /// If you need to obtain the length of *many* streams and you don't care - /// about the seek position afterwards, you can reduce the number of seek - /// operations by simply calling `seek(SeekFrom::End(0))` and using its - /// return value (it is also the stream length). - /// - /// Note that length of a stream can change over time (for example, when - /// data is appended to a file). So calling this method multiple times does - /// not necessarily return the same length each time. - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_stream_len)] - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// let len = f.stream_len()?; - /// println!("The file is currently {len} bytes long"); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_stream_len", issue = "59359")] - fn stream_len(&mut self) -> Result { - stream_len_default(self) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "seek_convenience", since = "1.51.0")] - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } - - /// Seeks relative to the current position. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but - /// doesn't return the new position which can allow some implementations - /// such as [`BufReader`] to perform more efficient seeks. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// f.seek_relative(10)?; - /// assert_eq!(f.stream_position()?, 10); - /// Ok(()) - /// } - /// ``` - /// - /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "1.80.0")] - fn seek_relative(&mut self, offset: i64) -> Result<()> { - self.seek(SeekFrom::Current(offset))?; - Ok(()) - } -} - -pub(crate) fn stream_len_default(self_: &mut T) -> Result { - let old_pos = self_.stream_position()?; - let len = self_.seek(SeekFrom::End(0))?; - - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self_.seek(SeekFrom::Start(old_pos))?; - } - - Ok(len) -} - -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] -pub enum SeekFrom { - /// Sets the offset to the provided number of bytes. - #[stable(feature = "rust1", since = "1.0.0")] - Start(#[stable(feature = "rust1", since = "1.0.0")] u64), - - /// Sets the offset to the size of this object plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - End(#[stable(feature = "rust1", since = "1.0.0")] i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - Current(#[stable(feature = "rust1", since = "1.0.0")] i64), -} - -fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => { - buf.extend_from_slice(&available[..=i]); - (true, i + 1) - } - None => { - buf.extend_from_slice(available); - (false, available.len()) - } - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -fn skip_until(r: &mut R, delim: u8) -> Result { - let mut read = 0; - loop { - let (done, used) = { - let available = match r.fill_buf() { - Ok(n) => n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - match memchr::memchr(delim, available) { - Some(i) => (true, i + 1), - None => (false, available.len()), - } - }; - r.consume(used); - read += used; - if done || used == 0 { - return Ok(read); - } - } -} - -/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it -/// to perform extra ways of reading. -/// -/// For example, reading line-by-line is inefficient without using a buffer, so -/// if you want to read by line, you'll need `BufRead`, which includes a -/// [`read_line`] method as well as a [`lines`] iterator. -/// -/// # Examples -/// -/// A locked standard input implements `BufRead`: -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// -/// let stdin = io::stdin(); -/// for line in stdin.lock().lines() { -/// println!("{}", line?); -/// } -/// # std::io::Result::Ok(()) -/// ``` -/// -/// If you have something that implements [`Read`], you can use the [`BufReader` -/// type][`BufReader`] to turn it into a `BufRead`. -/// -/// For example, [`File`] implements [`Read`], but not `BufRead`. -/// [`BufReader`] to the rescue! -/// -/// [`File`]: crate::fs::File -/// [`read_line`]: BufRead::read_line -/// [`lines`]: BufRead::lines -/// -/// ```no_run -/// use std::io::{self, BufReader}; -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> io::Result<()> { -/// let f = File::open("foo.txt")?; -/// let f = BufReader::new(f); -/// -/// for line in f.lines() { -/// let line = line?; -/// println!("{line}"); -/// } -/// -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] -pub trait BufRead: Read { - /// Returns the contents of the internal buffer, filling it with more data, via `Read` methods, if empty. - /// - /// This is a lower-level method and is meant to be used together with [`consume`], - /// which can be used to mark bytes that should not be returned by subsequent calls to `read`. - /// - /// [`consume`]: BufRead::consume - /// - /// Returns an empty buffer when the stream has reached EOF. - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// # Examples - /// - /// A locked standard input implements `BufRead`: - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// let buffer = stdin.fill_buf()?; - /// - /// // work with buffer - /// println!("{buffer:?}"); - /// - /// // mark the bytes we worked with as read - /// let length = buffer.len(); - /// stdin.consume(length); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn fill_buf(&mut self) -> Result<&[u8]>; - - /// Marks the given `amount` of additional bytes from the internal buffer as having been read. - /// Subsequent calls to `read` only return bytes that have not been marked as read. - /// - /// This is a lower-level method and is meant to be used together with [`fill_buf`], - /// which can be used to fill the internal buffer via `Read` methods. - /// - /// It is a logic error if `amount` exceeds the number of unread bytes in the internal buffer, which is returned by [`fill_buf`]. - /// - /// # Examples - /// - /// Since `consume()` is meant to be used with [`fill_buf`], - /// that method's example includes an example of `consume()`. - /// - /// [`fill_buf`]: BufRead::fill_buf - #[stable(feature = "rust1", since = "1.0.0")] - fn consume(&mut self, amount: usize); - - /// Checks if there is any data left to be `read`. - /// - /// This function may fill the buffer to check for data, - /// so this function returns `Result`, not `bool`. - /// - /// The default implementation calls `fill_buf` and checks that the - /// returned slice is empty (which means that there is no data left, - /// since EOF is reached). - /// - /// # Errors - /// - /// This function will return an I/O error if a `Read` method was called, but returned an error. - /// - /// Examples - /// - /// ``` - /// #![feature(buf_read_has_data_left)] - /// use std::io; - /// use std::io::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut stdin = stdin.lock(); - /// - /// while stdin.has_data_left()? { - /// let mut line = String::new(); - /// stdin.read_line(&mut line)?; - /// // work with line - /// println!("{line:?}"); - /// } - /// # std::io::Result::Ok(()) - /// ``` - #[unstable(feature = "buf_read_has_data_left", issue = "86423")] - fn has_data_left(&mut self) -> Result { - self.fill_buf().map(|b| !b.is_empty()) - } - - /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - /// - /// This function will read bytes from the underlying stream until the - /// delimiter or EOF is found. Once found, all bytes up to, and including, - /// the delimiter (if found) will be appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the bytes in a byte slice - /// in hyphen delimited segments: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); - /// let mut buf = vec![]; - /// - /// // cursor is at 'l' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 6); - /// assert_eq!(buf, b"lorem-"); - /// buf.clear(); - /// - /// // cursor is at 'i' - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 5); - /// assert_eq!(buf, b"ipsum"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_until(b'-', &mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, b""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - read_until(self, byte, buf) - } - - /// Skips all bytes until the delimiter `byte` or EOF is reached. - /// - /// This function will read (and discard) bytes from the underlying stream until the - /// delimiter or EOF is found. - /// - /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte if found. - /// - /// This is useful for efficiently skipping data such as NUL-terminated strings - /// in binary file formats without buffering. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending the delimiter - /// or EOF. - /// - /// # Errors - /// - /// This function will ignore all instances of [`ErrorKind::Interrupted`] and - /// will otherwise return any errors returned by [`fill_buf`]. - /// - /// If an I/O error is encountered then all bytes read so far will be - /// present in `buf` and its length will have been adjusted appropriately. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read some NUL-terminated information - /// about Ferris from a binary string, skipping the fun fact: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); - /// - /// // read name - /// let mut name = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut name) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 7); - /// assert_eq!(name, b"Ferris\0"); - /// - /// // skip fun fact - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 30); - /// - /// // read animal type - /// let mut animal = Vec::new(); - /// let num_bytes = cursor.read_until(b'\0', &mut animal) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 11); - /// assert_eq!(animal, b"Crustacean\0"); - /// - /// // reach EOF - /// let num_bytes = cursor.skip_until(b'\0') - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 1); - /// ``` - #[stable(feature = "bufread_skip_until", since = "1.83.0")] - fn skip_until(&mut self, byte: u8) -> Result { - skip_until(self, byte) - } - - /// Reads all bytes until a newline (the `0xA` byte) is reached, and append - /// them to the provided `String` buffer. - /// - /// Previous content of the buffer will be preserved. To avoid appending to - /// the buffer, you need to [`clear`] it first. - /// - /// This function will read bytes from the underlying stream until the - /// newline delimiter (the `0xA` byte) or EOF is found. Once found, all bytes - /// up to, and including, the delimiter (if found) will be appended to - /// `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// If this function returns [`Ok(0)`], the stream has reached EOF. - /// - /// This function is blocking and should be used carefully: it is possible for - /// an attacker to continuously send bytes without ever sending a newline - /// or EOF. You can use [`take`] to limit the maximum number of bytes read. - /// - /// [`Ok(0)`]: Ok - /// [`clear`]: String::clear - /// [`take`]: crate::io::Read::take - /// - /// # Errors - /// - /// This function has the same error semantics as [`read_until`] and will - /// also return an error if the read bytes are not valid UTF-8. If an I/O - /// error is encountered then `buf` may contain some bytes already read in - /// the event that all data read so far was valid UTF-8. - /// - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to read all the lines in a byte slice: - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let mut cursor = io::Cursor::new(b"foo\nbar"); - /// let mut buf = String::new(); - /// - /// // cursor is at 'f' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 4); - /// assert_eq!(buf, "foo\n"); - /// buf.clear(); - /// - /// // cursor is at 'b' - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 3); - /// assert_eq!(buf, "bar"); - /// buf.clear(); - /// - /// // cursor is at EOF - /// let num_bytes = cursor.read_line(&mut buf) - /// .expect("reading from cursor won't fail"); - /// assert_eq!(num_bytes, 0); - /// assert_eq!(buf, ""); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn read_line(&mut self, buf: &mut String) -> Result { - // Note that we are not calling the `.read_until` method here, but - // rather our hardcoded implementation. For more details as to why, see - // the comments in `default_read_to_string`. - unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) } - } - - /// Returns an iterator over the contents of this reader split on the byte - /// `byte`. - /// - /// The iterator returned from this function will return instances of - /// [io::Result]<[Vec]\>. Each vector returned will *not* have - /// the delimiter byte at the end. - /// - /// This function will yield errors whenever [`read_until`] would have - /// also yielded an error. - /// - /// [io::Result]: self::Result "io::Result" - /// [`read_until`]: BufRead::read_until - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all hyphen delimited - /// segments in a byte slice - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); - /// - /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); - /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); - /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); - /// assert_eq!(split_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn split(self, byte: u8) -> Split - where - Self: Sized, - { - Split { buf: self, delim: byte } - } - - /// Returns an iterator over the lines of this reader. - /// - /// The iterator returned from this function will yield instances of - /// [io::Result]<[String]>. Each string returned will *not* have a newline - /// byte (the `0xA` byte) or `CRLF` (`0xD`, `0xA` bytes) at the end. - /// - /// [io::Result]: self::Result "io::Result" - /// - /// # Examples - /// - /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In - /// this example, we use [`Cursor`] to iterate over all the lines in a byte - /// slice. - /// - /// ``` - /// use std::io::{self, BufRead}; - /// - /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); - /// - /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); - /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); - /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); - /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); - /// assert_eq!(lines_iter.next(), None); - /// ``` - /// - /// # Errors - /// - /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. - #[stable(feature = "rust1", since = "1.0.0")] - fn lines(self) -> Lines - where - Self: Sized, - { - Lines { buf: self } - } -} - -/// Adapter to chain together two readers. -/// -/// This struct is generally created by calling [`chain`] on a reader. -/// Please see the documentation of [`chain`] for more details. -/// -/// [`chain`]: Read::chain -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Chain { - first: T, - second: U, - done_first: bool, -} - -impl Chain { - /// Consumes the `Chain`, returning the wrapped readers. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn into_inner(self) -> (T, U) { - (self.first, self.second) - } - - /// Gets references to the underlying readers in this `Chain`. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying readers as doing so may corrupt the internal state of this - /// `Chain`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_ref(&self) -> (&T, &U) { - (&self.first, &self.second) - } - - /// Gets mutable references to the underlying readers in this `Chain`. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying readers as doing so may corrupt the internal state of this - /// `Chain`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut foo_file = File::open("foo.txt")?; - /// let mut bar_file = File::open("bar.txt")?; - /// - /// let mut chain = foo_file.chain(bar_file); - /// let (foo_file, bar_file) = chain.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_mut(&mut self) -> (&mut T, &mut U) { - (&mut self.first, &mut self.second) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Chain { - fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.done_first { - match self.first.read(buf)? { - 0 if !buf.is_empty() => self.done_first = true, - n => return Ok(n), - } - } - self.second.read(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - if !self.done_first { - match self.first.read_vectored(bufs)? { - 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, - n => return Ok(n), - } - } - self.second.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.first.is_read_vectored() || self.second.is_read_vectored() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - let mut read = 0; - if !self.done_first { - read += self.first.read_to_end(buf)?; - self.done_first = true; - } - read += self.second.read_to_end(buf)?; - Ok(read) - } - - // We don't override `read_to_string` here because an UTF-8 sequence could - // be split between the two parts of the chain - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { - if buf.capacity() == 0 { - return Ok(()); - } - - if !self.done_first { - let old_len = buf.written(); - self.first.read_buf(buf.reborrow())?; - - if buf.written() != old_len { - return Ok(()); - } else { - self.done_first = true; - } - } - self.second.read_buf(buf) - } -} - -#[stable(feature = "chain_bufread", since = "1.9.0")] -impl BufRead for Chain { - fn fill_buf(&mut self) -> Result<&[u8]> { - if !self.done_first { - match self.first.fill_buf()? { - buf if buf.is_empty() => self.done_first = true, - buf => return Ok(buf), - } - } - self.second.fill_buf() - } - - fn consume(&mut self, amt: usize) { - if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) } - } - - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { - let mut read = 0; - if !self.done_first { - let n = self.first.read_until(byte, buf)?; - read += n; - - match buf.last() { - Some(b) if *b == byte && n != 0 => return Ok(read), - _ => self.done_first = true, - } - } - read += self.second.read_until(byte, buf)?; - Ok(read) - } - - // We don't override `read_line` here because an UTF-8 sequence could be - // split between the two parts of the chain -} - -impl SizeHint for Chain { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second) - } - - #[inline] - fn upper_bound(&self) -> Option { - match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) { - (Some(first), Some(second)) => first.checked_add(second), - _ => None, - } - } -} - -/// Reader adapter which limits the bytes read from an underlying reader. -/// -/// This struct is generally created by calling [`take`] on a reader. -/// Please see the documentation of [`take`] for more details. -/// -/// [`take`]: Read::take -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Take { - inner: T, - len: u64, - limit: u64, -} - -impl Take { - /// Returns the number of bytes that can be read before this instance will - /// return EOF. - /// - /// # Note - /// - /// This instance may reach `EOF` after reading fewer bytes than indicated by - /// this method if the underlying [`Read`] instance reaches EOF. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// - /// // read at most five bytes - /// let handle = f.take(5); - /// - /// println!("limit: {}", handle.limit()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn limit(&self) -> u64 { - self.limit - } - - /// Returns the number of bytes read so far. - #[unstable(feature = "seek_io_take_position", issue = "97227")] - pub fn position(&self) -> u64 { - self.len - self.limit - } - - /// Sets the number of bytes that can be read before this instance will - /// return EOF. This is the same as constructing a new `Take` instance, so - /// the amount of bytes read and the previous limit value don't matter when - /// calling this method. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let f = File::open("foo.txt")?; - /// - /// // read at most five bytes - /// let mut handle = f.take(5); - /// handle.set_limit(10); - /// - /// assert_eq!(handle.limit(), 10); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "take_set_limit", since = "1.27.0")] - pub fn set_limit(&mut self, limit: u64) { - self.len = limit; - self.limit = limit; - } - - /// Consumes the `Take`, returning the wrapped reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "io_take_into_inner", since = "1.15.0")] - pub fn into_inner(self) -> T { - self.inner - } - - /// Gets a reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_ref(&self) -> &T { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> io::Result<()> { - /// let mut file = File::open("foo.txt")?; - /// - /// let mut buffer = [0; 5]; - /// let mut handle = file.take(5); - /// handle.read(&mut buffer)?; - /// - /// let file = handle.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "more_io_inner_methods", since = "1.20.0")] - pub fn get_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Take { - fn read(&mut self, buf: &mut [u8]) -> Result { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(0); - } - - let max = cmp::min(buf.len() as u64, self.limit) as usize; - let n = self.inner.read(&mut buf[..max])?; - assert!(n as u64 <= self.limit, "number of read bytes exceeds limit"); - self.limit -= n as u64; - Ok(n) - } - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(()); - } - - if self.limit < buf.capacity() as u64 { - // The condition above guarantees that `self.limit` fits in `usize`. - let limit = self.limit as usize; - - let extra_init = cmp::min(limit, buf.init_mut().len()); - - // SAFETY: no uninit data is written to ibuf - let ibuf = unsafe { &mut buf.as_mut()[..limit] }; - - let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); - - // SAFETY: extra_init bytes of ibuf are known to be initialized - unsafe { - sliced_buf.set_init(extra_init); - } - - let mut cursor = sliced_buf.unfilled(); - let result = self.inner.read_buf(cursor.reborrow()); - - let new_init = cursor.init_mut().len(); - let filled = sliced_buf.len(); - - // cursor / sliced_buf / ibuf must drop here - - unsafe { - // SAFETY: filled bytes have been filled and therefore initialized - buf.advance_unchecked(filled); - // SAFETY: new_init bytes of buf's unfilled buffer have been initialized - buf.set_init(new_init); - } - - self.limit -= filled as u64; - - result - } else { - let written = buf.written(); - let result = self.inner.read_buf(buf.reborrow()); - self.limit -= (buf.written() - written) as u64; - result - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Take { - fn fill_buf(&mut self) -> Result<&[u8]> { - // Don't call into inner reader at all at EOF because it may still block - if self.limit == 0 { - return Ok(&[]); - } - - let buf = self.inner.fill_buf()?; - let cap = cmp::min(buf.len() as u64, self.limit) as usize; - Ok(&buf[..cap]) - } - - fn consume(&mut self, amt: usize) { - // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit) as usize; - self.limit -= amt as u64; - self.inner.consume(amt); - } -} - -impl SizeHint for Take { - #[inline] - fn lower_bound(&self) -> usize { - cmp::min(SizeHint::lower_bound(&self.inner) as u64, self.limit) as usize - } - - #[inline] - fn upper_bound(&self) -> Option { - match SizeHint::upper_bound(&self.inner) { - Some(upper_bound) => Some(cmp::min(upper_bound as u64, self.limit) as usize), - None => self.limit.try_into().ok(), - } - } -} - -#[stable(feature = "seek_io_take", since = "1.89.0")] -impl Seek for Take { - fn seek(&mut self, pos: SeekFrom) -> Result { - let new_position = match pos { - SeekFrom::Start(v) => Some(v), - SeekFrom::Current(v) => self.position().checked_add_signed(v), - SeekFrom::End(v) => self.len.checked_add_signed(v), - }; - let new_position = match new_position { - Some(v) if v <= self.len => v, - _ => return Err(ErrorKind::InvalidInput.into()), - }; - while new_position != self.position() { - if let Some(offset) = new_position.checked_signed_diff(self.position()) { - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - break; - } - let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - } - Ok(new_position) - } - - fn stream_len(&mut self) -> Result { - Ok(self.len) - } - - fn stream_position(&mut self) -> Result { - Ok(self.position()) - } - - fn seek_relative(&mut self, offset: i64) -> Result<()> { - if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { - return Err(ErrorKind::InvalidInput.into()); - } - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - Ok(()) - } -} - -/// An iterator over `u8` values of a reader. -/// -/// This struct is generally created by calling [`bytes`] on a reader. -/// Please see the documentation of [`bytes`] for more details. -/// -/// [`bytes`]: Read::bytes -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Bytes { - inner: R, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Bytes { - type Item = Result; - - // Not `#[inline]`. This function gets inlined even without it, but having - // the inline annotation can result in worse code generation. See #116785. - fn next(&mut self) -> Option> { - SpecReadByte::spec_read_byte(&mut self.inner) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - SizeHint::size_hint(&self.inner) - } -} - -/// For the specialization of `Bytes::next`. -trait SpecReadByte { - fn spec_read_byte(&mut self) -> Option>; -} - -impl SpecReadByte for R -where - Self: Read, -{ - #[inline] - default fn spec_read_byte(&mut self) -> Option> { - inlined_slow_read_byte(self) - } -} - -/// Reads a single byte in a slow, generic way. This is used by the default -/// `spec_read_byte`. -#[inline] -fn inlined_slow_read_byte(reader: &mut R) -> Option> { - let mut byte = 0; - loop { - return match reader.read(slice::from_mut(&mut byte)) { - Ok(0) => None, - Ok(..) => Some(Ok(byte)), - Err(ref e) if e.is_interrupted() => continue, - Err(e) => Some(Err(e)), - }; - } -} - -// Used by `BufReader::spec_read_byte`, for which the `inline(never)` is -// important. -#[inline(never)] -fn uninlined_slow_read_byte(reader: &mut R) -> Option> { - inlined_slow_read_byte(reader) -} - -trait SizeHint { - fn lower_bound(&self) -> usize; - - fn upper_bound(&self) -> Option; - - fn size_hint(&self) -> (usize, Option) { - (self.lower_bound(), self.upper_bound()) - } -} - -impl SizeHint for T { - #[inline] - default fn lower_bound(&self) -> usize { - 0 - } - - #[inline] - default fn upper_bound(&self) -> Option { - None - } -} - -impl SizeHint for &mut T { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(*self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(*self) - } -} - -impl SizeHint for Box { - #[inline] - fn lower_bound(&self) -> usize { - SizeHint::lower_bound(&**self) - } - - #[inline] - fn upper_bound(&self) -> Option { - SizeHint::upper_bound(&**self) - } -} - -impl SizeHint for &[u8] { - #[inline] - fn lower_bound(&self) -> usize { - self.len() - } - - #[inline] - fn upper_bound(&self) -> Option { - Some(self.len()) - } -} - -/// An iterator over the contents of an instance of `BufRead` split on a -/// particular byte. -/// -/// This struct is generally created by calling [`split`] on a `BufRead`. -/// Please see the documentation of [`split`] for more details. -/// -/// [`split`]: BufRead::split -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Split { - buf: B, - delim: u8, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Split { - type Item = Result>; - - fn next(&mut self) -> Option>> { - let mut buf = Vec::new(); - match self.buf.read_until(self.delim, &mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf[buf.len() - 1] == self.delim { - buf.pop(); - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} - -/// An iterator over the lines of an instance of `BufRead`. -/// -/// This struct is generally created by calling [`lines`] on a `BufRead`. -/// Please see the documentation of [`lines`] for more details. -/// -/// [`lines`]: BufRead::lines -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")] -pub struct Lines { - buf: B, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Lines { - type Item = Result; - - fn next(&mut self) -> Option> { - let mut buf = String::new(); - match self.buf.read_line(&mut buf) { - Ok(0) => None, - Ok(_n) => { - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Some(Ok(buf)) - } - Err(e) => Some(Err(e)), - } - } -} diff --git a/crates/std/src/io/pipe.rs b/crates/std/src/io/pipe.rs deleted file mode 100644 index 61b81cf..0000000 --- a/crates/std/src/io/pipe.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::io; -use crate::sys::{FromInner, IntoInner, pipe as imp}; - -/// Creates an anonymous pipe. -/// -/// # Behavior -/// -/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is -/// typically used to communicate between two or more separate processes, as there are better, -/// faster ways to communicate within a single process. -/// -/// In particular: -/// -/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. -/// * A write on a [`PipeWriter`] blocks when the pipe is full. -/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] -/// returns EOF. -/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but -/// writes (above a target-specific threshold) may have their data interleaved. -/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any -/// given byte will only get consumed by one reader. There are no guarantees about data -/// interleaving. -/// * Portable applications cannot assume any atomicity of messages larger than a single byte. -/// -/// # Platform-specific behavior -/// -/// This function currently corresponds to the `pipe` function on Unix and the -/// `CreatePipe` function on Windows. -/// -/// Note that this [may change in the future][changes]. -/// -/// # Capacity -/// -/// Pipe capacity is platform dependent. To quote the Linux [man page]: -/// -/// > Different implementations have different limits for the pipe capacity. Applications should -/// > not rely on a particular capacity: an application should be designed so that a reading process -/// > consumes data as soon as it is available, so that a writing process does not remain blocked. -/// -/// # Example -/// -/// ```no_run -/// # #[cfg(miri)] fn main() {} -/// # #[cfg(not(miri))] -/// # fn main() -> std::io::Result<()> { -/// use std::io::{Read, Write, pipe}; -/// use std::process::Command; -/// let (ping_reader, mut ping_writer) = pipe()?; -/// let (mut pong_reader, pong_writer) = pipe()?; -/// -/// // Spawn a child process that echoes its input. -/// let mut echo_command = Command::new("cat"); -/// echo_command.stdin(ping_reader); -/// echo_command.stdout(pong_writer); -/// let mut echo_child = echo_command.spawn()?; -/// -/// // Send input to the child process. Note that because we're writing all the input before we -/// // read any output, this could deadlock if the child's input and output pipe buffers both -/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer -/// // inputs we'd need to read and write at the same time, e.g. using threads. -/// ping_writer.write_all(b"hello")?; -/// -/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer -/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below. -/// drop(ping_writer); -/// -/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is -/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it. -/// drop(echo_command); -/// -/// let mut buf = String::new(); -/// // Block until `cat` closes its stdout (a pong writer). -/// pong_reader.read_to_string(&mut buf)?; -/// assert_eq!(&buf, "hello"); -/// -/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie". -/// echo_child.wait()?; -/// # Ok(()) -/// # } -/// ``` -/// [changes]: io#platform-specific-behavior -/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[inline] -pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) -} - -/// Read end of an anonymous pipe. -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[derive(Debug)] -pub struct PipeReader(pub(crate) imp::Pipe); - -/// Write end of an anonymous pipe. -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[derive(Debug)] -pub struct PipeWriter(pub(crate) imp::Pipe); - -impl FromInner for PipeReader { - fn from_inner(inner: imp::Pipe) -> Self { - Self(inner) - } -} - -impl IntoInner for PipeReader { - fn into_inner(self) -> imp::Pipe { - self.0 - } -} - -impl FromInner for PipeWriter { - fn from_inner(inner: imp::Pipe) -> Self { - Self(inner) - } -} - -impl IntoInner for PipeWriter { - fn into_inner(self) -> imp::Pipe { - self.0 - } -} - -impl PipeReader { - /// Creates a new [`PipeReader`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// use std::fs; - /// use std::io::{pipe, Write}; - /// use std::process::Command; - /// const NUM_SLOT: u8 = 2; - /// const NUM_PROC: u8 = 5; - /// const OUTPUT: &str = "work.txt"; - /// - /// let mut jobs = vec![]; - /// let (reader, mut writer) = pipe()?; - /// - /// // Write NUM_SLOT characters the pipe. - /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; - /// - /// // Spawn several processes that read a character from the pipe, do some work, then - /// // write back to the pipe. When the pipe is empty, the processes block, so only - /// // NUM_SLOT processes can be working at any given time. - /// for _ in 0..NUM_PROC { - /// jobs.push( - /// Command::new("bash") - /// .args(["-c", - /// &format!( - /// "read -n 1\n\ - /// echo -n 'x' >> '{OUTPUT}'\n\ - /// echo -n '|'", - /// ), - /// ]) - /// .stdin(reader.try_clone()?) - /// .stdout(writer.try_clone()?) - /// .spawn()?, - /// ); - /// } - /// - /// // Wait for all jobs to finish. - /// for mut job in jobs { - /// job.wait()?; - /// } - /// - /// // Check our work and clean up. - /// let xs = fs::read_to_string(OUTPUT)?; - /// fs::remove_file(OUTPUT)?; - /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "anonymous_pipe", since = "1.87.0")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -impl PipeWriter { - /// Creates a new [`PipeWriter`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// use std::process::Command; - /// use std::io::{pipe, Read}; - /// let (mut reader, writer) = pipe()?; - /// - /// // Spawn a process that writes to stdout and stderr. - /// let mut peer = Command::new("bash") - /// .args([ - /// "-c", - /// "echo -n foo\n\ - /// echo -n bar >&2" - /// ]) - /// .stdout(writer.try_clone()?) - /// .stderr(writer) - /// .spawn()?; - /// - /// // Read and check the result. - /// let mut msg = String::new(); - /// reader.read_to_string(&mut msg)?; - /// assert_eq!(&msg, "foobar"); - /// - /// peer.wait()?; - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "anonymous_pipe", since = "1.87.0")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -impl io::Read for &PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -impl io::Read for PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -impl io::Write for &PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -#[stable(feature = "anonymous_pipe", since = "1.87.0")] -impl io::Write for PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/crates/std/src/io/pipe/tests.rs b/crates/std/src/io/pipe/tests.rs deleted file mode 100644 index f113b15..0000000 --- a/crates/std/src/io/pipe/tests.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::io::{Read, Write, pipe}; - -#[test] -#[cfg(all(any(unix, windows), not(miri)))] -fn pipe_creation_clone_and_rw() { - let (rx, tx) = pipe().unwrap(); - - tx.try_clone().unwrap().write_all(b"12345").unwrap(); - drop(tx); - - let mut rx2 = rx.try_clone().unwrap(); - drop(rx); - - let mut s = String::new(); - rx2.read_to_string(&mut s).unwrap(); - drop(rx2); - assert_eq!(s, "12345"); -} diff --git a/crates/std/src/io/prelude.rs b/crates/std/src/io/prelude.rs deleted file mode 100644 index d806431..0000000 --- a/crates/std/src/io/prelude.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! The I/O Prelude. -//! -//! The purpose of this module is to alleviate imports of many common I/O traits -//! by adding a glob import to the top of I/O heavy modules: -//! -//! ``` -//! # #![allow(unused_imports)] -//! use std::io::prelude::*; -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use super::{BufRead, Read, Seek, Write}; diff --git a/crates/std/src/io/stdio.rs b/crates/std/src/io/stdio.rs deleted file mode 100644 index 2d80fe4..0000000 --- a/crates/std/src/io/stdio.rs +++ /dev/null @@ -1,1290 +0,0 @@ -#![cfg_attr(test, allow(unused))] - -#[cfg(test)] -mod tests; - -use crate::cell::{Cell, RefCell}; -use crate::fmt; -use crate::fs::File; -use crate::io::prelude::*; -use crate::io::{ - self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, -}; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; -use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard}; -use crate::sys::stdio; -use crate::thread::AccessError; - -type LocalStream = Arc>>; - -thread_local! { - /// Used by the test crate to capture the output of the print macros and panics. - static OUTPUT_CAPTURE: Cell> = const { - Cell::new(None) - } -} - -/// Flag to indicate OUTPUT_CAPTURE is used. -/// -/// If it is None and was never set on any thread, this flag is set to false, -/// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time -/// and memory registering an unused thread local. -/// -/// Note about memory ordering: This contains information about whether a -/// thread local variable might be in use. Although this is a global flag, the -/// memory ordering between threads does not matter: we only want this flag to -/// have a consistent order between set_output_capture and print_to *within -/// the same thread*. Within the same thread, things always have a perfectly -/// consistent order. So Ordering::Relaxed is fine. -static OUTPUT_CAPTURE_USED: Atomic = AtomicBool::new(false); - -/// A handle to a raw instance of the standard input stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stdin_raw` function. -struct StdinRaw(stdio::Stdin); - -/// A handle to a raw instance of the standard output stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stdout_raw` function. -struct StdoutRaw(stdio::Stdout); - -/// A handle to a raw instance of the standard output stream of this process. -/// -/// This handle is not synchronized or buffered in any fashion. Constructed via -/// the `std::io::stdio::stderr_raw` function. -struct StderrRaw(stdio::Stderr); - -/// Constructs a new raw handle to the standard input of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` -/// handles is **not** available to raw handles returned from this function. -/// -/// The returned handle has no external synchronization or buffering. -#[unstable(feature = "libstd_sys_internals", issue = "none")] -const fn stdin_raw() -> StdinRaw { - StdinRaw(stdio::Stdin::new()) -} - -/// Constructs a new raw handle to the standard output stream of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stdout`. Note that data is buffered by the -/// `std::io::stdout` handles so writes which happen via this raw handle may -/// appear before previous writes. -/// -/// The returned handle has no external synchronization or buffering layered on -/// top. -#[unstable(feature = "libstd_sys_internals", issue = "none")] -const fn stdout_raw() -> StdoutRaw { - StdoutRaw(stdio::Stdout::new()) -} - -/// Constructs a new raw handle to the standard error stream of this process. -/// -/// The returned handle does not interact with any other handles created nor -/// handles returned by `std::io::stderr`. -/// -/// The returned handle has no external synchronization or buffering layered on -/// top. -#[unstable(feature = "libstd_sys_internals", issue = "none")] -const fn stderr_raw() -> StderrRaw { - StderrRaw(stdio::Stderr::new()) -} - -impl Read for StdinRaw { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - handle_ebadf(self.0.read(buf), || Ok(0)) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - handle_ebadf(self.0.read_buf(buf), || Ok(())) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - handle_ebadf(self.0.read_vectored(bufs), || Ok(0)) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if buf.is_empty() { - return Ok(()); - } - handle_ebadf(self.0.read_exact(buf), || Err(io::Error::READ_EXACT_EOF)) - } - - fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - if buf.capacity() == 0 { - return Ok(()); - } - handle_ebadf(self.0.read_buf_exact(buf), || Err(io::Error::READ_EXACT_EOF)) - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - handle_ebadf(self.0.read_to_end(buf), || Ok(0)) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - handle_ebadf(self.0.read_to_string(buf), || Ok(0)) - } -} - -impl Write for StdoutRaw { - fn write(&mut self, buf: &[u8]) -> io::Result { - handle_ebadf(self.0.write(buf), || Ok(buf.len())) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = || Ok(bufs.iter().map(|b| b.len()).sum()); - handle_ebadf(self.0.write_vectored(bufs), total) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - handle_ebadf(self.0.flush(), || Ok(())) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - handle_ebadf(self.0.write_all(buf), || Ok(())) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - handle_ebadf(self.0.write_fmt(fmt), || Ok(())) - } -} - -impl Write for StderrRaw { - fn write(&mut self, buf: &[u8]) -> io::Result { - handle_ebadf(self.0.write(buf), || Ok(buf.len())) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total = || Ok(bufs.iter().map(|b| b.len()).sum()); - handle_ebadf(self.0.write_vectored(bufs), total) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - handle_ebadf(self.0.flush(), || Ok(())) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - handle_ebadf(self.0.write_all(buf), || Ok(())) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - handle_ebadf(self.0.write_fmt(fmt), || Ok(())) - } -} - -fn handle_ebadf(r: io::Result, default: impl FnOnce() -> io::Result) -> io::Result { - match r { - Err(ref e) if stdio::is_ebadf(e) => default(), - r => r, - } -} - -/// A handle to the standard input stream of a process. -/// -/// Each handle is a shared reference to a global buffer of input data to this -/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods -/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect -/// to other reads. -/// -/// This handle implements the `Read` trait, but beware that concurrent reads -/// of `Stdin` must be executed with care. -/// -/// Created by the [`io::stdin`] method. -/// -/// [`io::stdin`]: stdin -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// # Examples -/// -/// ```no_run -/// use std::io; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let stdin = io::stdin(); // We get `Stdin` here. -/// stdin.read_line(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Stdin")] -pub struct Stdin { - inner: &'static Mutex>, -} - -/// A locked reference to the [`Stdin`] handle. -/// -/// This handle implements both the [`Read`] and [`BufRead`] traits, and -/// is constructed via the [`Stdin::lock`] method. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::{self, BufRead}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let stdin = io::stdin(); // We get `Stdin` here. -/// { -/// let mut handle = stdin.lock(); // We get `StdinLock` here. -/// handle.read_line(&mut buffer)?; -/// } // `StdinLock` is dropped here. -/// Ok(()) -/// } -/// ``` -#[must_use = "if unused stdin will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StdinLock<'a> { - inner: MutexGuard<'a, BufReader>, -} - -/// Constructs a new handle to the standard input of the current process. -/// -/// Each handle returned is a reference to a shared global buffer whose access -/// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [`Stdin::lock`] method. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// io::stdin().read_line(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, BufRead}; -/// -/// fn main() -> io::Result<()> { -/// let mut buffer = String::new(); -/// let stdin = io::stdin(); -/// let mut handle = stdin.lock(); -/// -/// handle.read_line(&mut buffer)?; -/// Ok(()) -/// } -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn stdin() -> Stdin { - static INSTANCE: OnceLock>> = OnceLock::new(); - Stdin { - inner: INSTANCE.get_or_init(|| { - Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())) - }), - } -} - -impl Stdin { - /// Locks this handle to the standard input stream, returning a readable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the [`Read`] and [`BufRead`] traits for - /// accessing the underlying data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{self, BufRead}; - /// - /// fn main() -> io::Result<()> { - /// let mut buffer = String::new(); - /// let stdin = io::stdin(); - /// let mut handle = stdin.lock(); - /// - /// handle.read_line(&mut buffer)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdinLock<'static> { - // Locks this handle with 'static lifetime. This depends on the - // implementation detail that the underlying `Mutex` is static. - StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } - } - - /// Locks this handle and reads a line of input, appending it to the specified buffer. - /// - /// For detailed semantics of this method, see the documentation on - /// [`BufRead::read_line`]. In particular: - /// * Previous content of the buffer will be preserved. To avoid appending - /// to the buffer, you need to [`clear`] it first. - /// * The trailing newline character, if any, is included in the buffer. - /// - /// [`clear`]: String::clear - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// - /// let mut input = String::new(); - /// match io::stdin().read_line(&mut input) { - /// Ok(n) => { - /// println!("{n} bytes read"); - /// println!("{input}"); - /// } - /// Err(error) => println!("error: {error}"), - /// } - /// ``` - /// - /// You can run the example one of two ways: - /// - /// - Pipe some text to it, e.g., `printf foo | path/to/executable` - /// - Give it text interactively by running the executable directly, - /// in which case it will wait for the Enter key to be pressed before - /// continuing - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("get_line")] - pub fn read_line(&self, buf: &mut String) -> io::Result { - self.lock().read_line(buf) - } - - /// Consumes this handle and returns an iterator over input lines. - /// - /// For detailed semantics of this method, see the documentation on - /// [`BufRead::lines`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// - /// let lines = io::stdin().lines(); - /// for line in lines { - /// println!("got a line: {}", line.unwrap()); - /// } - /// ``` - #[must_use = "`self` will be dropped if the result is not used"] - #[stable(feature = "stdin_forwarders", since = "1.62.0")] - pub fn lines(self) -> Lines> { - self.lock().lines() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stdin { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Stdin").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.lock().read(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.lock().read_buf(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.lock().read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.lock().is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.lock().read_to_end(buf) - } - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.lock().read_to_string(buf) - } - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.lock().read_exact(buf) - } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.lock().read_buf_exact(cursor) - } -} - -#[stable(feature = "read_shared_stdin", since = "1.78.0")] -impl Read for &Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.lock().read(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.lock().read_buf(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.lock().read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.lock().is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.lock().read_to_end(buf) - } - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.lock().read_to_string(buf) - } - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.lock().read_exact(buf) - } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.lock().read_buf_exact(cursor) - } -} - -// only used by platform-dependent io::copy specializations, i.e. unused on some platforms -#[cfg(any(target_os = "linux", target_os = "android"))] -impl StdinLock<'_> { - pub(crate) fn as_mut_buf(&mut self) -> &mut BufReader { - &mut self.inner - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for StdinLock<'_> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.read_buf(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.inner.read_to_end(buf) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - self.inner.read_to_string(buf) - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.inner.read_exact(buf) - } - - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.read_buf_exact(cursor) - } -} - -impl SpecReadByte for StdinLock<'_> { - #[inline] - fn spec_read_byte(&mut self) -> Option> { - BufReader::spec_read_byte(&mut *self.inner) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for StdinLock<'_> { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - self.inner.fill_buf() - } - - fn consume(&mut self, n: usize) { - self.inner.consume(n) - } - - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - self.inner.read_until(byte, buf) - } - - fn read_line(&mut self, buf: &mut String) -> io::Result { - self.inner.read_line(buf) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StdinLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StdinLock").finish_non_exhaustive() - } -} - -/// A handle to the global standard output stream of the current process. -/// -/// Each handle shares a global buffer of data to be written to the standard -/// output stream. Access is also synchronized via a lock and explicit control -/// over locking is available via the [`lock`] method. -/// -/// By default, the handle is line-buffered when connected to a terminal, meaning -/// it flushes automatically when a newline (`\n`) is encountered. For immediate -/// output, you can manually call the [`flush`] method. When the handle goes out -/// of scope, the buffer is automatically flushed. -/// -/// Created by the [`io::stdout`] method. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// [`lock`]: Stdout::lock -/// [`flush`]: Write::flush -/// [`io::stdout`]: stdout -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Stdout { - // FIXME: this should be LineWriter or BufWriter depending on the state of - // stdout (tty or not). Note that if this is not line buffered it - // should also flush-on-panic or some form of flush-on-abort. - inner: &'static ReentrantLock>>, -} - -/// A locked reference to the [`Stdout`] handle. -/// -/// This handle implements the [`Write`] trait, and is constructed via -/// the [`Stdout::lock`] method. See its documentation for more. -/// -/// By default, the handle is line-buffered when connected to a terminal, meaning -/// it flushes automatically when a newline (`\n`) is encountered. For immediate -/// output, you can manually call the [`flush`] method. When the handle goes out -/// of scope, the buffer is automatically flushed. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// [`flush`]: Write::flush -#[must_use = "if unused stdout will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StdoutLock<'a> { - inner: ReentrantLockGuard<'a, RefCell>>, -} - -static STDOUT: OnceLock>>> = OnceLock::new(); - -/// Constructs a new handle to the standard output of the current process. -/// -/// Each handle returned is a reference to a shared global buffer whose access -/// is synchronized via a mutex. If you need more explicit control over -/// locking, see the [`Stdout::lock`] method. -/// -/// By default, the handle is line-buffered when connected to a terminal, meaning -/// it flushes automatically when a newline (`\n`) is encountered. For immediate -/// output, you can manually call the [`flush`] method. When the handle goes out -/// of scope, the buffer is automatically flushed. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// io::stdout().write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// let stdout = io::stdout(); -/// let mut handle = stdout.lock(); -/// -/// handle.write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// Ensuring output is flushed immediately: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// let mut stdout = io::stdout(); -/// stdout.write_all(b"hello, ")?; -/// stdout.flush()?; // Manual flush -/// stdout.write_all(b"world!\n")?; // Automatically flushed -/// Ok(()) -/// } -/// ``` -/// -/// [`flush`]: Write::flush -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")] -pub fn stdout() -> Stdout { - Stdout { - inner: STDOUT - .get_or_init(|| ReentrantLock::new(RefCell::new(LineWriter::new(stdout_raw())))), - } -} - -// Flush the data and disable buffering during shutdown -// by replacing the line writer by one with zero -// buffering capacity. -pub fn cleanup() { - let mut initialized = false; - let stdout = STDOUT.get_or_init(|| { - initialized = true; - ReentrantLock::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) - }); - - if !initialized { - // The buffer was previously initialized, overwrite it here. - // We use try_lock() instead of lock(), because someone - // might have leaked a StdoutLock, which would - // otherwise cause a deadlock here. - if let Some(lock) = stdout.try_lock() { - *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); - } - } -} - -impl Stdout { - /// Locks this handle to the standard output stream, returning a writable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the `Write` trait for writing data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{self, Write}; - /// - /// fn main() -> io::Result<()> { - /// let mut stdout = io::stdout().lock(); - /// - /// stdout.write_all(b"hello world")?; - /// - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdoutLock<'static> { - // Locks this handle with 'static lifetime. This depends on the - // implementation detail that the underlying `ReentrantMutex` is - // static. - StdoutLock { inner: self.inner.lock() } - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for Stdout {} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl RefUnwindSafe for Stdout {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stdout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Stdout").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - (&*self).write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (&*self).write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) - } - fn flush(&mut self) -> io::Result<()> { - (&*self).flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (&*self).write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (&*self).write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - (&*self).write_fmt(args) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.lock().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.lock().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.lock().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.lock().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.lock().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.lock().write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - self.lock().write_fmt(args) - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for StdoutLock<'_> {} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl RefUnwindSafe for StdoutLock<'_> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for StdoutLock<'_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.borrow_mut().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.borrow_mut().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.borrow_mut().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.inner.borrow_mut().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.inner.borrow_mut().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.inner.borrow_mut().write_all_vectored(bufs) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StdoutLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StdoutLock").finish_non_exhaustive() - } -} - -/// A handle to the standard error stream of a process. -/// -/// For more information, see the [`io::stderr`] method. -/// -/// [`io::stderr`]: stderr -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Stderr { - inner: &'static ReentrantLock>, -} - -/// A locked reference to the [`Stderr`] handle. -/// -/// This handle implements the [`Write`] trait and is constructed via -/// the [`Stderr::lock`] method. See its documentation for more. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -#[must_use = "if unused stderr will immediately unlock"] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct StderrLock<'a> { - inner: ReentrantLockGuard<'a, RefCell>, -} - -/// Constructs a new handle to the standard error of the current process. -/// -/// This handle is not buffered. -/// -/// ### Note: Windows Portability Considerations -/// -/// When operating in a console, the Windows implementation of this stream does not support -/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -/// an error. -/// -/// In a process with a detached console, such as one using -/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -/// the contained handle will be null. In such cases, the standard library's `Read` and -/// `Write` will do nothing and silently succeed. All other I/O operations, via the -/// standard library or via raw Windows API calls, will fail. -/// -/// # Examples -/// -/// Using implicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// io::stderr().write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -/// -/// Using explicit synchronization: -/// -/// ```no_run -/// use std::io::{self, Write}; -/// -/// fn main() -> io::Result<()> { -/// let stderr = io::stderr(); -/// let mut handle = stderr.lock(); -/// -/// handle.write_all(b"hello world")?; -/// -/// Ok(()) -/// } -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "io_stderr")] -pub fn stderr() -> Stderr { - // Note that unlike `stdout()` we don't use `at_exit` here to register a - // destructor. Stderr is not buffered, so there's no need to run a - // destructor for flushing the buffer - static INSTANCE: ReentrantLock> = - ReentrantLock::new(RefCell::new(stderr_raw())); - - Stderr { inner: &INSTANCE } -} - -impl Stderr { - /// Locks this handle to the standard error stream, returning a writable - /// guard. - /// - /// The lock is released when the returned lock goes out of scope. The - /// returned guard also implements the [`Write`] trait for writing data. - /// - /// # Examples - /// - /// ``` - /// use std::io::{self, Write}; - /// - /// fn foo() -> io::Result<()> { - /// let stderr = io::stderr(); - /// let mut handle = stderr.lock(); - /// - /// handle.write_all(b"hello world")?; - /// - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StderrLock<'static> { - // Locks this handle with 'static lifetime. This depends on the - // implementation detail that the underlying `ReentrantMutex` is - // static. - StderrLock { inner: self.inner.lock() } - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for Stderr {} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl RefUnwindSafe for Stderr {} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Stderr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Stderr").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - (&*self).write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (&*self).write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - io::Write::is_write_vectored(&&*self) - } - fn flush(&mut self) -> io::Result<()> { - (&*self).flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (&*self).write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (&*self).write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - (&*self).write_fmt(args) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.lock().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.lock().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.lock().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.lock().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.lock().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.lock().write_all_vectored(bufs) - } - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { - self.lock().write_fmt(args) - } -} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl UnwindSafe for StderrLock<'_> {} - -#[stable(feature = "catch_unwind", since = "1.9.0")] -impl RefUnwindSafe for StderrLock<'_> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for StderrLock<'_> { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.borrow_mut().write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.borrow_mut().write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.inner.borrow_mut().is_write_vectored() - } - fn flush(&mut self) -> io::Result<()> { - self.inner.borrow_mut().flush() - } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.inner.borrow_mut().write_all(buf) - } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.inner.borrow_mut().write_all_vectored(bufs) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for StderrLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StderrLock").finish_non_exhaustive() - } -} - -/// Sets the thread-local output capture buffer and returns the old one. -#[unstable( - feature = "internal_output_capture", - reason = "this function is meant for use in the test crate \ - and may disappear in the future", - issue = "none" -)] -#[doc(hidden)] -pub fn set_output_capture(sink: Option) -> Option { - try_set_output_capture(sink).expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) -} - -/// Tries to set the thread-local output capture buffer and returns the old one. -/// This may fail once thread-local destructors are called. It's used in panic -/// handling instead of `set_output_capture`. -#[unstable( - feature = "internal_output_capture", - reason = "this function is meant for use in the test crate \ - and may disappear in the future", - issue = "none" -)] -#[doc(hidden)] -pub fn try_set_output_capture( - sink: Option, -) -> Result, AccessError> { - if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) { - // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false. - return Ok(None); - } - OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed); - OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink)) -} - -/// Writes `args` to the capture buffer if enabled and possible, or `global_s` -/// otherwise. `label` identifies the stream in a panic message. -/// -/// This function is used to print error messages, so it takes extra -/// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable. -/// For instance, if the TLS key for output capturing is already destroyed, or -/// if the local stream is in use by another thread, it will just fall back to -/// the global stream. -/// -/// However, if the actual I/O causes an error, this function does panic. -/// -/// Writing to non-blocking stdout/stderr can cause an error, which will lead -/// this function to panic. -fn print_to(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str) -where - T: Write, -{ - if print_to_buffer_if_capture_used(args) { - // Successfully wrote to capture buffer. - return; - } - - if let Err(e) = global_s().write_fmt(args) { - panic!("failed printing to {label}: {e}"); - } -} - -fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool { - OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) - && OUTPUT_CAPTURE.try_with(|s| { - // Note that we completely remove a local sink to write to in case - // our printing recursively panics/prints, so the recursive - // panic/print goes to the global sink instead of our local sink. - s.take().map(|w| { - let _ = w.lock().unwrap_or_else(|e| e.into_inner()).write_fmt(args); - s.set(Some(w)); - }) - }) == Ok(Some(())) -} - -/// Used by impl Termination for Result to print error after `main` or a test -/// has returned. Should avoid panicking, although we can't help it if one of -/// the Display impls inside args decides to. -pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) { - if print_to_buffer_if_capture_used(args) { - return; - } - - // Ignore error if the write fails, for example because stderr is already - // closed. There is not much point panicking at this point. - let _ = stderr().write_fmt(args); -} - -/// Trait to determine if a descriptor/handle refers to a terminal/tty. -#[stable(feature = "is_terminal", since = "1.70.0")] -pub trait IsTerminal: crate::sealed::Sealed { - /// Returns `true` if the descriptor/handle refers to a terminal/tty. - /// - /// On platforms where Rust does not know how to detect a terminal yet, this will return - /// `false`. This will also return `false` if an unexpected error occurred, such as from - /// passing an invalid file descriptor. - /// - /// # Platform-specific behavior - /// - /// On Windows, in addition to detecting consoles, this currently uses some heuristics to - /// detect older msys/cygwin/mingw pseudo-terminals based on device name: devices with names - /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. - /// Note that this [may change in the future][changes]. - /// - /// # Examples - /// - /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: - /// - /// ```no_run - /// use std::io::{self, IsTerminal, Write}; - /// - /// fn main() -> io::Result<()> { - /// let stdin = io::stdin(); - /// - /// // Indicate that the user is prompted for input, if this is a terminal. - /// if stdin.is_terminal() { - /// print!("> "); - /// io::stdout().flush()?; - /// } - /// - /// let mut name = String::new(); - /// let _ = stdin.read_line(&mut name)?; - /// - /// println!("Hello {}", name.trim_end()); - /// - /// Ok(()) - /// } - /// ``` - /// - /// The example can be run in two ways: - /// - /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` - /// it will print: `Hello foo`. - /// - If you instead run the example interactively by running `path/to/executable` directly, it will - /// prompt for input. - /// - /// [changes]: io#platform-specific-behavior - /// [`Stdin`]: crate::io::Stdin - #[doc(alias = "isatty")] - #[stable(feature = "is_terminal", since = "1.70.0")] - fn is_terminal(&self) -> bool; -} - -macro_rules! impl_is_terminal { - ($($t:ty),*$(,)?) => {$( - #[unstable(feature = "sealed", issue = "none")] - impl crate::sealed::Sealed for $t {} - - #[stable(feature = "is_terminal", since = "1.70.0")] - impl IsTerminal for $t { - #[inline] - fn is_terminal(&self) -> bool { - crate::sys::io::is_terminal(self) - } - } - )*} -} - -impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); - -#[unstable( - feature = "print_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -#[doc(hidden)] -#[cfg(not(test))] -pub fn _print(args: fmt::Arguments<'_>) { - print_to(args, stdout, "stdout"); -} - -#[unstable( - feature = "print_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -#[doc(hidden)] -#[cfg(not(test))] -pub fn _eprint(args: fmt::Arguments<'_>) { - print_to(args, stderr, "stderr"); -} - -#[cfg(test)] -pub use realstd::io::{_eprint, _print}; diff --git a/crates/std/src/io/stdio/tests.rs b/crates/std/src/io/stdio/tests.rs deleted file mode 100644 index e68d8c2..0000000 --- a/crates/std/src/io/stdio/tests.rs +++ /dev/null @@ -1,166 +0,0 @@ -use super::*; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::mpsc::sync_channel; -use crate::thread; - -#[test] -fn stdout_unwind_safe() { - assert_unwind_safe::(); -} -#[test] -fn stdoutlock_unwind_safe() { - assert_unwind_safe::>(); - assert_unwind_safe::>(); -} -#[test] -fn stderr_unwind_safe() { - assert_unwind_safe::(); -} -#[test] -fn stderrlock_unwind_safe() { - assert_unwind_safe::>(); - assert_unwind_safe::>(); -} - -fn assert_unwind_safe() {} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn panic_doesnt_poison() { - thread::spawn(|| { - let _a = stdin(); - let _a = _a.lock(); - let _a = stdout(); - let _a = _a.lock(); - let _a = stderr(); - let _a = _a.lock(); - panic!(); - }) - .join() - .unwrap_err(); - - let _a = stdin(); - let _a = _a.lock(); - let _a = stdout(); - let _a = _a.lock(); - let _a = stderr(); - let _a = _a.lock(); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn test_lock_stderr() { - test_lock(stderr, || stderr().lock()); -} -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn test_lock_stdin() { - test_lock(stdin, || stdin().lock()); -} -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn test_lock_stdout() { - test_lock(stdout, || stdout().lock()); -} - -// Helper trait to make lock testing function generic. -trait Stdio<'a>: 'static -where - Self::Lock: 'a, -{ - type Lock; - fn lock(&'a self) -> Self::Lock; -} -impl<'a> Stdio<'a> for Stderr { - type Lock = StderrLock<'a>; - fn lock(&'a self) -> StderrLock<'a> { - self.lock() - } -} -impl<'a> Stdio<'a> for Stdin { - type Lock = StdinLock<'a>; - fn lock(&'a self) -> StdinLock<'a> { - self.lock() - } -} -impl<'a> Stdio<'a> for Stdout { - type Lock = StdoutLock<'a>; - fn lock(&'a self) -> StdoutLock<'a> { - self.lock() - } -} - -// Helper trait to make lock testing function generic. -trait StdioOwnedLock: 'static {} -impl StdioOwnedLock for StderrLock<'static> {} -impl StdioOwnedLock for StdinLock<'static> {} -impl StdioOwnedLock for StdoutLock<'static> {} - -// Tests locking on stdio handles by starting two threads and checking that -// they block each other appropriately. -fn test_lock(get_handle: fn() -> T, get_locked: fn() -> U) -where - T: for<'a> Stdio<'a>, - U: StdioOwnedLock, -{ - // State enum to track different phases of the test, primarily when - // each lock is acquired and released. - #[derive(Debug, PartialEq)] - enum State { - Start1, - Acquire1, - Start2, - Release1, - Acquire2, - Release2, - } - use State::*; - // Logging vector to be checked to make sure lock acquisitions and - // releases happened in the correct order. - let log = Arc::new(Mutex::new(Vec::new())); - let ((tx1, rx1), (tx2, rx2)) = (sync_channel(0), sync_channel(0)); - let th1 = { - let (log, tx) = (Arc::clone(&log), tx1); - thread::spawn(move || { - log.lock().unwrap().push(Start1); - let handle = get_handle(); - { - let locked = handle.lock(); - log.lock().unwrap().push(Acquire1); - tx.send(Acquire1).unwrap(); // notify of acquisition - tx.send(Release1).unwrap(); // wait for release command - log.lock().unwrap().push(Release1); - } - tx.send(Acquire1).unwrap(); // wait for th2 acquire - { - let locked = handle.lock(); - log.lock().unwrap().push(Acquire1); - } - log.lock().unwrap().push(Release1); - }) - }; - let th2 = { - let (log, tx) = (Arc::clone(&log), tx2); - thread::spawn(move || { - tx.send(Start2).unwrap(); // wait for start command - let locked = get_locked(); - log.lock().unwrap().push(Acquire2); - tx.send(Acquire2).unwrap(); // notify of acquisition - tx.send(Release2).unwrap(); // wait for release command - log.lock().unwrap().push(Release2); - }) - }; - assert_eq!(rx1.recv().unwrap(), Acquire1); // wait for th1 acquire - log.lock().unwrap().push(Start2); - assert_eq!(rx2.recv().unwrap(), Start2); // block th2 - assert_eq!(rx1.recv().unwrap(), Release1); // release th1 - assert_eq!(rx2.recv().unwrap(), Acquire2); // wait for th2 acquire - assert_eq!(rx1.recv().unwrap(), Acquire1); // block th1 - assert_eq!(rx2.recv().unwrap(), Release2); // release th2 - th2.join().unwrap(); - th1.join().unwrap(); - assert_eq!( - *log.lock().unwrap(), - [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] - ); -} diff --git a/crates/std/src/io/tests.rs b/crates/std/src/io/tests.rs deleted file mode 100644 index b22988d..0000000 --- a/crates/std/src/io/tests.rs +++ /dev/null @@ -1,947 +0,0 @@ -use super::{BorrowedBuf, Cursor, SeekFrom, repeat}; -use crate::cmp::{self, min}; -use crate::io::{ - self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write, -}; -use crate::mem::MaybeUninit; -use crate::ops::Deref; - -#[test] -fn read_until() { - let mut buf = Cursor::new(&b"12"[..]); - let mut v = Vec::new(); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2); - assert_eq!(v, b"12"); - - let mut buf = Cursor::new(&b"1233"[..]); - let mut v = Vec::new(); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3); - assert_eq!(v, b"123"); - v.truncate(0); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1); - assert_eq!(v, b"3"); - v.truncate(0); - assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0); - assert_eq!(v, []); -} - -#[test] -fn skip_until() { - let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0"; - let mut reader = BufReader::new(bytes); - - // read from the bytes, alternating between - // consuming `read\0`s and skipping `ignore\0`s - loop { - // consume `read\0` - let mut out = Vec::new(); - let read = reader.read_until(0, &mut out).unwrap(); - if read == 0 { - // eof - break; - } else { - assert_eq!(out, b"read\0"); - assert_eq!(read, b"read\0".len()); - } - - // skip past `ignore\0` - let skipped = reader.skip_until(0).unwrap(); - assert_eq!(skipped, b"ignore\0".len()); - } - - // ensure we are at the end of the byte slice and that we can skip no further - // also ensure skip_until matches the behavior of read_until at EOF - let skipped = reader.skip_until(0).unwrap(); - assert_eq!(skipped, 0); -} - -#[test] -fn split() { - let buf = Cursor::new(&b"12"[..]); - let mut s = buf.split(b'3'); - assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); - assert!(s.next().is_none()); - - let buf = Cursor::new(&b"1233"[..]); - let mut s = buf.split(b'3'); - assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); - assert_eq!(s.next().unwrap().unwrap(), vec![]); - assert!(s.next().is_none()); -} - -#[test] -fn read_line() { - let mut buf = Cursor::new(&b"12"[..]); - let mut v = String::new(); - assert_eq!(buf.read_line(&mut v).unwrap(), 2); - assert_eq!(v, "12"); - - let mut buf = Cursor::new(&b"12\n\n"[..]); - let mut v = String::new(); - assert_eq!(buf.read_line(&mut v).unwrap(), 3); - assert_eq!(v, "12\n"); - v.truncate(0); - assert_eq!(buf.read_line(&mut v).unwrap(), 1); - assert_eq!(v, "\n"); - v.truncate(0); - assert_eq!(buf.read_line(&mut v).unwrap(), 0); - assert_eq!(v, ""); -} - -#[test] -fn lines() { - let buf = Cursor::new(&b"12\r"[..]); - let mut s = buf.lines(); - assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string()); - assert!(s.next().is_none()); - - let buf = Cursor::new(&b"12\r\n\n"[..]); - let mut s = buf.lines(); - assert_eq!(s.next().unwrap().unwrap(), "12".to_string()); - assert_eq!(s.next().unwrap().unwrap(), "".to_string()); - assert!(s.next().is_none()); -} - -#[test] -fn buf_read_has_data_left() { - let mut buf = Cursor::new(&b"abcd"[..]); - assert!(buf.has_data_left().unwrap()); - buf.read_exact(&mut [0; 2]).unwrap(); - assert!(buf.has_data_left().unwrap()); - buf.read_exact(&mut [0; 2]).unwrap(); - assert!(!buf.has_data_left().unwrap()); -} - -#[test] -fn read_to_end() { - let mut c = Cursor::new(&b""[..]); - let mut v = Vec::new(); - assert_eq!(c.read_to_end(&mut v).unwrap(), 0); - assert_eq!(v, []); - - let mut c = Cursor::new(&b"1"[..]); - let mut v = Vec::new(); - assert_eq!(c.read_to_end(&mut v).unwrap(), 1); - assert_eq!(v, b"1"); - - let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 }; - let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); - let mut v = Vec::new(); - let (a, b) = data.split_at(data.len() / 2); - assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); - assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); - assert_eq!(v, data); -} - -#[test] -fn read_to_string() { - let mut c = Cursor::new(&b""[..]); - let mut v = String::new(); - assert_eq!(c.read_to_string(&mut v).unwrap(), 0); - assert_eq!(v, ""); - - let mut c = Cursor::new(&b"1"[..]); - let mut v = String::new(); - assert_eq!(c.read_to_string(&mut v).unwrap(), 1); - assert_eq!(v, "1"); - - let mut c = Cursor::new(&b"\xff"[..]); - let mut v = String::new(); - assert!(c.read_to_string(&mut v).is_err()); -} - -#[test] -fn read_exact() { - let mut buf = [0; 4]; - - let mut c = Cursor::new(&b""[..]); - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - - let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..])); - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"1234"); - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"5678"); - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); -} - -#[test] -fn read_exact_slice() { - let mut buf = [0; 4]; - - let mut c = &b""[..]; - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - - let mut c = &b"123"[..]; - assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - // make sure the optimized (early returning) method is being used - assert_eq!(&buf, &[0; 4]); - - let mut c = &b"1234"[..]; - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"1234"); - - let mut c = &b"56789"[..]; - c.read_exact(&mut buf).unwrap(); - assert_eq!(&buf, b"5678"); - assert_eq!(c, b"9"); -} - -#[test] -fn read_buf_exact() { - let buf: &mut [_] = &mut [0; 4]; - let mut buf: BorrowedBuf<'_> = buf.into(); - - let mut c = Cursor::new(&b""[..]); - assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - - let mut c = Cursor::new(&b"123456789"[..]); - c.read_buf_exact(buf.unfilled()).unwrap(); - assert_eq!(buf.filled(), b"1234"); - - buf.clear(); - - c.read_buf_exact(buf.unfilled()).unwrap(); - assert_eq!(buf.filled(), b"5678"); - - buf.clear(); - - assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); -} - -#[test] -#[should_panic] -fn borrowed_cursor_advance_overflow() { - let mut buf = [0; 512]; - let mut buf = BorrowedBuf::from(&mut buf[..]); - buf.unfilled().advance(1); - buf.unfilled().advance(usize::MAX); -} - -#[test] -fn take_eof() { - struct R; - - impl Read for R { - fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::const_error!(io::ErrorKind::Other, "")) - } - } - impl BufRead for R { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::const_error!(io::ErrorKind::Other, "")) - } - fn consume(&mut self, _amt: usize) {} - } - - let mut buf = [0; 1]; - assert_eq!(0, R.take(0).read(&mut buf).unwrap()); - assert_eq!(b"", R.take(0).fill_buf().unwrap()); -} - -fn cmp_bufread(mut br1: Br1, mut br2: Br2, exp: &[u8]) { - let mut cat = Vec::new(); - loop { - let consume = { - let buf1 = br1.fill_buf().unwrap(); - let buf2 = br2.fill_buf().unwrap(); - let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() }; - assert_eq!(buf1[..minlen], buf2[..minlen]); - cat.extend_from_slice(&buf1[..minlen]); - minlen - }; - if consume == 0 { - break; - } - br1.consume(consume); - br2.consume(consume); - } - assert_eq!(br1.fill_buf().unwrap().len(), 0); - assert_eq!(br2.fill_buf().unwrap().len(), 0); - assert_eq!(&cat[..], &exp[..]) -} - -#[test] -fn chain_bufread() { - let testdata = b"ABCDEFGHIJKL"; - let chain1 = - (&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]); - let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]); - cmp_bufread(chain1, chain2, &testdata[..]); -} - -#[test] -fn chain_splitted_char() { - let chain = b"\xc3".chain(b"\xa9".as_slice()); - assert_eq!(crate::io::read_to_string(chain).unwrap(), "é"); - - let mut chain = b"\xc3".chain(b"\xa9\n".as_slice()); - let mut buf = String::new(); - assert_eq!(chain.read_line(&mut buf).unwrap(), 3); - assert_eq!(buf, "é\n"); -} - -#[test] -fn bufreader_size_hint() { - let testdata = b"ABCDEFGHIJKL"; - let mut buf_reader = BufReader::new(&testdata[..]); - assert_eq!(buf_reader.buffer().len(), 0); - - let buffer_length = testdata.len(); - buf_reader.fill_buf().unwrap(); - - // Check that size hint matches buffer contents - let mut buffered_bytes = buf_reader.bytes(); - let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); - assert_eq!(lower_bound, buffer_length); - - // Check that size hint matches buffer contents after advancing - buffered_bytes.next().unwrap().unwrap(); - let (lower_bound, _upper_bound) = buffered_bytes.size_hint(); - assert_eq!(lower_bound, buffer_length - 1); -} - -#[test] -fn empty_size_hint() { - let size_hint = io::empty().bytes().size_hint(); - assert_eq!(size_hint, (0, Some(0))); -} - -#[test] -fn slice_size_hint() { - let size_hint = (&[1, 2, 3]).bytes().size_hint(); - assert_eq!(size_hint, (3, Some(3))); -} - -#[test] -fn take_size_hint() { - let size_hint = (&[1, 2, 3]).take(2).bytes().size_hint(); - assert_eq!(size_hint, (2, Some(2))); - - let size_hint = (&[1, 2, 3]).take(4).bytes().size_hint(); - assert_eq!(size_hint, (3, Some(3))); - - let size_hint = io::repeat(0).take(3).bytes().size_hint(); - assert_eq!(size_hint, (3, Some(3))); -} - -#[test] -fn chain_empty_size_hint() { - let chain = io::empty().chain(io::empty()); - let size_hint = chain.bytes().size_hint(); - assert_eq!(size_hint, (0, Some(0))); -} - -#[test] -fn chain_size_hint() { - let testdata = b"ABCDEFGHIJKL"; - let mut buf_reader_1 = BufReader::new(&testdata[..6]); - let mut buf_reader_2 = BufReader::new(&testdata[6..]); - - buf_reader_1.fill_buf().unwrap(); - buf_reader_2.fill_buf().unwrap(); - - let chain = buf_reader_1.chain(buf_reader_2); - let size_hint = chain.bytes().size_hint(); - assert_eq!(size_hint, (testdata.len(), Some(testdata.len()))); -} - -#[test] -fn chain_zero_length_read_is_not_eof() { - let a = b"A"; - let b = b"B"; - let mut s = String::new(); - let mut chain = (&a[..]).chain(&b[..]); - chain.read(&mut []).unwrap(); - chain.read_to_string(&mut s).unwrap(); - assert_eq!("AB", s); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_read_to_end(b: &mut test::Bencher) { - b.iter(|| { - let mut lr = repeat(1).take(10000000); - let mut vec = Vec::with_capacity(1024); - super::default_read_to_end(&mut lr, &mut vec, None) - }); -} - -#[test] -fn seek_len() -> io::Result<()> { - let mut c = Cursor::new(vec![0; 15]); - assert_eq!(c.stream_len()?, 15); - - c.seek(SeekFrom::End(0))?; - let old_pos = c.stream_position()?; - assert_eq!(c.stream_len()?, 15); - assert_eq!(c.stream_position()?, old_pos); - - c.seek(SeekFrom::Start(7))?; - c.seek(SeekFrom::Current(2))?; - let old_pos = c.stream_position()?; - assert_eq!(c.stream_len()?, 15); - assert_eq!(c.stream_position()?, old_pos); - - Ok(()) -} - -#[test] -fn seek_position() -> io::Result<()> { - // All `asserts` are duplicated here to make sure the method does not - // change anything about the seek state. - let mut c = Cursor::new(vec![0; 15]); - assert_eq!(c.stream_position()?, 0); - assert_eq!(c.stream_position()?, 0); - - c.seek(SeekFrom::End(0))?; - assert_eq!(c.stream_position()?, 15); - assert_eq!(c.stream_position()?, 15); - - c.seek(SeekFrom::Start(7))?; - c.seek(SeekFrom::Current(2))?; - assert_eq!(c.stream_position()?, 9); - assert_eq!(c.stream_position()?, 9); - - c.seek(SeekFrom::End(-3))?; - c.seek(SeekFrom::Current(1))?; - c.seek(SeekFrom::Current(-5))?; - assert_eq!(c.stream_position()?, 8); - assert_eq!(c.stream_position()?, 8); - - c.rewind()?; - assert_eq!(c.stream_position()?, 0); - assert_eq!(c.stream_position()?, 0); - - Ok(()) -} - -#[test] -fn take_seek() -> io::Result<()> { - let mut buf = Cursor::new(b"0123456789"); - buf.set_position(2); - let mut take = buf.by_ref().take(4); - let mut buf1 = [0u8; 1]; - let mut buf2 = [0u8; 2]; - assert_eq!(take.position(), 0); - - assert_eq!(take.seek(SeekFrom::Start(0))?, 0); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'2', b'3']); - assert_eq!(take.seek(SeekFrom::Start(1))?, 1); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'3', b'4']); - assert_eq!(take.seek(SeekFrom::Start(2))?, 2); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'4', b'5']); - assert_eq!(take.seek(SeekFrom::Start(3))?, 3); - take.read_exact(&mut buf1)?; - assert_eq!(buf1, [b'5']); - assert_eq!(take.seek(SeekFrom::Start(4))?, 4); - assert_eq!(take.read(&mut buf1)?, 0); - - assert_eq!(take.seek(SeekFrom::End(0))?, 4); - assert_eq!(take.seek(SeekFrom::End(-1))?, 3); - take.read_exact(&mut buf1)?; - assert_eq!(buf1, [b'5']); - assert_eq!(take.seek(SeekFrom::End(-2))?, 2); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'4', b'5']); - assert_eq!(take.seek(SeekFrom::End(-3))?, 1); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'3', b'4']); - assert_eq!(take.seek(SeekFrom::End(-4))?, 0); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'2', b'3']); - - assert_eq!(take.seek(SeekFrom::Current(0))?, 2); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'4', b'5']); - - assert_eq!(take.seek(SeekFrom::Current(-3))?, 1); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'3', b'4']); - - assert_eq!(take.seek(SeekFrom::Current(-1))?, 2); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'4', b'5']); - - assert_eq!(take.seek(SeekFrom::Current(-4))?, 0); - take.read_exact(&mut buf2)?; - assert_eq!(buf2, [b'2', b'3']); - - assert_eq!(take.seek(SeekFrom::Current(2))?, 4); - assert_eq!(take.read(&mut buf1)?, 0); - - Ok(()) -} - -#[test] -fn take_seek_error() { - let buf = Cursor::new(b"0123456789"); - let mut take = buf.take(2); - assert!(take.seek(SeekFrom::Start(3)).is_err()); - assert!(take.seek(SeekFrom::End(1)).is_err()); - assert!(take.seek(SeekFrom::End(-3)).is_err()); - assert!(take.seek(SeekFrom::Current(-1)).is_err()); - assert!(take.seek(SeekFrom::Current(3)).is_err()); -} - -struct ExampleHugeRangeOfZeroes { - position: u64, -} - -impl Read for ExampleHugeRangeOfZeroes { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let max = buf.len().min(usize::MAX); - for i in 0..max { - if self.position == u64::MAX { - return Ok(i); - } - self.position += 1; - buf[i] = 0; - } - Ok(max) - } -} - -impl Seek for ExampleHugeRangeOfZeroes { - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - match pos { - io::SeekFrom::Start(i) => self.position = i, - io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX, - io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(), - io::SeekFrom::Current(i) => { - self.position = if i >= 0 { - self.position.saturating_add(i.unsigned_abs()) - } else { - self.position.saturating_sub(i.unsigned_abs()) - }; - } - } - Ok(self.position) - } -} - -#[test] -fn take_seek_big_offsets() -> io::Result<()> { - let inner = ExampleHugeRangeOfZeroes { position: 1 }; - let mut take = inner.take(u64::MAX - 2); - assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2); - assert_eq!(take.inner.position, u64::MAX - 1); - assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0); - assert_eq!(take.inner.position, 1); - assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3); - assert_eq!(take.inner.position, u64::MAX - 2); - Ok(()) -} - -// A simple example reader which uses the default implementation of -// read_to_end. -struct ExampleSliceReader<'a> { - slice: &'a [u8], -} - -impl<'a> Read for ExampleSliceReader<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = cmp::min(self.slice.len(), buf.len()); - buf[..len].copy_from_slice(&self.slice[..len]); - self.slice = &self.slice[len..]; - Ok(len) - } -} - -#[test] -fn test_read_to_end_capacity() -> io::Result<()> { - let input = &b"foo"[..]; - - // read_to_end() takes care not to over-allocate when a buffer is the - // exact size needed. - let mut vec1 = Vec::with_capacity(input.len()); - ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?; - assert_eq!(vec1.len(), input.len()); - assert_eq!(vec1.capacity(), input.len(), "did not allocate more"); - - Ok(()) -} - -#[test] -fn io_slice_mut_advance_slices() { - let mut buf1 = [1; 8]; - let mut buf2 = [2; 16]; - let mut buf3 = [3; 8]; - let mut bufs = &mut [ - IoSliceMut::new(&mut buf1), - IoSliceMut::new(&mut buf2), - IoSliceMut::new(&mut buf3), - ][..]; - - // Only in a single buffer.. - IoSliceMut::advance_slices(&mut bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - IoSliceMut::advance_slices(&mut bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - IoSliceMut::advance_slices(&mut bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); -} - -#[test] -#[should_panic] -fn io_slice_mut_advance_slices_empty_slice() { - let mut empty_bufs = &mut [][..]; - IoSliceMut::advance_slices(&mut empty_bufs, 1); -} - -#[test] -#[should_panic] -fn io_slice_mut_advance_slices_beyond_total_length() { - let mut buf1 = [1; 8]; - let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; - - IoSliceMut::advance_slices(&mut bufs, 9); - assert!(bufs.is_empty()); -} - -#[test] -fn io_slice_advance_slices() { - let buf1 = [1; 8]; - let buf2 = [2; 16]; - let buf3 = [3; 8]; - let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..]; - - // Only in a single buffer.. - IoSlice::advance_slices(&mut bufs, 1); - assert_eq!(bufs[0].deref(), [1; 7].as_ref()); - assert_eq!(bufs[1].deref(), [2; 16].as_ref()); - assert_eq!(bufs[2].deref(), [3; 8].as_ref()); - - // Removing a buffer, leaving others as is. - IoSlice::advance_slices(&mut bufs, 7); - assert_eq!(bufs[0].deref(), [2; 16].as_ref()); - assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - - // Removing a buffer and removing from the next buffer. - IoSlice::advance_slices(&mut bufs, 18); - assert_eq!(bufs[0].deref(), [3; 6].as_ref()); -} - -#[test] -#[should_panic] -fn io_slice_advance_slices_empty_slice() { - let mut empty_bufs = &mut [][..]; - IoSlice::advance_slices(&mut empty_bufs, 1); -} - -#[test] -#[should_panic] -fn io_slice_advance_slices_beyond_total_length() { - let buf1 = [1; 8]; - let mut bufs = &mut [IoSlice::new(&buf1)][..]; - - IoSlice::advance_slices(&mut bufs, 9); - assert!(bufs.is_empty()); -} - -#[test] -fn io_slice_as_slice() { - let buf = [1; 8]; - let slice = IoSlice::new(&buf).as_slice(); - assert_eq!(slice, buf); -} - -#[test] -fn io_slice_into_slice() { - let mut buf = [1; 8]; - let slice = IoSliceMut::new(&mut buf).into_slice(); - assert_eq!(slice, [1; 8]); -} - -/// Creates a new writer that reads from at most `n_bufs` and reads -/// `per_call` bytes (in total) per call to write. -fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { - TestWriter { n_bufs, per_call, written: Vec::new() } -} - -struct TestWriter { - n_bufs: usize, - per_call: usize, - written: Vec, -} - -impl Write for TestWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut left = self.per_call; - let mut written = 0; - for buf in bufs.iter().take(self.n_bufs) { - let n = min(left, buf.len()); - self.written.extend_from_slice(&buf[0..n]); - left -= n; - written += n; - } - Ok(written) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[test] -fn test_writer_read_from_one_buf() { - let mut writer = test_writer(1, 2); - - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - - // Read at most 2 bytes. - assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2); - let bufs = &[IoSlice::new(&[2, 2, 2])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 2); - - // Only read from first buf. - let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 1); - - assert_eq!(writer.written, &[1, 1, 2, 2, 3]); -} - -#[test] -fn test_writer_read_from_multiple_bufs() { - let mut writer = test_writer(3, 3); - - // Read at most 3 bytes from two buffers. - let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 3); - - // Read at most 3 bytes from three buffers. - let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])]; - assert_eq!(writer.write_vectored(bufs).unwrap(), 3); - - assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]); -} - -#[test] -fn test_write_all_vectored() { - #[rustfmt::skip] // Becomes unreadable otherwise. - let tests: Vec<(_, &'static [u8])> = vec![ - (vec![], &[]), - (vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]), - (vec![IoSlice::new(&[1])], &[1]), - (vec![IoSlice::new(&[1, 2])], &[1, 2]), - (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]), - (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]), - (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]), - (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]), - (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]), - (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]), - (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]), - ]; - - let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]; - - for (n_bufs, per_call) in writer_configs.iter().copied() { - for (mut input, wanted) in tests.clone().into_iter() { - let mut writer = test_writer(n_bufs, per_call); - assert!(writer.write_all_vectored(&mut *input).is_ok()); - assert_eq!(&*writer.written, &*wanted); - } - } -} - -// Issue 94981 -#[test] -#[should_panic = "number of read bytes exceeds limit"] -fn test_take_wrong_length() { - struct LieAboutSize(bool); - - impl Read for LieAboutSize { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // Lie about the read size at first time of read. - if core::mem::take(&mut self.0) { Ok(buf.len() + 1) } else { Ok(buf.len()) } - } - } - - let mut buffer = vec![0; 4]; - let mut reader = LieAboutSize(true).take(4); - // Primed the `Limit` by lying about the read size. - let _ = reader.read(&mut buffer[..]); -} - -#[test] -fn slice_read_exact_eof() { - let slice = &b"123456"[..]; - - let mut r = slice; - assert!(r.read_exact(&mut [0; 10]).is_err()); - assert!(r.is_empty()); - - let mut r = slice; - let buf = &mut [0; 10]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - assert!(r.read_buf_exact(buf.unfilled()).is_err()); - assert!(r.is_empty()); - assert_eq!(buf.filled(), b"123456"); -} - -#[test] -fn cursor_read_exact_eof() { - let slice = Cursor::new(b"123456"); - - let mut r = slice.clone(); - assert!(r.read_exact(&mut [0; 10]).is_err()); - assert!(Cursor::split(&r).1.is_empty()); - - let mut r = slice; - let buf = &mut [0; 10]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - assert!(r.read_buf_exact(buf.unfilled()).is_err()); - assert!(Cursor::split(&r).1.is_empty()); - assert_eq!(buf.filled(), b"123456"); -} - -#[bench] -fn bench_take_read(b: &mut test::Bencher) { - b.iter(|| { - let mut buf = [0; 64]; - - [255; 128].take(64).read(&mut buf).unwrap(); - }); -} - -#[bench] -fn bench_take_read_buf(b: &mut test::Bencher) { - b.iter(|| { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64]; - - let mut buf: BorrowedBuf<'_> = buf.into(); - - [255; 128].take(64).read_buf(buf.unfilled()).unwrap(); - }); -} - -// Issue #120603 -#[test] -#[should_panic] -fn read_buf_broken_read() { - struct MalformedRead; - - impl Read for MalformedRead { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // broken length calculation - Ok(buf.len() + 1) - } - } - - let _ = BufReader::new(MalformedRead).fill_buf(); -} - -#[test] -fn read_buf_full_read() { - struct FullRead; - - impl Read for FullRead { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - Ok(buf.len()) - } - } - - assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE); -} - -struct DataAndErrorReader(&'static [u8]); - -impl Read for DataAndErrorReader { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - panic!("We want tests to use `read_buf`") - } - - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf).unwrap(); - Err(io::Error::other("error")) - } -} - -#[test] -fn read_buf_data_and_error_take() { - let mut buf = [0; 64]; - let mut buf = io::BorrowedBuf::from(buf.as_mut_slice()); - - let mut r = DataAndErrorReader(&[4, 5, 6]).take(1); - assert!(r.read_buf(buf.unfilled()).is_err()); - assert_eq!(buf.filled(), &[4]); - - assert!(r.read_buf(buf.unfilled()).is_ok()); - assert_eq!(buf.filled(), &[4]); - assert_eq!(r.get_ref().0, &[5, 6]); -} - -#[test] -fn read_buf_data_and_error_buf() { - let mut r = BufReader::new(DataAndErrorReader(&[4, 5, 6])); - - assert!(r.fill_buf().is_err()); - assert_eq!(r.fill_buf().unwrap(), &[4, 5, 6]); -} - -#[test] -fn read_buf_data_and_error_read_to_end() { - let mut r = DataAndErrorReader(&[4, 5, 6]); - - let mut v = Vec::with_capacity(200); - assert!(r.read_to_end(&mut v).is_err()); - - assert_eq!(v, &[4, 5, 6]); -} - -#[test] -fn read_to_end_error() { - struct ErrorReader; - - impl Read for ErrorReader { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Err(io::Error::other("error")) - } - } - - let mut r = [4, 5, 6].chain(ErrorReader); - - let mut v = Vec::with_capacity(200); - assert!(r.read_to_end(&mut v).is_err()); - - assert_eq!(v, &[4, 5, 6]); -} - -#[test] -fn try_oom_error() { - use alloc::alloc::Layout; - use alloc::collections::{TryReserveError, TryReserveErrorKind}; - - // We simulate a `Vec::try_reserve` error rather than attempting a huge size for real. This way - // we're not subject to the whims of optimization that might skip the actual allocation, and it - // also works for 32-bit targets and miri that might not OOM at all. - let layout = Layout::new::(); - let kind = TryReserveErrorKind::AllocError { layout, non_exhaustive: () }; - let reserve_err = TryReserveError::from(kind); - - let io_err = io::Error::from(reserve_err); - assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind()); -} diff --git a/crates/std/src/io/util.rs b/crates/std/src/io/util.rs deleted file mode 100644 index 0410df3..0000000 --- a/crates/std/src/io/util.rs +++ /dev/null @@ -1,448 +0,0 @@ -#![allow(missing_copy_implementations)] - -#[cfg(test)] -mod tests; - -use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, -}; - -/// `Empty` ignores any data written via [`Write`], and will always be empty -/// (returning zero bytes) when read via [`Read`]. -/// -/// This struct is generally created by calling [`empty()`]. Please -/// see the documentation of [`empty()`] for more details. -#[stable(feature = "rust1", since = "1.0.0")] -#[non_exhaustive] -#[derive(Copy, Clone, Debug, Default)] -pub struct Empty; - -/// Creates a value that is always at EOF for reads, and ignores all data written. -/// -/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] -/// and the contents of the buffer will not be inspected. -/// -/// All calls to [`read`] from the returned reader will return [`Ok(0)`]. -/// -/// [`Ok(buf.len())`]: Ok -/// [`Ok(0)`]: Ok -/// -/// [`write`]: Write::write -/// [`read`]: Read::read -/// -/// # Examples -/// -/// ```rust -/// use std::io::{self, Write}; -/// -/// let buffer = vec![1, 2, 3, 5, 8]; -/// let num_bytes = io::empty().write(&buffer).unwrap(); -/// assert_eq!(num_bytes, 5); -/// ``` -/// -/// -/// ```rust -/// use std::io::{self, Read}; -/// -/// let mut buffer = String::new(); -/// io::empty().read_to_string(&mut buffer).unwrap(); -/// assert!(buffer.is_empty()); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] -pub const fn empty() -> Empty { - Empty -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Empty { - #[inline] - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Ok(0) - } - - #[inline] - fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - // Do not force `Chain` or `Chain` to use vectored - // reads, unless the other reader is vectored. - false - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } - } - - #[inline] - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } - } - - #[inline] - fn read_to_end(&mut self, _buf: &mut Vec) -> io::Result { - Ok(0) - } - - #[inline] - fn read_to_string(&mut self, _buf: &mut String) -> io::Result { - Ok(0) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for Empty { - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(&[]) - } - - #[inline] - fn consume(&mut self, _n: usize) {} - - #[inline] - fn has_data_left(&mut self) -> io::Result { - Ok(false) - } - - #[inline] - fn read_until(&mut self, _byte: u8, _buf: &mut Vec) -> io::Result { - Ok(0) - } - - #[inline] - fn skip_until(&mut self, _byte: u8) -> io::Result { - Ok(0) - } - - #[inline] - fn read_line(&mut self, _buf: &mut String) -> io::Result { - Ok(0) - } -} - -#[stable(feature = "empty_seek", since = "1.51.0")] -impl Seek for Empty { - #[inline] - fn seek(&mut self, _pos: SeekFrom) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - Ok(0) - } -} - -impl SizeHint for Empty { - #[inline] - fn upper_bound(&self) -> Option { - Some(0) - } -} - -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for &Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// A reader which yields one byte over and over and over and over and over and... -/// -/// This struct is generally created by calling [`repeat()`]. Please -/// see the documentation of [`repeat()`] for more details. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Repeat { - byte: u8, -} - -/// Creates an instance of a reader that infinitely repeats one byte. -/// -/// All reads from this reader will succeed by filling the specified buffer with -/// the given byte. -/// -/// # Examples -/// -/// ``` -/// use std::io::{self, Read}; -/// -/// let mut buffer = [0; 3]; -/// io::repeat(0b101).read_exact(&mut buffer).unwrap(); -/// assert_eq!(buffer, [0b101, 0b101, 0b101]); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] -pub const fn repeat(byte: u8) -> Repeat { - Repeat { byte } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for Repeat { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - buf.fill(self.byte); - Ok(buf.len()) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - buf.fill(self.byte); - Ok(()) - } - - #[inline] - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - // SAFETY: No uninit bytes are being written. - unsafe { buf.as_mut() }.write_filled(self.byte); - // SAFETY: the entire unfilled portion of buf has been initialized. - unsafe { buf.advance_unchecked(buf.capacity()) }; - Ok(()) - } - - #[inline] - fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.read_buf(buf) - } - - /// This function is not supported by `io::Repeat`, because there's no end of its data - fn read_to_end(&mut self, _: &mut Vec) -> io::Result { - Err(io::Error::from(io::ErrorKind::OutOfMemory)) - } - - /// This function is not supported by `io::Repeat`, because there's no end of its data - fn read_to_string(&mut self, _: &mut String) -> io::Result { - Err(io::Error::from(io::ErrorKind::OutOfMemory)) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.read(buf)?; - } - Ok(nwritten) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl SizeHint for Repeat { - #[inline] - fn lower_bound(&self) -> usize { - usize::MAX - } - - #[inline] - fn upper_bound(&self) -> Option { - None - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Repeat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Repeat").finish_non_exhaustive() - } -} - -/// A writer which will move data into the void. -/// -/// This struct is generally created by calling [`sink()`]. Please -/// see the documentation of [`sink()`] for more details. -#[stable(feature = "rust1", since = "1.0.0")] -#[non_exhaustive] -#[derive(Copy, Clone, Debug, Default)] -pub struct Sink; - -/// Creates an instance of a writer which will successfully consume all data. -/// -/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] -/// and the contents of the buffer will not be inspected. -/// -/// [`write`]: Write::write -/// [`Ok(buf.len())`]: Ok -/// -/// # Examples -/// -/// ```rust -/// use std::io::{self, Write}; -/// -/// let buffer = vec![1, 2, 3, 5, 8]; -/// let num_bytes = io::sink().write(&buffer).unwrap(); -/// assert_eq!(num_bytes, 5); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] -pub const fn sink() -> Sink { - Sink -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/crates/std/src/io/util/tests.rs b/crates/std/src/io/util/tests.rs deleted file mode 100644 index d0f106d..0000000 --- a/crates/std/src/io/util/tests.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::fmt; -use crate::io::prelude::*; -use crate::io::{ - BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink, -}; -use crate::mem::MaybeUninit; - -struct ErrorDisplay; - -impl fmt::Display for ErrorDisplay { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Err(fmt::Error) - } -} - -struct PanicDisplay; - -impl fmt::Display for PanicDisplay { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - panic!() - } -} - -#[track_caller] -fn test_sinking(mut w: W) { - assert_eq!(w.write(&[]).unwrap(), 0); - assert_eq!(w.write(&[0]).unwrap(), 1); - assert_eq!(w.write(&[0; 1024]).unwrap(), 1024); - w.write_all(&[]).unwrap(); - w.write_all(&[0]).unwrap(); - w.write_all(&[0; 1024]).unwrap(); - let mut bufs = - [IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])]; - assert!(w.is_write_vectored()); - assert_eq!(w.write_vectored(&[]).unwrap(), 0); - assert_eq!(w.write_vectored(&bufs).unwrap(), 1025); - w.write_all_vectored(&mut []).unwrap(); - w.write_all_vectored(&mut bufs).unwrap(); - assert!(w.flush().is_ok()); - assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024); - // Ignores fmt arguments - w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap(); - w.write_fmt(format_args!("{}", PanicDisplay)).unwrap(); -} - -#[test] -fn sink_sinks() { - test_sinking(sink()); -} - -#[test] -fn empty_reads() { - let mut e = empty(); - assert_eq!(e.read(&mut []).unwrap(), 0); - assert_eq!(e.read(&mut [0]).unwrap(), 0); - assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); - assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0); - - e.read_exact(&mut []).unwrap(); - assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof); - assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof); - - assert!(!e.is_read_vectored()); - assert_eq!(e.read_vectored(&mut []).unwrap(), 0); - let (mut buf1, mut buf1024) = ([0], [0; 1024]); - let bufs = &mut [ - IoSliceMut::new(&mut []), - IoSliceMut::new(&mut buf1), - IoSliceMut::new(&mut buf1024), - IoSliceMut::new(&mut []), - ]; - assert_eq!(e.read_vectored(bufs).unwrap(), 0); - - let buf: &mut [MaybeUninit<_>] = &mut []; - let mut buf: BorrowedBuf<'_> = buf.into(); - e.read_buf(buf.unfilled()).unwrap(); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit()]; - let mut buf: BorrowedBuf<'_> = buf.into(); - e.read_buf(buf.unfilled()).unwrap(); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; - let mut buf: BorrowedBuf<'_> = buf.into(); - e.read_buf(buf.unfilled()).unwrap(); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; - let mut buf: BorrowedBuf<'_> = buf.into(); - Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [MaybeUninit<_>] = &mut []; - let mut buf: BorrowedBuf<'_> = buf.into(); - e.read_buf_exact(buf.unfilled()).unwrap(); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit()]; - let mut buf: BorrowedBuf<'_> = buf.into(); - assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; - let mut buf: BorrowedBuf<'_> = buf.into(); - assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; - let mut buf: BorrowedBuf<'_> = buf.into(); - assert_eq!( - Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(), - ErrorKind::UnexpectedEof, - ); - assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); - - let mut buf = Vec::new(); - assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); - assert_eq!(buf, vec![]); - let mut buf = vec![1, 2, 3]; - assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); - assert_eq!(buf, vec![1, 2, 3]); - - let mut buf = String::new(); - assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); - assert_eq!(buf, ""); - let mut buf = "hello".to_owned(); - assert_eq!(e.read_to_string(&mut buf).unwrap(), 0); - assert_eq!(buf, "hello"); -} - -#[test] -fn empty_seeks() { - let mut e = empty(); - assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0))); - - assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0))); - - assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0))); - assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0))); -} - -#[test] -fn empty_sinks() { - test_sinking(empty()); -} - -#[test] -fn repeat_repeats() { - let mut r = repeat(4); - let mut b = [0; 1024]; - assert_eq!(r.read(&mut b).unwrap(), 1024); - assert!(b.iter().all(|b| *b == 4)); -} - -#[test] -fn take_some_bytes() { - assert_eq!(repeat(4).take(100).bytes().count(), 100); - assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); - assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); -} - -#[allow(dead_code)] -fn const_utils() { - const _: Empty = empty(); - const _: Repeat = repeat(b'c'); - const _: Sink = sink(); -} diff --git a/crates/std/src/net/hostname.rs b/crates/std/src/net/hostname.rs deleted file mode 100644 index 4042496..0000000 --- a/crates/std/src/net/hostname.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::ffi::OsString; - -/// Returns the system hostname. -/// -/// This can error out in platform-specific error cases; -/// for example, uefi and wasm, where hostnames aren't -/// supported. -/// -/// # Underlying system calls -/// -/// | Platform | System call | -/// |--------------|---------------------------------------------------------------------------------------------------------| -/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) | -/// | Windows (8+) | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) | -/// -/// Note that platform-specific behavior [may change in the future][changes]. -/// -/// [changes]: crate::io#platform-specific-behavior -#[unstable(feature = "gethostname", issue = "135142")] -pub fn hostname() -> crate::io::Result { - crate::sys::net::hostname() -} diff --git a/crates/std/src/net/ip_addr.rs b/crates/std/src/net/ip_addr.rs deleted file mode 100644 index 7262899..0000000 --- a/crates/std/src/net/ip_addr.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Tests for this module -#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))] -mod tests; - -#[stable(feature = "ip_addr", since = "1.7.0")] -pub use core::net::IpAddr; -#[unstable(feature = "ip", issue = "27709")] -pub use core::net::Ipv6MulticastScope; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::net::{Ipv4Addr, Ipv6Addr}; diff --git a/crates/std/src/net/ip_addr/tests.rs b/crates/std/src/net/ip_addr/tests.rs deleted file mode 100644 index 7bed6f8..0000000 --- a/crates/std/src/net/ip_addr/tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::net::Ipv4Addr; -use crate::net::test::{sa4, tsa}; - -#[test] -fn to_socket_addr_socketaddr() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345); - assert_eq!(Ok(vec![a]), tsa(a)); -} diff --git a/crates/std/src/net/mod.rs b/crates/std/src/net/mod.rs deleted file mode 100644 index 3e4447e..0000000 --- a/crates/std/src/net/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Networking primitives for TCP/UDP communication. -//! -//! This module provides networking functionality for the Transmission Control and User -//! Datagram Protocols, as well as types for IP and socket addresses and functions related -//! to network properties. -//! -//! # Organization -//! -//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP -//! * [`UdpSocket`] provides functionality for communication over UDP -//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and -//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses -//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] -//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses -//! * [`ToSocketAddrs`] is a trait that is used for generic address resolution when interacting -//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`] -//! * Other types are return or parameter types for various methods in this module -//! -//! Rust disables inheritance of socket objects to child processes by default when possible. For -//! example, through the use of the `CLOEXEC` flag in UNIX systems or the `HANDLE_FLAG_INHERIT` -//! flag on Windows. - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::net::AddrParseError; - -#[unstable(feature = "gethostname", issue = "135142")] -pub use self::hostname::hostname; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; -#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] -pub use self::tcp::IntoIncoming; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::tcp::{Incoming, TcpListener, TcpStream}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::udp::UdpSocket; - -mod hostname; -mod ip_addr; -mod socket_addr; -mod tcp; -#[cfg(test)] -pub(crate) mod test; -mod udp; - -/// Possible values which can be passed to the [`TcpStream::shutdown`] method. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Shutdown { - /// The reading portion of the [`TcpStream`] should be shut down. - /// - /// All currently blocked and future [reads] will return [Ok]\(0). - /// - /// [reads]: crate::io::Read "io::Read" - #[stable(feature = "rust1", since = "1.0.0")] - Read, - /// The writing portion of the [`TcpStream`] should be shut down. - /// - /// All currently blocked and future [writes] will return an error. - /// - /// [writes]: crate::io::Write "io::Write" - #[stable(feature = "rust1", since = "1.0.0")] - Write, - /// Both the reading and the writing portions of the [`TcpStream`] should be shut down. - /// - /// See [`Shutdown::Read`] and [`Shutdown::Write`] for more information. - #[stable(feature = "rust1", since = "1.0.0")] - Both, -} diff --git a/crates/std/src/net/socket_addr.rs b/crates/std/src/net/socket_addr.rs deleted file mode 100644 index 8214ad3..0000000 --- a/crates/std/src/net/socket_addr.rs +++ /dev/null @@ -1,268 +0,0 @@ -// Tests for this module -#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))] -mod tests; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; - -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::{io, iter, option, slice, vec}; - -/// A trait for objects which can be converted or resolved to one or more -/// [`SocketAddr`] values. -/// -/// This trait is used for generic address resolution when constructing network -/// objects. By default it is implemented for the following types: -/// -/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function. -/// -/// * [`SocketAddrV4`], [`SocketAddrV6`], ([IpAddr], [u16]), -/// ([Ipv4Addr], [u16]), ([Ipv6Addr], [u16]): -/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially. -/// -/// * (&[str], [u16]): &[str] should be either a string representation -/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host -/// name. [`u16`] is the port number. -/// -/// * &[str]: the string should be either a string representation of a -/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like -/// `:` pair where `` is a [`u16`] value. -/// -/// * &[[SocketAddr]]: all [`SocketAddr`] values in the slice will be used. -/// -/// This trait allows constructing network objects like [`TcpStream`] or -/// [`UdpSocket`] easily with values of various types for the bind/connection -/// address. It is needed because sometimes one type is more appropriate than -/// the other: for simple uses a string like `"localhost:12345"` is much nicer -/// than manual construction of the corresponding [`SocketAddr`], but sometimes -/// [`SocketAddr`] value is *the* main source of the address, and converting it to -/// some other type (e.g., a string) just for it to be converted back to -/// [`SocketAddr`] in constructor methods is pointless. -/// -/// Addresses returned by the operating system that are not IP addresses are -/// silently ignored. -/// -/// [`FromStr`]: crate::str::FromStr "std::str::FromStr" -/// [`TcpStream`]: crate::net::TcpStream "net::TcpStream" -/// [`to_socket_addrs`]: ToSocketAddrs::to_socket_addrs -/// [`UdpSocket`]: crate::net::UdpSocket "net::UdpSocket" -/// -/// # Examples -/// -/// Creating a [`SocketAddr`] iterator that yields one item: -/// -/// ``` -/// use std::net::{ToSocketAddrs, SocketAddr}; -/// -/// let addr = SocketAddr::from(([127, 0, 0, 1], 443)); -/// let mut addrs_iter = addr.to_socket_addrs().unwrap(); -/// -/// assert_eq!(Some(addr), addrs_iter.next()); -/// assert!(addrs_iter.next().is_none()); -/// ``` -/// -/// Creating a [`SocketAddr`] iterator from a hostname: -/// -/// ```no_run -/// use std::net::{SocketAddr, ToSocketAddrs}; -/// -/// // assuming 'localhost' resolves to 127.0.0.1 -/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap(); -/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443)))); -/// assert!(addrs_iter.next().is_none()); -/// -/// // assuming 'foo' does not resolve -/// assert!("foo:443".to_socket_addrs().is_err()); -/// ``` -/// -/// Creating a [`SocketAddr`] iterator that yields multiple items: -/// -/// ``` -/// use std::net::{SocketAddr, ToSocketAddrs}; -/// -/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80)); -/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443)); -/// let addrs = vec![addr1, addr2]; -/// -/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap(); -/// -/// assert_eq!(Some(addr1), addrs_iter.next()); -/// assert_eq!(Some(addr2), addrs_iter.next()); -/// assert!(addrs_iter.next().is_none()); -/// ``` -/// -/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted -/// socket address `&str` (missing the port): -/// -/// ``` -/// use std::io; -/// use std::net::ToSocketAddrs; -/// -/// let err = "127.0.0.1".to_socket_addrs().unwrap_err(); -/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); -/// ``` -/// -/// [`TcpStream::connect`] is an example of a function that utilizes -/// `ToSocketAddrs` as a trait bound on its parameter in order to accept -/// different types: -/// -/// ```no_run -/// use std::net::{TcpStream, Ipv4Addr}; -/// -/// let stream = TcpStream::connect(("127.0.0.1", 443)); -/// // or -/// let stream = TcpStream::connect("127.0.0.1:443"); -/// // or -/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443)); -/// ``` -/// -/// [`TcpStream::connect`]: crate::net::TcpStream::connect -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ToSocketAddrs { - /// Returned iterator over socket addresses which this type may correspond - /// to. - #[stable(feature = "rust1", since = "1.0.0")] - type Iter: Iterator; - - /// Converts this object to an iterator of resolved [`SocketAddr`]s. - /// - /// The returned iterator might not actually yield any values depending on the - /// outcome of any resolution performed. - /// - /// Note that this function may block the current thread while resolution is - /// performed. - #[stable(feature = "rust1", since = "1.0.0")] - fn to_socket_addrs(&self) -> io::Result; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddr { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - Ok(Some(*self).into_iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddrV4 { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - SocketAddr::V4(*self).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for SocketAddrV6 { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - SocketAddr::V6(*self).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (IpAddr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - match ip { - IpAddr::V4(ref a) => (*a, port).to_socket_addrs(), - IpAddr::V6(ref a) => (*a, port).to_socket_addrs(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (Ipv4Addr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - SocketAddrV4::new(ip, port).to_socket_addrs() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (Ipv6Addr, u16) { - type Iter = option::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (ip, port) = *self; - SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs() - } -} - -fn lookup_host(host: &str, port: u16) -> io::Result> { - let addrs = crate::sys::net::lookup_host(host, port)?; - Ok(Vec::from_iter(addrs).into_iter()) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for (&str, u16) { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - let (host, port) = *self; - - // Try to parse the host as a regular IP address first - if let Ok(addr) = host.parse::() { - let addr = SocketAddr::new(addr, port); - return Ok(vec![addr].into_iter()); - } - - // Otherwise, make the system look it up. - lookup_host(host, port) - } -} - -#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")] -impl ToSocketAddrs for (String, u16) { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - (&*self.0, self.1).to_socket_addrs() - } -} - -// accepts strings like 'localhost:12345' -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for str { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - // Try to parse as a regular SocketAddr first - if let Ok(addr) = self.parse() { - return Ok(vec![addr].into_iter()); - } - - // Otherwise, split the string by ':' and convert the second part to u16... - let Some((host, port_str)) = self.rsplit_once(':') else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); - }; - let Ok(port) = port_str.parse::() else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); - }; - - // ... and make the system look up the host. - lookup_host(host, port) - } -} - -#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")] -impl<'a> ToSocketAddrs for &'a [SocketAddr] { - type Iter = iter::Cloned>; - - fn to_socket_addrs(&self) -> io::Result { - Ok(self.iter().cloned()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToSocketAddrs for &T { - type Iter = T::Iter; - fn to_socket_addrs(&self) -> io::Result { - (**self).to_socket_addrs() - } -} - -#[stable(feature = "string_to_socket_addrs", since = "1.16.0")] -impl ToSocketAddrs for String { - type Iter = vec::IntoIter; - fn to_socket_addrs(&self) -> io::Result> { - (&**self).to_socket_addrs() - } -} diff --git a/crates/std/src/net/socket_addr/tests.rs b/crates/std/src/net/socket_addr/tests.rs deleted file mode 100644 index 6a065cf..0000000 --- a/crates/std/src/net/socket_addr/tests.rs +++ /dev/null @@ -1,306 +0,0 @@ -use crate::net::test::{sa4, sa6, tsa}; -use crate::net::*; - -#[test] -fn to_socket_addr_ipaddr_u16() { - let a = Ipv4Addr::new(77, 88, 21, 11); - let p = 12345; - let e = SocketAddr::V4(SocketAddrV4::new(a, p)); - assert_eq!(Ok(vec![e]), tsa((a, p))); -} - -#[test] -fn to_socket_addr_str_u16() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352))); - - let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); - assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53))); - - let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); - #[cfg(not(target_env = "sgx"))] - assert!(tsa(("localhost", 23924)).unwrap().contains(&a)); - #[cfg(target_env = "sgx")] - let _ = a; -} - -#[test] -fn to_socket_addr_str() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352")); - - let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53); - assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53")); - - let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); - #[cfg(not(target_env = "sgx"))] - assert!(tsa("localhost:23924").unwrap().contains(&a)); - #[cfg(target_env = "sgx")] - let _ = a; -} - -#[test] -fn to_socket_addr_string() { - let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352); - assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352"))); - assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352"))); - assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352"))); - - let s = format!("{}:{}", "77.88.21.11", "24352"); - assert_eq!(Ok(vec![a]), tsa(s)); - // s has been moved into the tsa call -} - -#[test] -fn ipv4_socket_addr_to_string() { - // Shortest possible IPv4 length. - assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0"); - - // Longest possible IPv4 length. - assert_eq!( - SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(), - "255.255.255.255:65535" - ); - - // Test padding. - assert_eq!( - format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), - "1.1.1.1:53 " - ); - assert_eq!( - format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), - " 1.1.1.1:53" - ); -} - -#[test] -fn ipv6_socket_addr_to_string() { - // IPv4-mapped address. - assert_eq!( - SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0) - .to_string(), - "[::ffff:192.0.2.128]:8080" - ); - - // IPv4-compatible address. - assert_eq!( - SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), - "[::c000:280]:8080" - ); - - // IPv6 address with no zero segments. - assert_eq!( - SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(), - "[8:9:a:b:c:d:e:f]:80" - ); - - // Shortest possible IPv6 length. - assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0"); - - // Longest possible IPv6 length. - assert_eq!( - SocketAddrV6::new( - Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888), - u16::MAX, - u32::MAX, - u32::MAX, - ) - .to_string(), - "[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535" - ); - - // Test padding. - assert_eq!( - format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), - "[1:2:3:4:5:6:7:8]:9 " - ); - assert_eq!( - format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), - " [1:2:3:4:5:6:7:8]:9" - ); -} - -#[test] -fn bind_udp_socket_bad() { - // rust-lang/rust#53957: This is a regression test for a parsing problem - // discovered as part of issue rust-lang/rust#23076, where we were - // incorrectly parsing invalid input and then that would result in a - // successful `UdpSocket` binding when we would expect failure. - // - // At one time, this test was written as a call to `tsa` with - // INPUT_23076. However, that structure yields an unreliable test, - // because it ends up passing junk input to the DNS server, and some DNS - // servers will respond with `Ok` to such input, with the ip address of - // the DNS server itself. - // - // This form of the test is more robust: even when the DNS server - // returns its own address, it is still an error to bind a UDP socket to - // a non-local address, and so we still get an error here in that case. - - const INPUT_23076: &str = "1200::AB00:1234::2552:7777:1313:34300"; - - assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err()) -} - -#[test] -fn set_ip() { - fn ip4(low: u8) -> Ipv4Addr { - Ipv4Addr::new(77, 88, 21, low) - } - fn ip6(low: u16) -> Ipv6Addr { - Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low) - } - - let mut v4 = SocketAddrV4::new(ip4(11), 80); - assert_eq!(v4.ip(), &ip4(11)); - v4.set_ip(ip4(12)); - assert_eq!(v4.ip(), &ip4(12)); - - let mut addr = SocketAddr::V4(v4); - assert_eq!(addr.ip(), IpAddr::V4(ip4(12))); - addr.set_ip(IpAddr::V4(ip4(13))); - assert_eq!(addr.ip(), IpAddr::V4(ip4(13))); - addr.set_ip(IpAddr::V6(ip6(14))); - assert_eq!(addr.ip(), IpAddr::V6(ip6(14))); - - let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0); - assert_eq!(v6.ip(), &ip6(1)); - v6.set_ip(ip6(2)); - assert_eq!(v6.ip(), &ip6(2)); - - let mut addr = SocketAddr::V6(v6); - assert_eq!(addr.ip(), IpAddr::V6(ip6(2))); - addr.set_ip(IpAddr::V6(ip6(3))); - assert_eq!(addr.ip(), IpAddr::V6(ip6(3))); - addr.set_ip(IpAddr::V4(ip4(4))); - assert_eq!(addr.ip(), IpAddr::V4(ip4(4))); -} - -#[test] -fn set_port() { - let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80); - assert_eq!(v4.port(), 80); - v4.set_port(443); - assert_eq!(v4.port(), 443); - - let mut addr = SocketAddr::V4(v4); - assert_eq!(addr.port(), 443); - addr.set_port(8080); - assert_eq!(addr.port(), 8080); - - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0); - assert_eq!(v6.port(), 80); - v6.set_port(443); - assert_eq!(v6.port(), 443); - - let mut addr = SocketAddr::V6(v6); - assert_eq!(addr.port(), 443); - addr.set_port(8080); - assert_eq!(addr.port(), 8080); -} - -#[test] -fn set_flowinfo() { - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0); - assert_eq!(v6.flowinfo(), 10); - v6.set_flowinfo(20); - assert_eq!(v6.flowinfo(), 20); -} - -#[test] -fn set_scope_id() { - let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10); - assert_eq!(v6.scope_id(), 10); - v6.set_scope_id(20); - assert_eq!(v6.scope_id(), 20); -} - -#[test] -fn is_v4() { - let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)); - assert!(v4.is_ipv4()); - assert!(!v4.is_ipv6()); -} - -#[test] -fn is_v6() { - let v6 = SocketAddr::V6(SocketAddrV6::new( - Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), - 80, - 10, - 0, - )); - assert!(!v6.is_ipv4()); - assert!(v6.is_ipv6()); -} - -#[test] -fn socket_v4_to_str() { - let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080); - - assert_eq!(format!("{socket}"), "192.168.0.1:8080"); - assert_eq!(format!("{socket:<20}"), "192.168.0.1:8080 "); - assert_eq!(format!("{socket:>20}"), " 192.168.0.1:8080"); - assert_eq!(format!("{socket:^20}"), " 192.168.0.1:8080 "); - assert_eq!(format!("{socket:.10}"), "192.168.0."); -} - -#[test] -fn socket_v6_to_str() { - let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0); - - assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1]:53"); - assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1]:53 "); - assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1]:53"); - assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1]:53 "); - assert_eq!(format!("{socket:.15}"), "[2a02:6b8:0:1::"); - - socket.set_scope_id(5); - - assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1%5]:53"); - assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1%5]:53 "); - assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1%5]:53"); - assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1%5]:53 "); - assert_eq!(format!("{socket:.18}"), "[2a02:6b8:0:1::1%5"); -} - -#[test] -fn compare() { - let v4_1 = "224.120.45.1:23456".parse::().unwrap(); - let v4_2 = "224.210.103.5:12345".parse::().unwrap(); - let v4_3 = "224.210.103.5:23456".parse::().unwrap(); - let v6_1 = "[2001:db8:f00::1002]:23456".parse::().unwrap(); - let v6_2 = "[2001:db8:f00::2001]:12345".parse::().unwrap(); - let v6_3 = "[2001:db8:f00::2001]:23456".parse::().unwrap(); - - // equality - assert_eq!(v4_1, v4_1); - assert_eq!(v6_1, v6_1); - assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1)); - assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1)); - assert!(v4_1 != v4_2); - assert!(v6_1 != v6_2); - - // compare different addresses - assert!(v4_1 < v4_2); - assert!(v6_1 < v6_2); - assert!(v4_2 > v4_1); - assert!(v6_2 > v6_1); - - // compare the same address with different ports - assert!(v4_2 < v4_3); - assert!(v6_2 < v6_3); - assert!(v4_3 > v4_2); - assert!(v6_3 > v6_2); - - // compare different addresses with the same port - assert!(v4_1 < v4_3); - assert!(v6_1 < v6_3); - assert!(v4_3 > v4_1); - assert!(v6_3 > v6_1); - - // compare with an inferred right-hand side - assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap()); - assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap()); - assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap()); -} diff --git a/crates/std/src/net/tcp.rs b/crates/std/src/net/tcp.rs deleted file mode 100644 index dac568e..0000000 --- a/crates/std/src/net/tcp.rs +++ /dev/null @@ -1,1083 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -#[cfg(all( - test, - not(any( - target_os = "emscripten", - all(target_os = "wasi", target_env = "p1"), - target_os = "xous", - target_os = "trusty", - )) -))] -mod tests; - -use crate::fmt; -use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::iter::FusedIterator; -use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; -use crate::time::Duration; - -/// A TCP stream between a local and a remote socket. -/// -/// After creating a `TcpStream` by either [`connect`]ing to a remote host or -/// [`accept`]ing a connection on a [`TcpListener`], data can be transmitted -/// by [reading] and [writing] to it. -/// -/// The connection will be closed when the value is dropped. The reading and writing -/// portions of the connection can also be shut down individually with the [`shutdown`] -/// method. -/// -/// The Transmission Control Protocol is specified in [IETF RFC 793]. -/// -/// [`accept`]: TcpListener::accept -/// [`connect`]: TcpStream::connect -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [reading]: Read -/// [`shutdown`]: TcpStream::shutdown -/// [writing]: Write -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::net::TcpStream; -/// -/// fn main() -> std::io::Result<()> { -/// let mut stream = TcpStream::connect("127.0.0.1:34254")?; -/// -/// stream.write(&[1])?; -/// stream.read(&mut [0; 128])?; -/// Ok(()) -/// } // the stream is closed here -/// ``` -/// -/// # Platform-specific Behavior -/// -/// On Unix, writes to the underlying socket in `SOCK_STREAM` mode are made with -/// `MSG_NOSIGNAL` flag. This suppresses the emission of the `SIGPIPE` signal when writing -/// to disconnected socket. In some cases, getting a `SIGPIPE` would trigger process termination. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct TcpStream(net_imp::TcpStream); - -/// A TCP socket server, listening for connections. -/// -/// After creating a `TcpListener` by [`bind`]ing it to a socket address, it listens -/// for incoming TCP connections. These can be accepted by calling [`accept`] or by -/// iterating over the [`Incoming`] iterator returned by [`incoming`][`TcpListener::incoming`]. -/// -/// The socket will be closed when the value is dropped. -/// -/// The Transmission Control Protocol is specified in [IETF RFC 793]. -/// -/// [`accept`]: TcpListener::accept -/// [`bind`]: TcpListener::bind -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// -/// # Examples -/// -/// ```no_run -/// use std::net::{TcpListener, TcpStream}; -/// -/// fn handle_client(stream: TcpStream) { -/// // ... -/// } -/// -/// fn main() -> std::io::Result<()> { -/// let listener = TcpListener::bind("127.0.0.1:80")?; -/// -/// // accept connections and process them serially -/// for stream in listener.incoming() { -/// handle_client(stream?); -/// } -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct TcpListener(net_imp::TcpListener); - -/// An iterator that infinitely [`accept`]s connections on a [`TcpListener`]. -/// -/// This `struct` is created by the [`TcpListener::incoming`] method. -/// See its documentation for more. -/// -/// [`accept`]: TcpListener::accept -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Incoming<'a> { - listener: &'a TcpListener, -} - -/// An iterator that infinitely [`accept`]s connections on a [`TcpListener`]. -/// -/// This `struct` is created by the [`TcpListener::into_incoming`] method. -/// See its documentation for more. -/// -/// [`accept`]: TcpListener::accept -#[derive(Debug)] -#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] -pub struct IntoIncoming { - listener: TcpListener, -} - -impl TcpStream { - /// Opens a TCP connection to a remote host. - /// - /// `addr` is an address of the remote host. Anything which implements - /// [`ToSocketAddrs`] trait can be supplied for the address; see this trait - /// documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `connect` will be attempted with - /// each of the addresses until a connection is successful. If none of - /// the addresses result in a successful connection, the error returned from - /// the last connection attempt (the last address) is returned. - /// - /// # Examples - /// - /// Open a TCP connection to `127.0.0.1:8080`: - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") { - /// println!("Connected to the server!"); - /// } else { - /// println!("Couldn't connect to server..."); - /// } - /// ``` - /// - /// Open a TCP connection to `127.0.0.1:8080`. If the connection fails, open - /// a TCP connection to `127.0.0.1:8081`: - /// - /// ```no_run - /// use std::net::{SocketAddr, TcpStream}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 8080)), - /// SocketAddr::from(([127, 0, 0, 1], 8081)), - /// ]; - /// if let Ok(stream) = TcpStream::connect(&addrs[..]) { - /// println!("Connected to the server!"); - /// } else { - /// println!("Couldn't connect to server..."); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn connect(addr: A) -> io::Result { - net_imp::TcpStream::connect(addr).map(TcpStream) - } - - /// Opens a TCP connection to a remote host with a timeout. - /// - /// Unlike `connect`, `connect_timeout` takes a single [`SocketAddr`] since - /// timeout must be applied to individual addresses. - /// - /// It is an error to pass a zero `Duration` to this function. - /// - /// Unlike other methods on `TcpStream`, this does not correspond to a - /// single system call. It instead calls `connect` in nonblocking mode and - /// then uses an OS-specific mechanism to await the completion of the - /// connection request. - #[stable(feature = "tcpstream_connect_timeout", since = "1.21.0")] - pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { - net_imp::TcpStream::connect_timeout(addr, timeout).map(TcpStream) - } - - /// Returns the socket address of the remote peer of this TCP connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// assert_eq!(stream.peer_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn peer_addr(&self) -> io::Result { - self.0.peer_addr() - } - - /// Returns the socket address of the local half of this TCP connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{IpAddr, Ipv4Addr, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// assert_eq!(stream.local_addr().unwrap().ip(), - /// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Shuts down the read, write, or both halves of this connection. - /// - /// This function will cause all pending and future I/O on the specified - /// portions to return immediately with an appropriate value (see the - /// documentation of [`Shutdown`]). - /// - /// # Platform-specific behavior - /// - /// Calling this function multiple times may result in different behavior, - /// depending on the operating system. On Linux, the second call will - /// return `Ok(())`, but on macOS, it will return `ErrorKind::NotConnected`. - /// This may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Shutdown, TcpStream}; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.0.shutdown(how) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `TcpStream` is a reference to the same stream that this - /// object references. Both handles will read and write the same stream of - /// data, and options set on one stream will be propagated to the other - /// stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// let stream_clone = stream.try_clone().expect("clone failed..."); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(TcpStream) - } - - /// Sets the read timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a read times out as - /// a result of setting this option. For example Unix typically returns an - /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`read`]: Read::read - /// [`WouldBlock`]: io::ErrorKind::WouldBlock - /// [`TimedOut`]: io::ErrorKind::TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); - /// let result = stream.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_read_timeout(dur) - } - - /// Sets the write timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a write times out - /// as a result of setting this option. For example Unix typically returns - /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`write`]: Write::write - /// [`WouldBlock`]: io::ErrorKind::WouldBlock - /// [`TimedOut`]: io::ErrorKind::TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); - /// let result = stream.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_write_timeout(dur) - } - - /// Returns the read timeout of this socket. - /// - /// If the timeout is [`None`], then [`read`] calls will block indefinitely. - /// - /// # Platform-specific behavior - /// - /// Some platforms do not provide access to the current timeout. - /// - /// [`read`]: Read::read - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); - /// assert_eq!(stream.read_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.read_timeout() - } - - /// Returns the write timeout of this socket. - /// - /// If the timeout is [`None`], then [`write`] calls will block indefinitely. - /// - /// # Platform-specific behavior - /// - /// Some platforms do not provide access to the current timeout. - /// - /// [`write`]: Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); - /// assert_eq!(stream.write_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.write_timeout() - } - - /// Receives data on the socket from the remote address to which it is - /// connected, without removing that data from the queue. On success, - /// returns the number of bytes peeked. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recv` system call. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8000") - /// .expect("Couldn't connect to the server..."); - /// let mut buf = [0; 10]; - /// let len = stream.peek(&mut buf).expect("peek failed"); - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.0.peek(buf) - } - - /// Sets the value of the `SO_LINGER` option on this socket. - /// - /// This value controls how the socket is closed when data remains - /// to be sent. If `SO_LINGER` is set, the socket will remain open - /// for the specified duration as the system attempts to send pending data. - /// Otherwise, the system may close the socket immediately, or wait for a - /// default timeout. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(tcp_linger)] - /// - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); - /// ``` - #[unstable(feature = "tcp_linger", issue = "88494")] - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - self.0.set_linger(linger) - } - - /// Gets the value of the `SO_LINGER` option on this socket. - /// - /// For more information about this option, see [`TcpStream::set_linger`]. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(tcp_linger)] - /// - /// use std::net::TcpStream; - /// use std::time::Duration; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); - /// assert_eq!(stream.linger().unwrap(), Some(Duration::from_secs(0))); - /// ``` - #[unstable(feature = "tcp_linger", issue = "88494")] - pub fn linger(&self) -> io::Result> { - self.0.linger() - } - - /// Sets the value of the `TCP_NODELAY` option on this socket. - /// - /// If set, this option disables the Nagle algorithm. This means that - /// segments are always sent as soon as possible, even if there is only a - /// small amount of data. When not set, data is buffered until there is a - /// sufficient amount to send out, thereby avoiding the frequent sending of - /// small packets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - self.0.set_nodelay(nodelay) - } - - /// Gets the value of the `TCP_NODELAY` option on this socket. - /// - /// For more information about this option, see [`TcpStream::set_nodelay`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); - /// assert_eq!(stream.nodelay().unwrap_or(false), true); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn nodelay(&self) -> io::Result { - self.0.nodelay() - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`TcpStream::set_ttl`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); - /// assert_eq!(stream.ttl().unwrap_or(0), 100); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:8080") - /// .expect("Couldn't connect to the server..."); - /// stream.take_error().expect("No error was expected..."); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Moves this TCP stream into or out of nonblocking mode. - /// - /// This will result in `read`, `write`, `recv` and `send` system operations - /// becoming nonblocking, i.e., immediately returning from their calls. - /// If the IO operation is successful, `Ok` is returned and no further - /// action is required. If the IO operation could not be completed and needs - /// to be retried, an error with kind [`io::ErrorKind::WouldBlock`] is - /// returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// # Examples - /// - /// Reading bytes from a TCP stream in non-blocking mode: - /// - /// ```no_run - /// use std::io::{self, Read}; - /// use std::net::TcpStream; - /// - /// let mut stream = TcpStream::connect("127.0.0.1:7878") - /// .expect("Couldn't connect to the server..."); - /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// let mut buf = vec![]; - /// loop { - /// match stream.read_to_end(&mut buf) { - /// Ok(_) => break, - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// } - /// Err(e) => panic!("encountered IO error: {e}"), - /// }; - /// }; - /// println!("bytes: {buf:?}"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -// In addition to the `impl`s here, `TcpStream` also has `impl`s for -// `AsFd`/`From`/`Into` and -// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and -// `AsSocket`/`From`/`Into` and -// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for &TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl AsInner for TcpStream { - #[inline] - fn as_inner(&self) -> &net_imp::TcpStream { - &self.0 - } -} - -impl FromInner for TcpStream { - fn from_inner(inner: net_imp::TcpStream) -> TcpStream { - TcpStream(inner) - } -} - -impl IntoInner for TcpStream { - fn into_inner(self) -> net_imp::TcpStream { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl TcpListener { - /// Creates a new `TcpListener` which will be bound to the specified - /// address. - /// - /// The returned listener is ready for accepting connections. - /// - /// Binding with a port number of 0 will request that the OS assigns a port - /// to this listener. The port allocated can be queried via the - /// [`TcpListener::local_addr`] method. - /// - /// The address type can be any implementor of [`ToSocketAddrs`] trait. See - /// its documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `bind` will be attempted with - /// each of the addresses until one succeeds and returns the listener. If - /// none of the addresses succeed in creating a listener, the error returned - /// from the last attempt (the last address) is returned. - /// - /// # Examples - /// - /// Creates a TCP listener bound to `127.0.0.1:80`: - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// ``` - /// - /// Creates a TCP listener bound to `127.0.0.1:80`. If that fails, create a - /// TCP listener bound to `127.0.0.1:443`: - /// - /// ```no_run - /// use std::net::{SocketAddr, TcpListener}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 80)), - /// SocketAddr::from(([127, 0, 0, 1], 443)), - /// ]; - /// let listener = TcpListener::bind(&addrs[..]).unwrap(); - /// ``` - /// - /// Creates a TCP listener bound to a port assigned by the operating system - /// at `127.0.0.1`. - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let socket = TcpListener::bind("127.0.0.1:0").unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn bind(addr: A) -> io::Result { - net_imp::TcpListener::bind(addr).map(TcpListener) - } - - /// Returns the local socket address of this listener. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener}; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// assert_eq!(listener.local_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned [`TcpListener`] is a reference to the same socket that this - /// object references. Both handles can be used to accept incoming - /// connections and options set on one listener will affect the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// let listener_clone = listener.try_clone().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(TcpListener) - } - - /// Accept a new incoming connection from this listener. - /// - /// This function will block the calling thread until a new TCP connection - /// is established. When established, the corresponding [`TcpStream`] and the - /// remote peer's address will be returned. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); - /// match listener.accept() { - /// Ok((_socket, addr)) => println!("new client: {addr:?}"), - /// Err(e) => println!("couldn't get client: {e:?}"), - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so - // the `a` variable here is technically unused. - #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] - self.0.accept().map(|(a, b)| (TcpStream(a), b)) - } - - /// Returns an iterator over the connections being received on this - /// listener. - /// - /// The returned iterator will never return [`None`] and will also not yield - /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to - /// calling [`TcpListener::accept`] in a loop. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{TcpListener, TcpStream}; - /// - /// fn handle_connection(stream: TcpStream) { - /// //... - /// } - /// - /// fn main() -> std::io::Result<()> { - /// let listener = TcpListener::bind("127.0.0.1:80")?; - /// - /// for stream in listener.incoming() { - /// match stream { - /// Ok(stream) => { - /// handle_connection(stream); - /// } - /// Err(e) => { /* connection failed */ } - /// } - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn incoming(&self) -> Incoming<'_> { - Incoming { listener: self } - } - - /// Turn this into an iterator over the connections being received on this - /// listener. - /// - /// The returned iterator will never return [`None`] and will also not yield - /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to - /// calling [`TcpListener::accept`] in a loop. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(tcplistener_into_incoming)] - /// use std::net::{TcpListener, TcpStream}; - /// - /// fn listen_on(port: u16) -> impl Iterator { - /// let listener = TcpListener::bind(("127.0.0.1", port)).unwrap(); - /// listener.into_incoming() - /// .filter_map(Result::ok) /* Ignore failed connections */ - /// } - /// - /// fn main() -> std::io::Result<()> { - /// for stream in listen_on(80) { - /// /* handle the connection here */ - /// } - /// Ok(()) - /// } - /// ``` - #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "tcplistener_into_incoming", issue = "88373")] - pub fn into_incoming(self) -> IntoIncoming { - IntoIncoming { listener: self } - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`TcpListener::set_ttl`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); - /// assert_eq!(listener.ttl().unwrap_or(0), 100); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - #[stable(feature = "net2_mutators", since = "1.9.0")] - #[deprecated(since = "1.16.0", note = "this option can only be set before the socket is bound")] - #[allow(missing_docs)] - pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - self.0.set_only_v6(only_v6) - } - - #[stable(feature = "net2_mutators", since = "1.9.0")] - #[deprecated(since = "1.16.0", note = "this option can only be set before the socket is bound")] - #[allow(missing_docs)] - pub fn only_v6(&self) -> io::Result { - self.0.only_v6() - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.take_error().expect("No error was expected"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Moves this TCP stream into or out of nonblocking mode. - /// - /// This will result in the `accept` operation becoming nonblocking, - /// i.e., immediately returning from their calls. If the IO operation is - /// successful, `Ok` is returned and no further action is required. If the - /// IO operation could not be completed and needs to be retried, an error - /// with kind [`io::ErrorKind::WouldBlock`] is returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// # Examples - /// - /// Bind a TCP listener to an address, listen for connections, and read - /// bytes in nonblocking mode: - /// - /// ```no_run - /// use std::io; - /// use std::net::TcpListener; - /// - /// let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// # fn handle_connection(stream: std::net::TcpStream) { unimplemented!() } - /// for stream in listener.incoming() { - /// match stream { - /// Ok(s) => { - /// // do something with the TcpStream - /// handle_connection(s); - /// } - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// continue; - /// } - /// Err(e) => panic!("encountered IO error: {e}"), - /// } - /// } - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -// In addition to the `impl`s here, `TcpListener` also has `impl`s for -// `AsFd`/`From`/`Into` and -// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and -// `AsSocket`/`From`/`Into` and -// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a> Iterator for Incoming<'a> { - type Item = io::Result; - fn next(&mut self) -> Option> { - Some(self.listener.accept().map(|p| p.0)) - } -} - -#[stable(feature = "tcp_listener_incoming_fused_iterator", since = "1.64.0")] -impl FusedIterator for Incoming<'_> {} - -#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] -impl Iterator for IntoIncoming { - type Item = io::Result; - fn next(&mut self) -> Option> { - Some(self.listener.accept().map(|p| p.0)) - } -} - -#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] -impl FusedIterator for IntoIncoming {} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &net_imp::TcpListener { - &self.0 - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: net_imp::TcpListener) -> TcpListener { - TcpListener(inner) - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> net_imp::TcpListener { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} diff --git a/crates/std/src/net/tcp/tests.rs b/crates/std/src/net/tcp/tests.rs deleted file mode 100644 index e4a30b8..0000000 --- a/crates/std/src/net/tcp/tests.rs +++ /dev/null @@ -1,940 +0,0 @@ -use crate::io::prelude::*; -use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; -use crate::mem::MaybeUninit; -use crate::net::test::{next_test_ip4, next_test_ip6}; -use crate::net::*; -use crate::sync::mpsc::channel; -use crate::time::{Duration, Instant}; -use crate::{fmt, thread}; - -fn each_ip(f: &mut dyn FnMut(SocketAddr)) { - f(next_test_ip4()); - f(next_test_ip6()); -} - -macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - }; -} - -#[test] -fn bind_error() { - match TcpListener::bind("1.1.1.1:9999") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), - } -} - -#[test] -fn connect_error() { - match TcpStream::connect("0.0.0.0:1") { - Ok(..) => panic!(), - Err(e) => assert!( - e.kind() == ErrorKind::ConnectionRefused - || e.kind() == ErrorKind::InvalidInput - || e.kind() == ErrorKind::AddrInUse - || e.kind() == ErrorKind::AddrNotAvailable - || e.kind() == ErrorKind::NetworkUnreachable, - "bad error: {} {:?}", - e, - e.kind() - ), - } -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -fn connect_timeout_error() { - let socket_addr = next_test_ip4(); - let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); - assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); - - let _listener = TcpListener::bind(&socket_addr).unwrap(); - assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok()); -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn listen_localhost() { - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); - - let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); - t!(stream.write(&[144])); - }); - - let mut stream = t!(listener.accept()).0; - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 144); -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn connect_loopback() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let host = match addr { - SocketAddr::V4(..) => "127.0.0.1", - SocketAddr::V6(..) => "::1", - }; - let mut stream = t!(TcpStream::connect(&(host, addr.port()))); - t!(stream.write(&[66])); - }); - - let mut stream = t!(acceptor.accept()).0; - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 66); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn smoke_test() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - t!(stream.write(&[99])); - tx.send(t!(stream.local_addr())).unwrap(); - }); - - let (mut stream, addr) = t!(acceptor.accept()); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 99); - assert_eq!(addr, t!(rx.recv())); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn read_eof() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let _stream = t!(TcpStream::connect(&addr)); - // Close - }); - - let mut stream = t!(acceptor.accept()).0; - let mut buf = [0]; - let nread = t!(stream.read(&mut buf)); - assert_eq!(nread, 0); - let nread = t!(stream.read(&mut buf)); - assert_eq!(nread, 0); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn write_close() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - drop(t!(TcpStream::connect(&addr))); - tx.send(()).unwrap(); - }); - - let mut stream = t!(acceptor.accept()).0; - rx.recv().unwrap(); - let buf = [0]; - match stream.write(&buf) { - Ok(..) => {} - Err(e) => { - assert!( - e.kind() == ErrorKind::ConnectionReset - || e.kind() == ErrorKind::BrokenPipe - || e.kind() == ErrorKind::ConnectionAborted, - "unknown error: {e}" - ); - } - } - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn multiple_connect_serial() { - each_ip(&mut |addr| { - let max = 10; - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - for _ in 0..max { - let mut stream = t!(TcpStream::connect(&addr)); - t!(stream.write(&[99])); - } - }); - - for stream in acceptor.incoming().take(max) { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert_eq!(buf[0], 99); - } - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn multiple_connect_interleaved_greedy_schedule() { - const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let acceptor = acceptor; - for (i, stream) in acceptor.incoming().enumerate().take(MAX) { - // Start another thread to handle the connection - let _t = thread::spawn(move || { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == i as u8); - }); - } - }); - - connect(0, addr); - }); - - fn connect(i: usize, addr: SocketAddr) { - if i == MAX { - return; - } - - let t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - // Connect again before writing - connect(i + 1, addr); - t!(stream.write(&[i as u8])); - }); - t.join().ok().expect("thread panicked"); - } -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn multiple_connect_interleaved_lazy_schedule() { - const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - for stream in acceptor.incoming().take(MAX) { - // Start another thread to handle the connection - let _t = thread::spawn(move || { - let mut stream = t!(stream); - let mut buf = [0]; - t!(stream.read(&mut buf)); - assert!(buf[0] == 99); - }); - } - }); - - connect(0, addr); - }); - - fn connect(i: usize, addr: SocketAddr) { - if i == MAX { - return; - } - - let t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&addr)); - connect(i + 1, addr); - t!(stream.write(&[99])); - }); - t.join().ok().expect("thread panicked"); - } -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn socket_and_peer_name() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); - let so_name = t!(listener.local_addr()); - assert_eq!(addr, so_name); - let _t = thread::spawn(move || { - t!(listener.accept()); - }); - - let stream = t!(TcpStream::connect(&addr)); - assert_eq!(addr, t!(stream.peer_addr())); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn partial_read() { - each_ip(&mut |addr| { - let (tx, rx) = channel(); - let srv = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut cl = t!(srv.accept()).0; - cl.write(&[10]).unwrap(); - let mut b = [0]; - t!(cl.read(&mut b)); - tx.send(()).unwrap(); - }); - - let mut c = t!(TcpStream::connect(&addr)); - let mut b = [0; 10]; - assert_eq!(c.read(&mut b).unwrap(), 1); - t!(c.write(&[1])); - rx.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn read_buf() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); - let t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - s.write_all(&[1, 2, 3, 4]).unwrap(); - }); - - let mut s = t!(srv.accept()).0; - let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - t!(s.read_buf(buf.unfilled())); - assert_eq!(buf.filled(), &[1, 2, 3, 4]); - // TcpStream::read_buf should omit buffer initialization. - assert_eq!(buf.init_len(), 4); - - t.join().ok().expect("thread panicked"); - }) -} - -#[test] -fn read_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); - let mut s1 = t!(TcpStream::connect(&addr)); - let mut s2 = t!(srv.accept()).0; - - let len = s1.write(&[10, 11, 12]).unwrap(); - assert_eq!(len, 3); - - let mut a = []; - let mut b = [0]; - let mut c = [0; 3]; - let len = t!(s2.read_vectored(&mut [ - IoSliceMut::new(&mut a), - IoSliceMut::new(&mut b), - IoSliceMut::new(&mut c) - ],)); - assert!(len > 0); - assert_eq!(b, [10]); - // some implementations don't support readv, so we may only fill the first buffer - assert!(len == 1 || c == [11, 12, 0]); - }) -} - -#[test] -fn write_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); - let mut s1 = t!(TcpStream::connect(&addr)); - let mut s2 = t!(srv.accept()).0; - - let a = []; - let b = [10]; - let c = [11, 12]; - t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)])); - - let mut buf = [0; 4]; - let len = t!(s2.read(&mut buf)); - // some implementations don't support writev, so we may only write the first buffer - if len == 1 { - assert_eq!(buf, [10, 0, 0, 0]); - } else { - assert_eq!(len, 3); - assert_eq!(buf, [10, 11, 12, 0]); - } - }) -} - -#[test] -fn double_bind() { - each_ip(&mut |addr| { - let listener1 = t!(TcpListener::bind(&addr)); - match TcpListener::bind(&addr) { - Ok(listener2) => panic!( - "This system (perhaps due to options set by TcpListener::bind) \ - permits double binding: {:?} and {:?}", - listener1, listener2 - ), - Err(e) => { - assert!( - e.kind() == ErrorKind::ConnectionRefused - || e.kind() == ErrorKind::Uncategorized - || e.kind() == ErrorKind::AddrInUse, - "unknown error: {} {:?}", - e, - e.kind() - ); - } - } - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn tcp_clone_smoke() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - let mut buf = [0, 0]; - assert_eq!(s.read(&mut buf).unwrap(), 1); - assert_eq!(buf[0], 1); - t!(s.write(&[2])); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - rx1.recv().unwrap(); - t!(s2.write(&[1])); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - assert_eq!(s1.read(&mut buf).unwrap(), 1); - rx2.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn tcp_clone_two_read() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - t!(s.write(&[1])); - rx.recv().unwrap(); - t!(s.write(&[2])); - rx.recv().unwrap(); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - let mut buf = [0, 0]; - t!(s2.read(&mut buf)); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - t!(s1.read(&mut buf)); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn tcp_clone_two_write() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); - - let _t = thread::spawn(move || { - let mut s = t!(TcpStream::connect(&addr)); - let mut buf = [0, 1]; - t!(s.read(&mut buf)); - t!(s.read(&mut buf)); - }); - - let mut s1 = t!(acceptor.accept()).0; - let s2 = t!(s1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut s2 = s2; - t!(s2.write(&[1])); - done.send(()).unwrap(); - }); - t!(s1.write(&[2])); - - rx.recv().unwrap(); - }) -} - -#[test] -// FIXME: https://github.com/fortanix/rust-sgx/issues/110 -#[cfg_attr(target_env = "sgx", ignore)] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn shutdown_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut c = t!(a.accept()).0; - let mut b = [0]; - assert_eq!(c.read(&mut b).unwrap(), 0); - t!(c.write(&[1])); - }); - - let mut s = t!(TcpStream::connect(&addr)); - t!(s.shutdown(Shutdown::Write)); - assert!(s.write(&[1]).is_err()); - let mut b = [0, 0]; - assert_eq!(t!(s.read(&mut b)), 1); - assert_eq!(b[0], 1); - }) -} - -#[test] -// FIXME: https://github.com/fortanix/rust-sgx/issues/110 -#[cfg_attr(target_env = "sgx", ignore)] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn close_readwrite_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let (tx, rx) = channel::<()>(); - let _t = thread::spawn(move || { - let _s = t!(a.accept()); - let _ = rx.recv(); - }); - - let mut b = [0]; - let mut s = t!(TcpStream::connect(&addr)); - let mut s2 = t!(s.try_clone()); - - // closing should prevent reads/writes - t!(s.shutdown(Shutdown::Write)); - assert!(s.write(&[0]).is_err()); - t!(s.shutdown(Shutdown::Read)); - assert_eq!(s.read(&mut b).unwrap(), 0); - - // closing should affect previous handles - assert!(s2.write(&[0]).is_err()); - assert_eq!(s2.read(&mut b).unwrap(), 0); - - // closing should affect new handles - let mut s3 = t!(s.try_clone()); - assert!(s3.write(&[0]).is_err()); - assert_eq!(s3.read(&mut b).unwrap(), 0); - - // make sure these don't die - let _ = s2.shutdown(Shutdown::Read); - let _ = s2.shutdown(Shutdown::Write); - let _ = s3.shutdown(Shutdown::Read); - let _ = s3.shutdown(Shutdown::Write); - drop(tx); - }) -} - -#[test] -// FIXME: https://github.com/fortanix/rust-sgx/issues/110 -#[cfg_attr(target_env = "sgx", ignore)] -// On windows, shutdown will not wake up blocking I/O operations. -#[cfg_attr(windows, ignore)] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn close_read_wakes_up() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let (stream, _) = t!(listener.accept()); - stream - }); - - let mut stream = t!(TcpStream::connect(&addr)); - let stream2 = t!(stream.try_clone()); - - let _t = thread::spawn(move || { - let stream2 = stream2; - - // to make it more likely that `read` happens before `shutdown` - thread::sleep(Duration::from_millis(1000)); - - // this should wake up the reader up - t!(stream2.shutdown(Shutdown::Read)); - }); - - // this `read` should get interrupted by `shutdown` - assert_eq!(t!(stream.read(&mut [0])), 0); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn clone_while_reading() { - each_ip(&mut |addr| { - let accept = t!(TcpListener::bind(&addr)); - - // Enqueue a thread to write to a socket - let (tx, rx) = channel(); - let (txdone, rxdone) = channel(); - let txdone2 = txdone.clone(); - let _t = thread::spawn(move || { - let mut tcp = t!(TcpStream::connect(&addr)); - rx.recv().unwrap(); - t!(tcp.write(&[0])); - txdone2.send(()).unwrap(); - }); - - // Spawn off a reading clone - let tcp = t!(accept.accept()).0; - let tcp2 = t!(tcp.try_clone()); - let txdone3 = txdone.clone(); - let _t = thread::spawn(move || { - let mut tcp2 = tcp2; - t!(tcp2.read(&mut [0])); - txdone3.send(()).unwrap(); - }); - - // Try to ensure that the reading clone is indeed reading - for _ in 0..50 { - thread::yield_now(); - } - - // clone the handle again while it's reading, then let it finish the - // read. - let _ = t!(tcp.try_clone()); - tx.send(()).unwrap(); - rxdone.recv().unwrap(); - rxdone.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn clone_accept_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let a2 = t!(a.try_clone()); - - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - - t!(a.accept()); - t!(a2.accept()); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn clone_accept_concurrent() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); - let a2 = t!(a.try_clone()); - - let (tx, rx) = channel(); - let tx2 = tx.clone(); - - let _t = thread::spawn(move || { - tx.send(t!(a.accept())).unwrap(); - }); - let _t = thread::spawn(move || { - tx2.send(t!(a2.accept())).unwrap(); - }); - - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - let _t = thread::spawn(move || { - let _ = TcpStream::connect(&addr); - }); - - rx.recv().unwrap(); - rx.recv().unwrap(); - }) -} - -#[test] -fn debug() { - #[cfg(not(target_env = "sgx"))] - fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { - addr - } - #[cfg(target_env = "sgx")] - fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { - addr.to_string() - } - - #[cfg(any(unix, target_os = "wasi"))] - use crate::os::fd::AsRawFd; - #[cfg(target_env = "sgx")] - use crate::os::fortanix_sgx::io::AsRawFd; - #[cfg(not(windows))] - fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug { - addr.as_raw_fd() - } - #[cfg(windows)] - fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug { - addr.as_raw_socket() - } - - let inner_name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&socket_addr)); - let compare = format!( - "TcpListener {{ addr: {:?}, {}: {:?} }}", - render_socket_addr(&socket_addr), - inner_name, - render_inner(&listener) - ); - assert_eq!(format!("{listener:?}"), compare); - - let stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); - let compare = format!( - "TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}", - render_socket_addr(&stream.local_addr().unwrap()), - render_socket_addr(&stream.peer_addr().unwrap()), - inner_name, - render_inner(&stream) - ); - assert_eq!(format!("{stream:?}"), compare); -} - -// FIXME: re-enabled openbsd tests once their socket timeout code -// no longer has rounding errors. -// VxWorks ignores SO_SNDTIMEO. -#[cfg_attr( - any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), - ignore -)] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -#[test] -fn timeouts() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - let dur = Duration::new(15410, 0); - - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_read_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.read_timeout())); - - assert_eq!(None, t!(stream.write_timeout())); - - t!(stream.set_write_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.write_timeout())); - - t!(stream.set_read_timeout(None)); - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_write_timeout(None)); - assert_eq!(None, t!(stream.write_timeout())); - drop(listener); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -fn test_read_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut buf = [0; 10]; - let start = Instant::now(); - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - assert!(start.elapsed() > Duration::from_millis(400)); - drop(listener); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -fn test_read_with_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut other_end = t!(listener.accept()).0; - t!(other_end.write_all(b"hello world")); - - let mut buf = [0; 11]; - t!(stream.read(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - - let start = Instant::now(); - let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - assert!(start.elapsed() > Duration::from_millis(400)); - drop(listener); -} - -// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors -// when passed zero Durations -#[test] -fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); - - let result = stream.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = stream.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - drop(listener); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] -#[cfg_attr(target_os = "wasi", ignore)] // linger not supported -fn linger() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - assert_eq!(None, t!(stream.linger())); - t!(stream.set_linger(Some(Duration::from_secs(1)))); - assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger())); - t!(stream.set_linger(None)); - assert_eq!(None, t!(stream.linger())); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] -fn nodelay() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - assert_eq!(false, t!(stream.nodelay())); - t!(stream.set_nodelay(true)); - assert_eq!(true, t!(stream.nodelay())); - t!(stream.set_nodelay(false)); - assert_eq!(false, t!(stream.nodelay())); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] -fn ttl() { - let ttl = 100; - - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - t!(listener.set_ttl(ttl)); - assert_eq!(ttl, t!(listener.ttl())); - - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - t!(stream.set_ttl(ttl)); - assert_eq!(ttl, t!(stream.ttl())); -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] -fn set_nonblocking() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); - - t!(listener.set_nonblocking(true)); - t!(listener.set_nonblocking(false)); - - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); - - t!(stream.set_nonblocking(false)); - t!(stream.set_nonblocking(true)); - - let mut buf = [0]; - match stream.read(&mut buf) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {e}"), - } -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn peek() { - each_ip(&mut |addr| { - let (txdone, rxdone) = channel(); - - let srv = t!(TcpListener::bind(&addr)); - let _t = thread::spawn(move || { - let mut cl = t!(srv.accept()).0; - cl.write(&[1, 3, 3, 7]).unwrap(); - t!(rxdone.recv()); - }); - - let mut c = t!(TcpStream::connect(&addr)); - let mut b = [0; 10]; - for _ in 1..3 { - let len = c.peek(&mut b).unwrap(); - assert_eq!(len, 4); - } - let len = c.read(&mut b).unwrap(); - assert_eq!(len, 4); - - t!(c.set_nonblocking(true)); - match c.peek(&mut b) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {e}"), - } - t!(txdone.send(())); - }) -} - -#[test] -#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 -fn connect_timeout_valid() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let addr = listener.local_addr().unwrap(); - TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap(); -} diff --git a/crates/std/src/net/test.rs b/crates/std/src/net/test.rs deleted file mode 100644 index df48b2f..0000000 --- a/crates/std/src/net/test.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![allow(warnings)] // not used on emscripten - -use crate::env; -use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; -use crate::sync::atomic::{AtomicUsize, Ordering}; - -static PORT: AtomicUsize = AtomicUsize::new(0); -const BASE_PORT: u16 = 19600; - -pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) -} - -pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; - SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) -} - -pub fn sa4(a: Ipv4Addr, p: u16) -> SocketAddr { - SocketAddr::V4(SocketAddrV4::new(a, p)) -} - -pub fn sa6(a: Ipv6Addr, p: u16) -> SocketAddr { - SocketAddr::V6(SocketAddrV6::new(a, p, 0, 0)) -} - -pub fn tsa(a: A) -> Result, String> { - match a.to_socket_addrs() { - Ok(a) => Ok(a.collect()), - Err(e) => Err(e.to_string()), - } -} - -pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { - match (a, b) { - (SocketAddr::V6(a), SocketAddr::V6(b)) => { - a.ip().segments() == b.ip().segments() - && a.flowinfo() == b.flowinfo() - && a.port() == b.port() - } - _ => a == b, - } -} diff --git a/crates/std/src/net/udp.rs b/crates/std/src/net/udp.rs deleted file mode 100644 index 136803a..0000000 --- a/crates/std/src/net/udp.rs +++ /dev/null @@ -1,848 +0,0 @@ -#[cfg(all( - test, - not(any( - target_os = "emscripten", - all(target_os = "wasi", target_env = "p1"), - target_env = "sgx", - target_os = "xous", - target_os = "trusty", - )) -))] -mod tests; - -use crate::fmt; -use crate::io::{self, ErrorKind}; -use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; -use crate::time::Duration; - -/// A UDP socket. -/// -/// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be -/// [sent to] and [received from] any other socket address. -/// -/// Although UDP is a connectionless protocol, this implementation provides an interface -/// to set an address where data should be sent and received from. After setting a remote -/// address with [`connect`], data can be sent to and received from that address with -/// [`send`] and [`recv`]. -/// -/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is -/// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP -/// primitives. -/// -/// [`bind`]: UdpSocket::bind -/// [`connect`]: UdpSocket::connect -/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768 -/// [`recv`]: UdpSocket::recv -/// [received from]: UdpSocket::recv_from -/// [`send`]: UdpSocket::send -/// [sent to]: UdpSocket::send_to -/// [`TcpListener`]: crate::net::TcpListener -/// [`TcpStream`]: crate::net::TcpStream -/// -/// # Examples -/// -/// ```no_run -/// use std::net::UdpSocket; -/// -/// fn main() -> std::io::Result<()> { -/// { -/// let socket = UdpSocket::bind("127.0.0.1:34254")?; -/// -/// // Receives a single datagram message on the socket. If `buf` is too small to hold -/// // the message, it will be cut off. -/// let mut buf = [0; 10]; -/// let (amt, src) = socket.recv_from(&mut buf)?; -/// -/// // Redeclare `buf` as slice of the received data and send reverse data back to origin. -/// let buf = &mut buf[..amt]; -/// buf.reverse(); -/// socket.send_to(buf, &src)?; -/// } // the socket is closed here -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct UdpSocket(net_imp::UdpSocket); - -impl UdpSocket { - /// Creates a UDP socket from the given address. - /// - /// The address type can be any implementor of [`ToSocketAddrs`] trait. See - /// its documentation for concrete examples. - /// - /// If `addr` yields multiple addresses, `bind` will be attempted with - /// each of the addresses until one succeeds and returns the socket. If none - /// of the addresses succeed in creating a socket, the error returned from - /// the last attempt (the last address) is returned. - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:3400`: - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); - /// ``` - /// - /// Creates a UDP socket bound to `127.0.0.1:3400`. If the socket cannot be - /// bound to that address, create a UDP socket bound to `127.0.0.1:3401`: - /// - /// ```no_run - /// use std::net::{SocketAddr, UdpSocket}; - /// - /// let addrs = [ - /// SocketAddr::from(([127, 0, 0, 1], 3400)), - /// SocketAddr::from(([127, 0, 0, 1], 3401)), - /// ]; - /// let socket = UdpSocket::bind(&addrs[..]).expect("couldn't bind to address"); - /// ``` - /// - /// Creates a UDP socket bound to a port assigned by the operating system - /// at `127.0.0.1`. - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - /// ``` - /// - /// Note that `bind` declares the scope of your network connection. - /// You can only receive datagrams from and send datagrams to - /// participants in that view of the network. - /// For instance, binding to a loopback address as in the example - /// above will prevent you from sending datagrams to another device - /// in your local network. - /// - /// In order to limit your view of the network the least, `bind` to - /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`]. - #[stable(feature = "rust1", since = "1.0.0")] - pub fn bind(addr: A) -> io::Result { - net_imp::UdpSocket::bind(addr).map(UdpSocket) - } - - /// Receives a single datagram message on the socket. On success, returns the number - /// of bytes read and the origin. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let mut buf = [0; 10]; - /// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf) - /// .expect("Didn't receive data"); - /// let filled_buf = &mut buf[..number_of_bytes]; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0.recv_from(buf) - } - - /// Receives a single datagram message on the socket, without removing it from the - /// queue. On success, returns the number of bytes read and the origin. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call. - /// - /// Do not use this function to implement busy waiting, instead use `libc::poll` to - /// synchronize IO events on one or more sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let mut buf = [0; 10]; - /// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf) - /// .expect("Didn't receive data"); - /// let filled_buf = &mut buf[..number_of_bytes]; - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0.peek_from(buf) - } - - /// Sends data on the socket to the given address. On success, returns the - /// number of bytes written. Note that the operating system may refuse - /// buffers larger than 65507. However, partial writes are not possible - /// until buffer sizes above `i32::MAX`. - /// - /// Address type can be any implementor of [`ToSocketAddrs`] trait. See its - /// documentation for concrete examples. - /// - /// It is possible for `addr` to yield multiple addresses, but `send_to` - /// will only send data to the first address yielded by `addr`. - /// - /// This will return an error when the IP version of the local socket - /// does not match that returned from [`ToSocketAddrs`]. - /// - /// See [Issue #34202] for more details. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data"); - /// ``` - /// - /// [Issue #34202]: https://github.com/rust-lang/rust/issues/34202 - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { - match addr.to_socket_addrs()?.next() { - Some(addr) => self.0.send_to(buf, &addr), - None => Err(io::const_error!(ErrorKind::InvalidInput, "no addresses to send data to")), - } - } - - /// Returns the socket address of the remote peer this socket was connected to. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("192.168.0.1:41203").expect("couldn't connect to address"); - /// assert_eq!(socket.peer_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 41203))); - /// ``` - /// - /// If the socket isn't connected, it will return a [`NotConnected`] error. - /// - /// [`NotConnected`]: io::ErrorKind::NotConnected - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// assert_eq!(socket.peer_addr().unwrap_err().kind(), - /// std::io::ErrorKind::NotConnected); - /// ``` - #[stable(feature = "udp_peer_addr", since = "1.40.0")] - pub fn peer_addr(&self) -> io::Result { - self.0.peer_addr() - } - - /// Returns the socket address that this socket was created from. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// assert_eq!(socket.local_addr().unwrap(), - /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 34254))); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn local_addr(&self) -> io::Result { - self.0.socket_addr() - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UdpSocket` is a reference to the same socket that this - /// object references. Both handles will read and write the same port, and - /// options set on one socket will be propagated to the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// let socket_clone = socket.try_clone().expect("couldn't clone the socket"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(UdpSocket) - } - - /// Sets the read timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a read times out as - /// a result of setting this option. For example Unix typically returns an - /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`read`]: io::Read::read - /// [`WouldBlock`]: io::ErrorKind::WouldBlock - /// [`TimedOut`]: io::ErrorKind::TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); - /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_read_timeout(dur) - } - - /// Sets the write timeout to the timeout specified. - /// - /// If the value specified is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// # Platform-specific behavior - /// - /// Platforms may return a different error code whenever a write times out - /// as a result of setting this option. For example Unix typically returns - /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. - /// - /// [`write`]: io::Write::write - /// [`WouldBlock`]: io::ErrorKind::WouldBlock - /// [`TimedOut`]: io::ErrorKind::TimedOut - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); - /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.0.set_write_timeout(dur) - } - - /// Returns the read timeout of this socket. - /// - /// If the timeout is [`None`], then [`read`] calls will block indefinitely. - /// - /// [`read`]: io::Read::read - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); - /// assert_eq!(socket.read_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn read_timeout(&self) -> io::Result> { - self.0.read_timeout() - } - - /// Returns the write timeout of this socket. - /// - /// If the timeout is [`None`], then [`write`] calls will block indefinitely. - /// - /// [`write`]: io::Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); - /// assert_eq!(socket.write_timeout().unwrap(), None); - /// ``` - #[stable(feature = "socket_timeout", since = "1.4.0")] - pub fn write_timeout(&self) -> io::Result> { - self.0.write_timeout() - } - - /// Sets the value of the `SO_BROADCAST` option for this socket. - /// - /// When enabled, this socket is allowed to send packets to a broadcast - /// address. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_broadcast(false).expect("set_broadcast call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - self.0.set_broadcast(broadcast) - } - - /// Gets the value of the `SO_BROADCAST` option for this socket. - /// - /// For more information about this option, see [`UdpSocket::set_broadcast`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_broadcast(false).expect("set_broadcast call failed"); - /// assert_eq!(socket.broadcast().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn broadcast(&self) -> io::Result { - self.0.broadcast() - } - - /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. - /// - /// If enabled, multicast packets will be looped back to the local socket. - /// Note that this might not have any effect on IPv6 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - self.0.set_multicast_loop_v4(multicast_loop_v4) - } - - /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. - /// - /// For more information about this option, see [`UdpSocket::set_multicast_loop_v4`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); - /// assert_eq!(socket.multicast_loop_v4().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_loop_v4(&self) -> io::Result { - self.0.multicast_loop_v4() - } - - /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. - /// - /// Indicates the time-to-live value of outgoing multicast packets for - /// this socket. The default value is 1 which means that multicast packets - /// don't leave the local network unless explicitly requested. - /// - /// Note that this might not have any effect on IPv6 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - self.0.set_multicast_ttl_v4(multicast_ttl_v4) - } - - /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. - /// - /// For more information about this option, see [`UdpSocket::set_multicast_ttl_v4`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); - /// assert_eq!(socket.multicast_ttl_v4().unwrap(), 42); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0.multicast_ttl_v4() - } - - /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. - /// - /// Controls whether this socket sees the multicast packets it sends itself. - /// Note that this might not have any affect on IPv4 sockets. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - self.0.set_multicast_loop_v6(multicast_loop_v6) - } - - /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. - /// - /// For more information about this option, see [`UdpSocket::set_multicast_loop_v6`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); - /// assert_eq!(socket.multicast_loop_v6().unwrap(), false); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn multicast_loop_v6(&self) -> io::Result { - self.0.multicast_loop_v6() - } - - /// Sets the value for the `IP_TTL` option on this socket. - /// - /// This value sets the time-to-live field that is used in every packet sent - /// from this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_ttl(42).expect("set_ttl call failed"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.0.set_ttl(ttl) - } - - /// Gets the value of the `IP_TTL` option for this socket. - /// - /// For more information about this option, see [`UdpSocket::set_ttl`]. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.set_ttl(42).expect("set_ttl call failed"); - /// assert_eq!(socket.ttl().unwrap(), 42); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn ttl(&self) -> io::Result { - self.0.ttl() - } - - /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. - /// - /// This function specifies a new multicast group for this socket to join. - /// The address must be a valid multicast address, and `interface` is the - /// address of the local interface with which the system should join the - /// multicast group. If it's equal to [`UNSPECIFIED`](Ipv4Addr::UNSPECIFIED) - /// then an appropriate interface is chosen by the system. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.0.join_multicast_v4(multiaddr, interface) - } - - /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. - /// - /// This function specifies a new multicast group for this socket to join. - /// The address must be a valid multicast address, and `interface` is the - /// index of the interface to join/leave (or 0 to indicate any interface). - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.0.join_multicast_v6(multiaddr, interface) - } - - /// Executes an operation of the `IP_DROP_MEMBERSHIP` type. - /// - /// For more information about this option, see [`UdpSocket::join_multicast_v4`]. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.0.leave_multicast_v4(multiaddr, interface) - } - - /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. - /// - /// For more information about this option, see [`UdpSocket::join_multicast_v6`]. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.0.leave_multicast_v6(multiaddr, interface) - } - - /// Gets the value of the `SO_ERROR` option on this socket. - /// - /// This will retrieve the stored error in the underlying socket, clearing - /// the field in the process. This can be useful for checking errors between - /// calls. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// match socket.take_error() { - /// Ok(Some(error)) => println!("UdpSocket error: {error:?}"), - /// Ok(None) => println!("No error"), - /// Err(error) => println!("UdpSocket.take_error failed: {error:?}"), - /// } - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn take_error(&self) -> io::Result> { - self.0.take_error() - } - - /// Connects this UDP socket to a remote address, allowing the `send` and - /// `recv` syscalls to be used to send data and also applies filters to only - /// receive data from the specified address. - /// - /// If `addr` yields multiple addresses, `connect` will be attempted with - /// each of the addresses until the underlying OS function returns no - /// error. Note that usually, a successful `connect` call does not specify - /// that there is a remote server listening on the port, rather, such an - /// error would only be detected after the first send. If the OS returns an - /// error for each of the specified addresses, the error returned from the - /// last connection attempt (the last address) is returned. - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:3400` and connect the socket to - /// `127.0.0.1:8080`: - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// ``` - /// - /// Unlike in the TCP case, passing an array of addresses to the `connect` - /// function of a UDP socket is not a useful thing to do: The OS will be - /// unable to determine whether something is listening on the remote - /// address without the application sending data. - /// - /// If your first `connect` is to a loopback address, subsequent - /// `connect`s to non-loopback addresses might fail, depending - /// on the platform. - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn connect(&self, addr: A) -> io::Result<()> { - self.0.connect(addr) - } - - /// Sends data on the socket to the remote address to which it is connected. - /// On success, returns the number of bytes written. Note that the operating - /// system may refuse buffers larger than 65507. However, partial writes are - /// not possible until buffer sizes above `i32::MAX`. - /// - /// [`UdpSocket::connect`] will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// socket.send(&[0, 1, 2]).expect("couldn't send message"); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn send(&self, buf: &[u8]) -> io::Result { - self.0.send(buf) - } - - /// Receives a single datagram message on the socket from the remote address to - /// which it is connected. On success, returns the number of bytes read. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// [`UdpSocket::connect`] will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// let mut buf = [0; 10]; - /// match socket.recv(&mut buf) { - /// Ok(received) => println!("received {received} bytes {:?}", &buf[..received]), - /// Err(e) => println!("recv function failed: {e:?}"), - /// } - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.0.recv(buf) - } - - /// Receives single datagram on the socket from the remote address to which it is - /// connected, without removing the message from input queue. On success, returns - /// the number of bytes peeked. - /// - /// The function must be called with valid byte array `buf` of sufficient size to - /// hold the message bytes. If a message is too long to fit in the supplied buffer, - /// excess bytes may be discarded. - /// - /// Successive calls return the same data. This is accomplished by passing - /// `MSG_PEEK` as a flag to the underlying `recv` system call. - /// - /// Do not use this function to implement busy waiting, instead use `libc::poll` to - /// synchronize IO events on one or more sockets. - /// - /// [`UdpSocket::connect`] will connect this socket to a remote address. This - /// method will fail if the socket is not connected. - /// - /// # Errors - /// - /// This method will fail if the socket is not connected. The `connect` method - /// will connect this socket to a remote address. - /// - /// # Examples - /// - /// ```no_run - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); - /// socket.connect("127.0.0.1:8080").expect("connect function failed"); - /// let mut buf = [0; 10]; - /// match socket.peek(&mut buf) { - /// Ok(received) => println!("received {received} bytes"), - /// Err(e) => println!("peek function failed: {e:?}"), - /// } - /// ``` - #[stable(feature = "peek", since = "1.18.0")] - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.0.peek(buf) - } - - /// Moves this UDP socket into or out of nonblocking mode. - /// - /// This will result in `recv`, `recv_from`, `send`, and `send_to` system - /// operations becoming nonblocking, i.e., immediately returning from their - /// calls. If the IO operation is successful, `Ok` is returned and no - /// further action is required. If the IO operation could not be completed - /// and needs to be retried, an error with kind - /// [`io::ErrorKind::WouldBlock`] is returned. - /// - /// On Unix platforms, calling this method corresponds to calling `fcntl` - /// `FIONBIO`. On Windows calling this method corresponds to calling - /// `ioctlsocket` `FIONBIO`. - /// - /// # Examples - /// - /// Creates a UDP socket bound to `127.0.0.1:7878` and read bytes in - /// nonblocking mode: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// - /// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap(); - /// socket.set_nonblocking(true).unwrap(); - /// - /// # fn wait_for_fd() { unimplemented!() } - /// let mut buf = [0; 10]; - /// let (num_bytes_read, _) = loop { - /// match socket.recv_from(&mut buf) { - /// Ok(n) => break n, - /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - /// // wait until network socket is ready, typically implemented - /// // via platform-specific APIs such as epoll or IOCP - /// wait_for_fd(); - /// } - /// Err(e) => panic!("encountered IO error: {e}"), - /// } - /// }; - /// println!("bytes: {:?}", &buf[..num_bytes_read]); - /// ``` - #[stable(feature = "net2_mutators", since = "1.9.0")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } -} - -// In addition to the `impl`s here, `UdpSocket` also has `impl`s for -// `AsFd`/`From`/`Into` and -// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and -// `AsSocket`/`From`/`Into` and -// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. - -impl AsInner for UdpSocket { - #[inline] - fn as_inner(&self) -> &net_imp::UdpSocket { - &self.0 - } -} - -impl FromInner for UdpSocket { - fn from_inner(inner: net_imp::UdpSocket) -> UdpSocket { - UdpSocket(inner) - } -} - -impl IntoInner for UdpSocket { - fn into_inner(self) -> net_imp::UdpSocket { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} diff --git a/crates/std/src/net/udp/tests.rs b/crates/std/src/net/udp/tests.rs deleted file mode 100644 index 0638b36..0000000 --- a/crates/std/src/net/udp/tests.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::io::ErrorKind; -use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6}; -use crate::net::*; -use crate::sync::mpsc::channel; -use crate::thread; -use crate::time::{Duration, Instant}; - -fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) { - f(next_test_ip4(), next_test_ip4()); - f(next_test_ip6(), next_test_ip6()); -} - -macro_rules! t { - ($e:expr) => { - match $e { - Ok(t) => t, - Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), - } - }; -} - -#[test] -fn bind_error() { - match UdpSocket::bind("1.1.1.1:9999") { - Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable), - } -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn socket_smoke_test_ip4() { - each_ip(&mut |server_ip, client_ip| { - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - - let _t = thread::spawn(move || { - let client = t!(UdpSocket::bind(&client_ip)); - rx1.recv().unwrap(); - t!(client.send_to(&[99], &server_ip)); - tx2.send(()).unwrap(); - }); - - let server = t!(UdpSocket::bind(&server_ip)); - tx1.send(()).unwrap(); - let mut buf = [0]; - let (nread, src) = t!(server.recv_from(&mut buf)); - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(compare_ignore_zoneid(&src, &client_ip), true); - rx2.recv().unwrap(); - }) -} - -#[test] -fn socket_name() { - each_ip(&mut |addr, _| { - let server = t!(UdpSocket::bind(&addr)); - assert_eq!(addr, t!(server.local_addr())); - }) -} - -#[test] -fn socket_peer() { - each_ip(&mut |addr1, addr2| { - let server = t!(UdpSocket::bind(&addr1)); - assert_eq!(server.peer_addr().unwrap_err().kind(), ErrorKind::NotConnected); - t!(server.connect(&addr2)); - assert_eq!(addr2, t!(server.peer_addr())); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn udp_clone_smoke() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - - let _t = thread::spawn(move || { - let mut buf = [0, 0]; - let res = sock2.recv_from(&mut buf).unwrap(); - assert_eq!(res.0, 1); - assert_eq!(compare_ignore_zoneid(&res.1, &addr1), true); - assert_eq!(buf[0], 1); - t!(sock2.send_to(&[2], &addr1)); - }); - - let sock3 = t!(sock1.try_clone()); - - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - rx1.recv().unwrap(); - t!(sock3.send_to(&[1], &addr2)); - tx2.send(()).unwrap(); - }); - tx1.send(()).unwrap(); - let mut buf = [0, 0]; - let res = sock1.recv_from(&mut buf).unwrap(); - assert_eq!(res.0, 1); - assert_eq!(compare_ignore_zoneid(&res.1, &addr2), true); - rx2.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn udp_clone_two_read() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - let (tx1, rx) = channel(); - let tx2 = tx1.clone(); - - let _t = thread::spawn(move || { - t!(sock2.send_to(&[1], &addr1)); - rx.recv().unwrap(); - t!(sock2.send_to(&[2], &addr1)); - rx.recv().unwrap(); - }); - - let sock3 = t!(sock1.try_clone()); - - let (done, rx) = channel(); - let _t = thread::spawn(move || { - let mut buf = [0, 0]; - t!(sock3.recv_from(&mut buf)); - tx2.send(()).unwrap(); - done.send(()).unwrap(); - }); - let mut buf = [0, 0]; - t!(sock1.recv_from(&mut buf)); - tx1.send(()).unwrap(); - - rx.recv().unwrap(); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // no threads -fn udp_clone_two_write() { - each_ip(&mut |addr1, addr2| { - let sock1 = t!(UdpSocket::bind(&addr1)); - let sock2 = t!(UdpSocket::bind(&addr2)); - - let (tx, rx) = channel(); - let (serv_tx, serv_rx) = channel(); - - let _t = thread::spawn(move || { - let mut buf = [0, 1]; - rx.recv().unwrap(); - t!(sock2.recv_from(&mut buf)); - serv_tx.send(()).unwrap(); - }); - - let sock3 = t!(sock1.try_clone()); - - let (done, rx) = channel(); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - if sock3.send_to(&[1], &addr2).is_ok() { - let _ = tx2.send(()); - } - done.send(()).unwrap(); - }); - if sock1.send_to(&[2], &addr2).is_ok() { - let _ = tx.send(()); - } - drop(tx); - - rx.recv().unwrap(); - serv_rx.recv().unwrap(); - }) -} - -#[test] -fn debug() { - let name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - - let udpsock = t!(UdpSocket::bind(&socket_addr)); - let udpsock_inner = udpsock.0.socket().as_raw(); - let compare = format!("UdpSocket {{ addr: {socket_addr:?}, {name}: {udpsock_inner:?} }}"); - assert_eq!(format!("{udpsock:?}"), compare); -} - -// FIXME: re-enabled openbsd/netbsd tests once their socket timeout code -// no longer has rounding errors. -// VxWorks ignores SO_SNDTIMEO. -#[cfg_attr( - any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), - ignore -)] -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -#[test] -fn timeouts() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - let dur = Duration::new(15410, 0); - - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_read_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.read_timeout())); - - assert_eq!(None, t!(stream.write_timeout())); - - t!(stream.set_write_timeout(Some(dur))); - assert_eq!(Some(dur), t!(stream.write_timeout())); - - t!(stream.set_read_timeout(None)); - assert_eq!(None, t!(stream.read_timeout())); - - t!(stream.set_write_timeout(None)); - assert_eq!(None, t!(stream.write_timeout())); -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -fn test_read_timeout() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - let mut buf = [0; 10]; - - let start = Instant::now(); - loop { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - if kind != ErrorKind::Interrupted { - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - break; - } - } - assert!(start.elapsed() > Duration::from_millis(400)); -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported -fn test_read_with_timeout() { - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); - - t!(stream.send_to(b"hello world", &addr)); - - let mut buf = [0; 11]; - t!(stream.recv_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - - let start = Instant::now(); - loop { - let kind = stream.recv_from(&mut buf).err().expect("expected error").kind(); - if kind != ErrorKind::Interrupted { - assert!( - kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, - "unexpected_error: {:?}", - kind - ); - break; - } - } - assert!(start.elapsed() > Duration::from_millis(400)); -} - -// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors -// when passed zero Durations -#[test] -fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let socket = t!(UdpSocket::bind(&addr)); - - let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); - - let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - let err = result.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::InvalidInput); -} - -#[test] -fn connect_send_recv() { - let addr = next_test_ip4(); - - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.connect(addr)); - - t!(socket.send(b"hello world")); - - let mut buf = [0; 11]; - t!(socket.recv(&mut buf)); - assert_eq!(b"hello world", &buf[..]); -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // peek not supported -fn connect_send_peek_recv() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.connect(addr)); - - t!(socket.send(b"hello world")); - - for _ in 1..3 { - let mut buf = [0; 11]; - let size = t!(socket.peek(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - } - - let mut buf = [0; 11]; - let size = t!(socket.recv(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - }) -} - -#[test] -#[cfg_attr(target_os = "wasi", ignore)] // peek_from not supported -fn peek_from() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - t!(socket.send_to(b"hello world", &addr)); - - for _ in 1..3 { - let mut buf = [0; 11]; - let (size, _) = t!(socket.peek_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - } - - let mut buf = [0; 11]; - let (size, _) = t!(socket.recv_from(&mut buf)); - assert_eq!(b"hello world", &buf[..]); - assert_eq!(size, 11); - }) -} - -#[test] -fn ttl() { - let ttl = 100; - - let addr = next_test_ip4(); - - let stream = t!(UdpSocket::bind(&addr)); - - t!(stream.set_ttl(ttl)); - assert_eq!(ttl, t!(stream.ttl())); -} - -#[test] -fn set_nonblocking() { - each_ip(&mut |addr, _| { - let socket = t!(UdpSocket::bind(&addr)); - - t!(socket.set_nonblocking(true)); - t!(socket.set_nonblocking(false)); - - t!(socket.connect(addr)); - - t!(socket.set_nonblocking(false)); - t!(socket.set_nonblocking(true)); - - let mut buf = [0]; - match socket.recv(&mut buf) { - Ok(_) => panic!("expected error"), - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => panic!("unexpected error {e}"), - } - }) -} diff --git a/crates/std/src/num/f128.rs b/crates/std/src/num/f128.rs deleted file mode 100644 index 2c8898a..0000000 --- a/crates/std/src/num/f128.rs +++ /dev/null @@ -1,1086 +0,0 @@ -//! Constants for the `f128` quadruple-precision floating point type. -//! -//! *[See also the `f128` primitive type](primitive@f128).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. - -#![unstable(feature = "f128", issue = "116909")] -#![doc(test(attr(feature(cfg_target_has_reliable_f16_f128), expect(internal_features))))] - -#[unstable(feature = "f128", issue = "116909")] -pub use core::f128::consts; - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[cfg(not(test))] -#[doc(test(attr(allow(unused_features))))] -impl f128 { - /// Raises a number to a floating point power. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f128::powf(f128::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0_f128; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// assert!(abs_difference <= f128::EPSILON); - /// - /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); - /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); - /// assert_eq!(f128::powf(0.0, 0.0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powf(self, n: f128) -> f128 { - intrinsics::powf128(self, n) - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let one = 1.0f128; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp(self) -> f128 { - intrinsics::expf128(self) - } - - /// Returns `2^(self)`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 2.0f128; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp2(self) -> f128 { - intrinsics::exp2f128(self) - } - - /// Returns the natural logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let one = 1.0f128; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// assert_eq!(0_f128.ln(), f128::NEG_INFINITY); - /// assert!((-42_f128).ln().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ln(self) -> f128 { - intrinsics::logf128(self) - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// The result might not be correctly rounded owing to implementation details; - /// `self.log2()` can produce more accurate results for base 2, and - /// `self.log10()` can produce more accurate results for base 10. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let five = 5.0f128; - /// - /// // log5(5) - 1 == 0 - /// let abs_difference = (five.log(5.0) - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// assert_eq!(0_f128.log(10.0), f128::NEG_INFINITY); - /// assert!((-42_f128).log(10.0).is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log(self, base: f128) -> f128 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let two = 2.0f128; - /// - /// // log2(2) - 1 == 0 - /// let abs_difference = (two.log2() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// assert_eq!(0_f128.log2(), f128::NEG_INFINITY); - /// assert!((-42_f128).log2().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log2(self) -> f128 { - intrinsics::log2f128(self) - } - - /// Returns the base 10 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let ten = 10.0f128; - /// - /// // log10(10) - 1 == 0 - /// let abs_difference = (ten.log10() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// assert_eq!(0_f128.log10(), f128::NEG_INFINITY); - /// assert!((-42_f128).log10().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log10(self) -> f128 { - intrinsics::log10f128(self) - } - - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// - /// This function currently corresponds to the `cbrtf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 8.0f128; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cbrt(self) -> f128 { - cmath::cbrtf128(self) - } - - /// Compute the distance between the origin and a point (`x`, `y`) on the - /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a - /// right-angle triangle with other sides having length `x.abs()` and - /// `y.abs()`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// - /// This function currently corresponds to the `hypotf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0f128; - /// let y = 3.0f128; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn hypot(self, other: f128) -> f128 { - cmath::hypotf128(self, other) - } - - /// Computes the sine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = std::f128::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sin(self) -> f128 { - intrinsics::sinf128(self) - } - - /// Computes the cosine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0 * std::f128::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cos(self) -> f128 { - intrinsics::cosf128(self) - } - - /// Computes the tangent of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tanf128` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = std::f128::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn tan(self) -> f128 { - cmath::tanf128(self) - } - - /// Computes the arcsine of a number. Return value is in radians in - /// the range [-pi/2, pi/2] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `asinf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = std::f128::consts::FRAC_PI_4; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arcsin")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn asin(self) -> f128 { - cmath::asinf128(self) - } - - /// Computes the arccosine of a number. Return value is in radians in - /// the range [0, pi] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `acosf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = std::f128::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arccos")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn acos(self) -> f128 { - cmath::acosf128(self) - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `atanf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let f = 1.0f128; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arctan")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atan(self) -> f128 { - cmath::atanf128(self) - } - - /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. - /// - /// | `x` | `y` | Piecewise Definition | Range | - /// |---------|---------|----------------------|---------------| - /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | - /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | - /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| - /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `atan2f128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0f128; - /// let y1 = -3.0f128; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0f128; - /// let y2 = 3.0f128; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 <= f128::EPSILON); - /// assert!(abs_difference_2 <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atan2(self, other: f128) -> f128 { - cmath::atan2f128(self, other) - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `(f128::sin(x), - /// f128::cos(x))`. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = std::f128::consts::FRAC_PI_4; - /// let f = x.sin_cos(); - /// - /// let abs_difference_0 = (f.0 - x.sin()).abs(); - /// let abs_difference_1 = (f.1 - x.cos()).abs(); - /// - /// assert!(abs_difference_0 <= f128::EPSILON); - /// assert!(abs_difference_1 <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "sincos")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - pub fn sin_cos(self) -> (f128, f128) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `expm1f128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 1e-8_f128; - /// - /// // for very small x, e^x is approximately 1 + x + x^2 / 2 - /// let approx = x + x * x / 2.0; - /// let abs_difference = (x.exp_m1() - approx).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp_m1(self) -> f128 { - cmath::expm1f128(self) - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `log1pf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 1e-8_f128; - /// - /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 - /// let approx = x - x * x / 2.0; - /// let abs_difference = (x.ln_1p() - approx).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// # } - /// ``` - /// - /// Out-of-range values: - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// assert_eq!((-1.0_f128).ln_1p(), f128::NEG_INFINITY); - /// assert!((-2.0_f128).ln_1p().is_nan()); - /// # } - /// ``` - #[inline] - #[doc(alias = "log1p")] - #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - pub fn ln_1p(self) -> f128 { - cmath::log1pf128(self) - } - - /// Hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `sinhf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let e = std::f128::consts::E; - /// let x = 1.0f128; - /// - /// let f = x.sinh(); - /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` - /// let g = ((e * e) - 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sinh(self) -> f128 { - cmath::sinhf128(self) - } - - /// Hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `coshf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let e = std::f128::consts::E; - /// let x = 1.0f128; - /// let f = x.cosh(); - /// // Solving cosh() at 1 gives this result - /// let g = ((e * e) + 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// // Same result - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cosh(self) -> f128 { - cmath::coshf128(self) - } - - /// Hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tanhf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let e = std::f128::consts::E; - /// let x = 1.0f128; - /// - /// let f = x.tanh(); - /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` - /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn tanh(self) -> f128 { - cmath::tanhf128(self) - } - - /// Inverse hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 1.0f128; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arcsinh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn asinh(self) -> f128 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 1.0f128; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arccosh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn acosh(self) -> f128 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = std::f128::consts::FRAC_PI_6; - /// let f = x.tanh().atanh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// # } - /// ``` - #[inline] - #[doc(alias = "arctanh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atanh(self) -> f128 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tgammaf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// #![feature(float_gamma)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 5.0f128; - /// - /// let abs_difference = (x.gamma() - 24.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_gamma", issue = "99842")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn gamma(self) -> f128 { - cmath::tgammaf128(self) - } - - /// Natural logarithm of the absolute value of the gamma function - /// - /// The integer part of the tuple indicates the sign of the gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `lgammaf128_r` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// #![feature(float_gamma)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// - /// let x = 2.0f128; - /// - /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_gamma", issue = "99842")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ln_gamma(self) -> (f128, i32) { - let mut signgamp: i32 = 0; - let x = cmath::lgammaf128_r(self, &mut signgamp); - (x, signgamp) - } - - /// Error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erff128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// #![feature(float_erf)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// /// The error function relates what percent of a normal distribution lies - /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). - /// fn within_standard_deviations(x: f128) -> f128 { - /// (x * std::f128::consts::FRAC_1_SQRT_2).erf() * 100.0 - /// } - /// - /// // 68% of a normal distribution is within one standard deviation - /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); - /// // 95% of a normal distribution is within two standard deviations - /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); - /// // 99.7% of a normal distribution is within three standard deviations - /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); - /// # } - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erf(self) -> f128 { - cmath::erff128(self) - } - - /// Complementary error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erfcf128` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f128)] - /// #![feature(float_erf)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f128_math)] { - /// let x: f128 = 0.123; - /// - /// let one = x.erf() + x.erfc(); - /// let abs_difference = (one - 1.0).abs(); - /// - /// assert!(abs_difference <= f128::EPSILON); - /// # } - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erfc(self) -> f128 { - cmath::erfcf128(self) - } -} diff --git a/crates/std/src/num/f16.rs b/crates/std/src/num/f16.rs deleted file mode 100644 index 7ca266c..0000000 --- a/crates/std/src/num/f16.rs +++ /dev/null @@ -1,1047 +0,0 @@ -//! Constants for the `f16` half-precision floating point type. -//! -//! *[See also the `f16` primitive type](primitive@f16).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. - -#![unstable(feature = "f16", issue = "116909")] -#![doc(test(attr(feature(cfg_target_has_reliable_f16_f128), expect(internal_features))))] - -#[unstable(feature = "f16", issue = "116909")] -pub use core::f16::consts; - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[cfg(not(test))] -#[doc(test(attr(allow(unused_features))))] -impl f16 { - /// Raises a number to a floating point power. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f16::powf(f16::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0_f16; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// assert!(abs_difference <= f16::EPSILON); - /// - /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); - /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); - /// assert_eq!(f16::powf(0.0, 0.0), 1.0); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powf(self, n: f16) -> f16 { - intrinsics::powf16(self, n) - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let one = 1.0f16; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp(self) -> f16 { - intrinsics::expf16(self) - } - - /// Returns `2^(self)`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 2.0f16; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp2(self) -> f16 { - intrinsics::exp2f16(self) - } - - /// Returns the natural logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let one = 1.0f16; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// assert_eq!(0_f16.ln(), f16::NEG_INFINITY); - /// assert!((-42_f16).ln().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ln(self) -> f16 { - intrinsics::logf16(self) - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// The result might not be correctly rounded owing to implementation details; - /// `self.log2()` can produce more accurate results for base 2, and - /// `self.log10()` can produce more accurate results for base 10. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let five = 5.0f16; - /// - /// // log5(5) - 1 == 0 - /// let abs_difference = (five.log(5.0) - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// assert_eq!(0_f16.log(10.0), f16::NEG_INFINITY); - /// assert!((-42_f16).log(10.0).is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log(self, base: f16) -> f16 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let two = 2.0f16; - /// - /// // log2(2) - 1 == 0 - /// let abs_difference = (two.log2() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// assert_eq!(0_f16.log2(), f16::NEG_INFINITY); - /// assert!((-42_f16).log2().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log2(self) -> f16 { - intrinsics::log2f16(self) - } - - /// Returns the base 10 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let ten = 10.0f16; - /// - /// // log10(10) - 1 == 0 - /// let abs_difference = (ten.log10() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - /// - /// Non-positive values: - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// assert_eq!(0_f16.log10(), f16::NEG_INFINITY); - /// assert!((-42_f16).log10().is_nan()); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn log10(self) -> f16 { - intrinsics::log10f16(self) - } - - /// Compute the distance between the origin and a point (`x`, `y`) on the - /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a - /// right-angle triangle with other sides having length `x.abs()` and - /// `y.abs()`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `hypotf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0f16; - /// let y = 3.0f16; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn hypot(self, other: f16) -> f16 { - cmath::hypotf(self as f32, other as f32) as f16 - } - - /// Computes the sine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = std::f16::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sin(self) -> f16 { - intrinsics::sinf16(self) - } - - /// Computes the cosine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0 * std::f16::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cos(self) -> f16 { - intrinsics::cosf16(self) - } - - /// Computes the tangent of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tanf` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = std::f16::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn tan(self) -> f16 { - cmath::tanf(self as f32) as f16 - } - - /// Computes the arcsine of a number. Return value is in radians in - /// the range [-pi/2, pi/2] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `asinf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = std::f16::consts::FRAC_PI_4; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arcsin")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn asin(self) -> f16 { - cmath::asinf(self as f32) as f16 - } - - /// Computes the arccosine of a number. Return value is in radians in - /// the range [0, pi] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `acosf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = std::f16::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arccos")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn acos(self) -> f16 { - cmath::acosf(self as f32) as f16 - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `atanf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let f = 1.0f16; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arctan")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atan(self) -> f16 { - cmath::atanf(self as f32) as f16 - } - - /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. - /// - /// | `x` | `y` | Piecewise Definition | Range | - /// |---------|---------|----------------------|---------------| - /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | - /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | - /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| - /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `atan2f` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0f16; - /// let y1 = -3.0f16; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0f16; - /// let y2 = 3.0f16; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 <= f16::EPSILON); - /// assert!(abs_difference_2 <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atan2(self, other: f16) -> f16 { - cmath::atan2f(self as f32, other as f32) as f16 - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `(f16::sin(x), - /// f16::cos(x))`. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = std::f16::consts::FRAC_PI_4; - /// let f = x.sin_cos(); - /// - /// let abs_difference_0 = (f.0 - x.sin()).abs(); - /// let abs_difference_1 = (f.1 - x.cos()).abs(); - /// - /// assert!(abs_difference_0 <= f16::EPSILON); - /// assert!(abs_difference_1 <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "sincos")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - pub fn sin_cos(self) -> (f16, f16) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `expm1f` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 1e-4_f16; - /// - /// // for very small x, e^x is approximately 1 + x + x^2 / 2 - /// let approx = x + x * x / 2.0; - /// let abs_difference = (x.exp_m1() - approx).abs(); - /// - /// assert!(abs_difference < 1e-4); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn exp_m1(self) -> f16 { - cmath::expm1f(self as f32) as f16 - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `log1pf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 1e-4_f16; - /// - /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 - /// let approx = x - x * x / 2.0; - /// let abs_difference = (x.ln_1p() - approx).abs(); - /// - /// assert!(abs_difference < 1e-4); - /// # } - /// ``` - /// - /// Out-of-range values: - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// assert_eq!((-1.0_f16).ln_1p(), f16::NEG_INFINITY); - /// assert!((-2.0_f16).ln_1p().is_nan()); - /// # } - /// ``` - #[inline] - #[doc(alias = "log1p")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ln_1p(self) -> f16 { - cmath::log1pf(self as f32) as f16 - } - - /// Hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `sinhf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let e = std::f16::consts::E; - /// let x = 1.0f16; - /// - /// let f = x.sinh(); - /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` - /// let g = ((e * e) - 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn sinh(self) -> f16 { - cmath::sinhf(self as f32) as f16 - } - - /// Hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `coshf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let e = std::f16::consts::E; - /// let x = 1.0f16; - /// let f = x.cosh(); - /// // Solving cosh() at 1 gives this result - /// let g = ((e * e) + 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// // Same result - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn cosh(self) -> f16 { - cmath::coshf(self as f32) as f16 - } - - /// Hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tanhf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let e = std::f16::consts::E; - /// let x = 1.0f16; - /// - /// let f = x.tanh(); - /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` - /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn tanh(self) -> f16 { - cmath::tanhf(self as f32) as f16 - } - - /// Inverse hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 1.0f16; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arcsinh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn asinh(self) -> f16 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 1.0f16; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[doc(alias = "arccosh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn acosh(self) -> f16 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = std::f16::consts::FRAC_PI_6; - /// let f = x.tanh().atanh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= 0.01); - /// # } - /// ``` - #[inline] - #[doc(alias = "arctanh")] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn atanh(self) -> f16 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `tgammaf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 5.0f16; - /// - /// let abs_difference = (x.gamma() - 24.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_gamma", issue = "99842")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn gamma(self) -> f16 { - cmath::tgammaf(self as f32) as f16 - } - - /// Natural logarithm of the absolute value of the gamma function - /// - /// The integer part of the tuple indicates the sign of the gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `lgamma_r` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// - /// let x = 2.0f16; - /// - /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[inline] - #[rustc_allow_incoherent_impl] - #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_gamma", issue = "99842")] - #[must_use = "method returns a new number and does not mutate the original value"] - pub fn ln_gamma(self) -> (f16, i32) { - let mut signgamp: i32 = 0; - let x = cmath::lgammaf_r(self as f32, &mut signgamp) as f16; - (x, signgamp) - } - - /// Error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erff` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// /// The error function relates what percent of a normal distribution lies - /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). - /// fn within_standard_deviations(x: f16) -> f16 { - /// (x * std::f16::consts::FRAC_1_SQRT_2).erf() * 100.0 - /// } - /// - /// // 68% of a normal distribution is within one standard deviation - /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.1); - /// // 95% of a normal distribution is within two standard deviations - /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.1); - /// // 99.7% of a normal distribution is within three standard deviations - /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.1); - /// # } - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erf(self) -> f16 { - cmath::erff(self as f32) as f16 - } - - /// Complementary error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erfcf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(f16)] - /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { - /// let x: f16 = 0.123; - /// - /// let one = x.erf() + x.erfc(); - /// let abs_difference = (one - 1.0).abs(); - /// - /// assert!(abs_difference <= f16::EPSILON); - /// # } - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erfc(self) -> f16 { - cmath::erfcf(self as f32) as f16 - } -} diff --git a/crates/std/src/num/f32.rs b/crates/std/src/num/f32.rs deleted file mode 100644 index 77e6824..0000000 --- a/crates/std/src/num/f32.rs +++ /dev/null @@ -1,1276 +0,0 @@ -//! Constants for the `f32` single-precision floating point type. -//! -//! *[See also the `f32` primitive type](primitive@f32).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! For the constants defined directly in this module -//! (as distinct from those defined in the `consts` sub-module), -//! new code should instead use the associated constants -//! defined directly on the `f32` type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future)] -pub use core::f32::{ - DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, - MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, -}; - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[cfg(not(test))] -impl f32 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f32; - /// let g = 3.0_f32; - /// let h = -3.7_f32; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn floor(self) -> f32 { - core::f32::math::floor(self) - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.01_f32; - /// let g = 4.0_f32; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// ``` - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn ceil(self) -> f32 { - core::f32::math::ceil(self) - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f32; - /// let g = -3.3_f32; - /// let h = -3.7_f32; - /// let i = 3.5_f32; - /// let j = 4.5_f32; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn round(self) -> f32 { - core::f32::math::round(self) - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f32; - /// let g = -3.3_f32; - /// let h = 3.5_f32; - /// let i = 4.5_f32; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "round_ties_even", since = "1.77.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn round_ties_even(self) -> f32 { - core::f32::math::round_ties_even(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f32; - /// let g = 3.0_f32; - /// let h = -3.7_f32; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// ``` - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn trunc(self) -> f32 { - core::f32::math::trunc(self) - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let x = 3.6_f32; - /// let y = -3.6_f32; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x <= f32::EPSILON); - /// assert!(abs_difference_y <= f32::EPSILON); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn fract(self) -> f32 { - core::f32::math::fract(self) - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// let m = 10.0_f32; - /// let x = 4.0_f32; - /// let b = 60.0_f32; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f32 + f32::EPSILON; - /// let one_minus_eps = 1.0_f32 - f32::EPSILON; - /// let minus_one = -1.0_f32; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f32::EPSILON * f32::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[rustc_const_stable(feature = "const_mul_add", since = "1.94.0")] - pub const fn mul_add(self, a: f32, b: f32) -> f32 { - core::f32::math::mul_add(self, a, b) - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// let a: f32 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn div_euclid(self, rhs: f32) -> f32 { - core::f32::math::div_euclid(self, rhs) - } - - /// Calculates the least nonnegative remainder of `self` when divided by - /// `rhs`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// let a: f32 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f32::EPSILON).rem_euclid(3.0) != 0.0); - /// ``` - #[doc(alias = "modulo", alias = "mod")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn rem_euclid(self, rhs: f32) -> f32 { - core::f32::math::rem_euclid(self, rhs) - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f32::powi(f32::NAN, 0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f32; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= 1e-5); - /// - /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); - /// assert_eq!(f32::powi(0.0, 0), 1.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powi(self, n: i32) -> f32 { - core::f32::math::powi(self, n) - } - - /// Raises a number to a floating point power. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f32::powf(f32::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f32; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// assert!(abs_difference <= 1e-5); - /// - /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); - /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); - /// assert_eq!(f32::powf(0.0, 0.0), 1.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powf(self, n: f32) -> f32 { - intrinsics::powf32(self, n) - } - - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// let positive = 4.0_f32; - /// let negative = -4.0_f32; - /// let negative_zero = -0.0_f32; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// ``` - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sqrt(self) -> f32 { - core::f32::math::sqrt(self) - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0f32; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp(self) -> f32 { - intrinsics::expf32(self) - } - - /// Returns `2^(self)`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let f = 2.0f32; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp2(self) -> f32 { - intrinsics::exp2f32(self) - } - - /// Returns the natural logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0f32; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f32.ln(), f32::NEG_INFINITY); - /// assert!((-42_f32).ln().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln(self) -> f32 { - intrinsics::logf32(self) - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// The result might not be correctly rounded owing to implementation details; - /// `self.log2()` can produce more accurate results for base 2, and - /// `self.log10()` can produce more accurate results for base 10. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let five = 5.0f32; - /// - /// // log5(5) - 1 == 0 - /// let abs_difference = (five.log(5.0) - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f32.log(10.0), f32::NEG_INFINITY); - /// assert!((-42_f32).log(10.0).is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log(self, base: f32) -> f32 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let two = 2.0f32; - /// - /// // log2(2) - 1 == 0 - /// let abs_difference = (two.log2() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f32.log2(), f32::NEG_INFINITY); - /// assert!((-42_f32).log2().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log2(self) -> f32 { - intrinsics::log2f32(self) - } - - /// Returns the base 10 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let ten = 10.0f32; - /// - /// // log10(10) - 1 == 0 - /// let abs_difference = (ten.log10() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f32.log10(), f32::NEG_INFINITY); - /// assert!((-42_f32).log10().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log10(self) -> f32 { - intrinsics::log10f32(self) - } - - /// The positive difference of two numbers. - /// - /// * If `self <= other`: `0.0` - /// * Else: `self - other` - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `fdimf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 3.0f32; - /// let y = -3.0f32; - /// - /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); - /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); - /// - /// assert!(abs_difference_x <= 1e-6); - /// assert!(abs_difference_y <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[deprecated( - since = "1.10.0", - note = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` \ - except that `abs_sub` also propagates NaNs (also \ - known as `fdimf` in C). If you truly need the positive \ - difference, consider using that expression or the C function \ - `fdimf`, depending on how you wish to handle NaN (please consider \ - filing an issue describing your use-case too)." - )] - pub fn abs_sub(self, other: f32) -> f32 { - #[allow(deprecated)] - core::f32::math::abs_sub(self, other) - } - - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `cbrtf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 8.0f32; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cbrt(self) -> f32 { - core::f32::math::cbrt(self) - } - - /// Compute the distance between the origin and a point (`x`, `y`) on the - /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a - /// right-angle triangle with other sides having length `x.abs()` and - /// `y.abs()`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `hypotf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0f32; - /// let y = 3.0f32; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn hypot(self, other: f32) -> f32 { - cmath::hypotf(self, other) - } - - /// Computes the sine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin(self) -> f32 { - intrinsics::sinf32(self) - } - - /// Computes the cosine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0 * std::f32::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cos(self) -> f32 { - intrinsics::cosf32(self) - } - - /// Computes the tangent of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tanf` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tan(self) -> f32 { - cmath::tanf(self) - } - - /// Computes the arcsine of a number. Return value is in radians in - /// the range [-pi/2, pi/2] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `asinf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = std::f32::consts::FRAC_PI_4; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[doc(alias = "arcsin")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asin(self) -> f32 { - cmath::asinf(self) - } - - /// Computes the arccosine of a number. Return value is in radians in - /// the range [0, pi] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `acosf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = std::f32::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f32::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[doc(alias = "arccos")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acos(self) -> f32 { - cmath::acosf(self) - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `atanf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = 1.0f32; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[doc(alias = "arctan")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan(self) -> f32 { - cmath::atanf(self) - } - - /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. - /// - /// | `x` | `y` | Piecewise Definition | Range | - /// |---------|---------|----------------------|---------------| - /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | - /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | - /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| - /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `atan2f` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0f32; - /// let y1 = -3.0f32; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0f32; - /// let y2 = 3.0f32; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f32::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f32::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 <= 1e-5); - /// assert!(abs_difference_2 <= 1e-5); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan2(self, other: f32) -> f32 { - cmath::atan2f(self, other) - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `(f32::sin(x), - /// f32::cos(x))`. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_4; - /// let f = x.sin_cos(); - /// - /// let abs_difference_0 = (f.0 - x.sin()).abs(); - /// let abs_difference_1 = (f.1 - x.cos()).abs(); - /// - /// assert!(abs_difference_0 <= 1e-4); - /// assert!(abs_difference_1 <= 1e-4); - /// ``` - #[doc(alias = "sincos")] - #[rustc_allow_incoherent_impl] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin_cos(self) -> (f32, f32) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `expm1f` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 1e-8_f32; - /// - /// // for very small x, e^x is approximately 1 + x + x^2 / 2 - /// let approx = x + x * x / 2.0; - /// let abs_difference = (x.exp_m1() - approx).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp_m1(self) -> f32 { - cmath::expm1f(self) - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `log1pf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 1e-8_f32; - /// - /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 - /// let approx = x - x * x / 2.0; - /// let abs_difference = (x.ln_1p() - approx).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - /// - /// Out-of-range values: - /// ``` - /// assert_eq!((-1.0_f32).ln_1p(), f32::NEG_INFINITY); - /// assert!((-2.0_f32).ln_1p().is_nan()); - /// ``` - #[doc(alias = "log1p")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln_1p(self) -> f32 { - cmath::log1pf(self) - } - - /// Hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `sinhf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// - /// let f = x.sinh(); - /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` - /// let g = ((e * e) - 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sinh(self) -> f32 { - cmath::sinhf(self) - } - - /// Hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `coshf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// let f = x.cosh(); - /// // Solving cosh() at 1 gives this result - /// let g = ((e * e) + 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// // Same result - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cosh(self) -> f32 { - cmath::coshf(self) - } - - /// Hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tanhf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f32::consts::E; - /// let x = 1.0f32; - /// - /// let f = x.tanh(); - /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` - /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tanh(self) -> f32 { - cmath::tanhf(self) - } - - /// Inverse hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0f32; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[doc(alias = "arcsinh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asinh(self) -> f32 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0f32; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[doc(alias = "arccosh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acosh(self) -> f32 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = std::f32::consts::FRAC_PI_6; - /// let f = x.tanh().atanh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// ``` - #[doc(alias = "arctanh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atanh(self) -> f32 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tgammaf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_gamma)] - /// let x = 5.0f32; - /// - /// let abs_difference = (x.gamma() - 24.0).abs(); - /// - /// assert!(abs_difference <= 1e-5); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_gamma", issue = "99842")] - #[inline] - pub fn gamma(self) -> f32 { - cmath::tgammaf(self) - } - - /// Natural logarithm of the absolute value of the gamma function - /// - /// The integer part of the tuple indicates the sign of the gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `lgamma_r` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_gamma)] - /// let x = 2.0f32; - /// - /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_gamma", issue = "99842")] - #[inline] - pub fn ln_gamma(self) -> (f32, i32) { - let mut signgamp: i32 = 0; - let x = cmath::lgammaf_r(self, &mut signgamp); - (x, signgamp) - } - - /// Error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erff` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_erf)] - /// /// The error function relates what percent of a normal distribution lies - /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). - /// fn within_standard_deviations(x: f32) -> f32 { - /// (x * std::f32::consts::FRAC_1_SQRT_2).erf() * 100.0 - /// } - /// - /// // 68% of a normal distribution is within one standard deviation - /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); - /// // 95% of a normal distribution is within two standard deviations - /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); - /// // 99.7% of a normal distribution is within three standard deviations - /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erf(self) -> f32 { - cmath::erff(self) - } - - /// Complementary error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erfcf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_erf)] - /// let x: f32 = 0.123; - /// - /// let one = x.erf() + x.erfc(); - /// let abs_difference = (one - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-6); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erfc(self) -> f32 { - cmath::erfcf(self) - } -} diff --git a/crates/std/src/num/f64.rs b/crates/std/src/num/f64.rs deleted file mode 100644 index e0b9948..0000000 --- a/crates/std/src/num/f64.rs +++ /dev/null @@ -1,1276 +0,0 @@ -//! Constants for the `f64` double-precision floating point type. -//! -//! *[See also the `f64` primitive type](primitive@f64).* -//! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! -//! For the constants defined directly in this module -//! (as distinct from those defined in the `consts` sub-module), -//! new code should instead use the associated constants -//! defined directly on the `f64` type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future)] -pub use core::f64::{ - DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, - MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, consts, -}; - -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - -#[cfg(not(test))] -impl f64 { - /// Returns the largest integer less than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f64; - /// let g = 3.0_f64; - /// let h = -3.7_f64; - /// - /// assert_eq!(f.floor(), 3.0); - /// assert_eq!(g.floor(), 3.0); - /// assert_eq!(h.floor(), -4.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn floor(self) -> f64 { - core::f64::math::floor(self) - } - - /// Returns the smallest integer greater than or equal to `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.01_f64; - /// let g = 4.0_f64; - /// - /// assert_eq!(f.ceil(), 4.0); - /// assert_eq!(g.ceil(), 4.0); - /// ``` - #[doc(alias = "ceiling")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn ceil(self) -> f64 { - core::f64::math::ceil(self) - } - - /// Returns the nearest integer to `self`. If a value is half-way between two - /// integers, round away from `0.0`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f64; - /// let g = -3.3_f64; - /// let h = -3.7_f64; - /// let i = 3.5_f64; - /// let j = 4.5_f64; - /// - /// assert_eq!(f.round(), 3.0); - /// assert_eq!(g.round(), -3.0); - /// assert_eq!(h.round(), -4.0); - /// assert_eq!(i.round(), 4.0); - /// assert_eq!(j.round(), 5.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn round(self) -> f64 { - core::f64::math::round(self) - } - - /// Returns the nearest integer to a number. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.3_f64; - /// let g = -3.3_f64; - /// let h = 3.5_f64; - /// let i = 4.5_f64; - /// - /// assert_eq!(f.round_ties_even(), 3.0); - /// assert_eq!(g.round_ties_even(), -3.0); - /// assert_eq!(h.round_ties_even(), 4.0); - /// assert_eq!(i.round_ties_even(), 4.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "round_ties_even", since = "1.77.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn round_ties_even(self) -> f64 { - core::f64::math::round_ties_even(self) - } - - /// Returns the integer part of `self`. - /// This means that non-integer numbers are always truncated towards zero. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let f = 3.7_f64; - /// let g = 3.0_f64; - /// let h = -3.7_f64; - /// - /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), 3.0); - /// assert_eq!(h.trunc(), -3.0); - /// ``` - #[doc(alias = "truncate")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn trunc(self) -> f64 { - core::f64::math::trunc(self) - } - - /// Returns the fractional part of `self`. - /// - /// This function always returns the precise result. - /// - /// # Examples - /// - /// ``` - /// let x = 3.6_f64; - /// let y = -3.6_f64; - /// let abs_difference_x = (x.fract() - 0.6).abs(); - /// let abs_difference_y = (y.fract() - (-0.6)).abs(); - /// - /// assert!(abs_difference_x < 1e-10); - /// assert!(abs_difference_y < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_round_methods", since = "1.90.0")] - #[inline] - pub const fn fract(self) -> f64 { - core::f64::math::fract(self) - } - - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding - /// error, yielding a more accurate result than an unfused multiply-add. - /// - /// Using `mul_add` *may* be more performant than an unfused multiply-add if - /// the target architecture has a dedicated `fma` CPU instruction. However, - /// this is not always true, and will be heavily dependant on designing - /// algorithms with specific target hardware in mind. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as - /// `fusedMultiplyAdd` and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// let m = 10.0_f64; - /// let x = 4.0_f64; - /// let b = 60.0_f64; - /// - /// assert_eq!(m.mul_add(x, b), 100.0); - /// assert_eq!(m * x + b, 100.0); - /// - /// let one_plus_eps = 1.0_f64 + f64::EPSILON; - /// let one_minus_eps = 1.0_f64 - f64::EPSILON; - /// let minus_one = -1.0_f64; - /// - /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. - /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f64::EPSILON * f64::EPSILON); - /// // Different rounding with the non-fused multiply and add. - /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[doc(alias = "fma", alias = "fusedMultiplyAdd")] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[rustc_const_stable(feature = "const_mul_add", since = "1.94.0")] - pub const fn mul_add(self, a: f64, b: f64) -> f64 { - core::f64::math::mul_add(self, a, b) - } - - /// Calculates Euclidean division, the matching method for `rem_euclid`. - /// - /// This computes the integer `n` such that - /// `self = n * rhs + self.rem_euclid(rhs)`. - /// In other words, the result is `self / rhs` rounded to the integer `n` - /// such that `self >= n * rhs`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// let a: f64 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn div_euclid(self, rhs: f64) -> f64 { - core::f64::math::div_euclid(self, rhs) - } - - /// Calculates the least nonnegative remainder of `self` when divided by - /// `rhs`. - /// - /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in - /// most cases. However, due to a floating point round-off error it can - /// result in `r == rhs.abs()`, violating the mathematical definition, if - /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. - /// This result is not an element of the function's codomain, but it is the - /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` - /// approximately. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. - /// - /// # Examples - /// - /// ``` - /// let a: f64 = 7.0; - /// let b = 4.0; - /// assert_eq!(a.rem_euclid(b), 3.0); - /// assert_eq!((-a).rem_euclid(b), 1.0); - /// assert_eq!(a.rem_euclid(-b), 3.0); - /// assert_eq!((-a).rem_euclid(-b), 1.0); - /// // limitation due to round-off error - /// assert!((-f64::EPSILON).rem_euclid(3.0) != 0.0); - /// ``` - #[doc(alias = "modulo", alias = "mod")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - pub fn rem_euclid(self, rhs: f64) -> f64 { - core::f64::math::rem_euclid(self, rhs) - } - - /// Raises a number to an integer power. - /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f64::powi(f64::NAN, 0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= 1e-14); - /// - /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); - /// assert_eq!(f64::powi(0.0, 0), 1.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powi(self, n: i32) -> f64 { - core::f64::math::powi(self, n) - } - - /// Raises a number to a floating point power. - /// - /// Note that this function is special in that it can return non-NaN results for NaN inputs. For - /// example, `f64::powf(f64::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* - /// NaN, then the result is non-deterministically either a NaN or the result that the - /// corresponding quiet NaN would produce. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// assert!(abs_difference <= 1e-14); - /// - /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); - /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); - /// assert_eq!(f64::powf(0.0, 0.0), 1.0); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn powf(self, n: f64) -> f64 { - intrinsics::powf64(self, n) - } - - /// Returns the square root of a number. - /// - /// Returns NaN if `self` is a negative number other than `-0.0`. - /// - /// # Precision - /// - /// The result of this operation is guaranteed to be the rounded - /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` - /// and guaranteed not to change. - /// - /// # Examples - /// - /// ``` - /// let positive = 4.0_f64; - /// let negative = -4.0_f64; - /// let negative_zero = -0.0_f64; - /// - /// assert_eq!(positive.sqrt(), 2.0); - /// assert!(negative.sqrt().is_nan()); - /// assert!(negative_zero.sqrt() == negative_zero); - /// ``` - #[doc(alias = "squareRoot")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sqrt(self) -> f64 { - core::f64::math::sqrt(self) - } - - /// Returns `e^(self)`, (the exponential function). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0_f64; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp(self) -> f64 { - intrinsics::expf64(self) - } - - /// Returns `2^(self)`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let f = 2.0_f64; - /// - /// // 2^2 - 4 == 0 - /// let abs_difference = (f.exp2() - 4.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp2(self) -> f64 { - intrinsics::exp2f64(self) - } - - /// Returns the natural logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let one = 1.0_f64; - /// // e^1 - /// let e = one.exp(); - /// - /// // ln(e) - 1 == 0 - /// let abs_difference = (e.ln() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f64.ln(), f64::NEG_INFINITY); - /// assert!((-42_f64).ln().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln(self) -> f64 { - intrinsics::logf64(self) - } - - /// Returns the logarithm of the number with respect to an arbitrary base. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// The result might not be correctly rounded owing to implementation details; - /// `self.log2()` can produce more accurate results for base 2, and - /// `self.log10()` can produce more accurate results for base 10. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let twenty_five = 25.0_f64; - /// - /// // log5(25) - 2 == 0 - /// let abs_difference = (twenty_five.log(5.0) - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f64.log(10.0), f64::NEG_INFINITY); - /// assert!((-42_f64).log(10.0).is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log(self, base: f64) -> f64 { - self.ln() / base.ln() - } - - /// Returns the base 2 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let four = 4.0_f64; - /// - /// // log2(4) - 2 == 0 - /// let abs_difference = (four.log2() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f64.log2(), f64::NEG_INFINITY); - /// assert!((-42_f64).log2().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log2(self) -> f64 { - intrinsics::log2f64(self) - } - - /// Returns the base 10 logarithm of the number. - /// - /// This returns NaN when the number is negative, and negative infinity when number is zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let hundred = 100.0_f64; - /// - /// // log10(100) - 2 == 0 - /// let abs_difference = (hundred.log10() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - /// - /// Non-positive values: - /// ``` - /// assert_eq!(0_f64.log10(), f64::NEG_INFINITY); - /// assert!((-42_f64).log10().is_nan()); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn log10(self) -> f64 { - intrinsics::log10f64(self) - } - - /// The positive difference of two numbers. - /// - /// * If `self <= other`: `0.0` - /// * Else: `self - other` - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `fdim` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 3.0_f64; - /// let y = -3.0_f64; - /// - /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); - /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); - /// - /// assert!(abs_difference_x < 1e-10); - /// assert!(abs_difference_y < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - #[deprecated( - since = "1.10.0", - note = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` \ - except that `abs_sub` also propagates NaNs (also \ - known as `fdim` in C). If you truly need the positive \ - difference, consider using that expression or the C function \ - `fdim`, depending on how you wish to handle NaN (please consider \ - filing an issue describing your use-case too)." - )] - pub fn abs_sub(self, other: f64) -> f64 { - #[allow(deprecated)] - core::f64::math::abs_sub(self, other) - } - - /// Returns the cube root of a number. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `cbrt` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 8.0_f64; - /// - /// // x^(1/3) - 2 == 0 - /// let abs_difference = (x.cbrt() - 2.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cbrt(self) -> f64 { - core::f64::math::cbrt(self) - } - - /// Compute the distance between the origin and a point (`x`, `y`) on the - /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a - /// right-angle triangle with other sides having length `x.abs()` and - /// `y.abs()`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `hypot` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0_f64; - /// let y = 3.0_f64; - /// - /// // sqrt(x^2 + y^2) - /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn hypot(self, other: f64) -> f64 { - cmath::hypot(self, other) - } - - /// Computes the sine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_2; - /// - /// let abs_difference = (x.sin() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin(self) -> f64 { - intrinsics::sinf64(self) - } - - /// Computes the cosine of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 2.0 * std::f64::consts::PI; - /// - /// let abs_difference = (x.cos() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cos(self) -> f64 { - intrinsics::cosf64(self) - } - - /// Computes the tangent of a number (in radians). - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tan` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_4; - /// let abs_difference = (x.tan() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-14); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tan(self) -> f64 { - cmath::tan(self) - } - - /// Computes the arcsine of a number. Return value is in radians in - /// the range [-pi/2, pi/2] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `asin` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = std::f64::consts::FRAC_PI_4; - /// - /// // asin(sin(pi/2)) - /// let abs_difference = (f.sin().asin() - f).abs(); - /// - /// assert!(abs_difference < 1e-14); - /// ``` - #[doc(alias = "arcsin")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asin(self) -> f64 { - cmath::asin(self) - } - - /// Computes the arccosine of a number. Return value is in radians in - /// the range [0, pi] or NaN if the number is outside the range - /// [-1, 1]. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `acos` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = std::f64::consts::FRAC_PI_4; - /// - /// // acos(cos(pi/4)) - /// let abs_difference = (f.cos().acos() - std::f64::consts::FRAC_PI_4).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[doc(alias = "arccos")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acos(self) -> f64 { - cmath::acos(self) - } - - /// Computes the arctangent of a number. Return value is in radians in the - /// range [-pi/2, pi/2]; - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `atan` from libc on Unix and - /// Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let f = 1.0_f64; - /// - /// // atan(tan(1)) - /// let abs_difference = (f.tan().atan() - 1.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[doc(alias = "arctan")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan(self) -> f64 { - cmath::atan(self) - } - - /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. - /// - /// | `x` | `y` | Piecewise Definition | Range | - /// |---------|---------|----------------------|---------------| - /// | `>= +0` | `>= +0` | `arctan(y/x)` | `[+0, +pi/2]` | - /// | `>= +0` | `<= -0` | `arctan(y/x)` | `[-pi/2, -0]` | - /// | `<= -0` | `>= +0` | `arctan(y/x) + pi` | `[+pi/2, +pi]`| - /// | `<= -0` | `<= -0` | `arctan(y/x) - pi` | `[-pi, -pi/2]`| - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `atan2` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// // Positive angles measured counter-clockwise - /// // from positive x axis - /// // -pi/4 radians (45 deg clockwise) - /// let x1 = 3.0_f64; - /// let y1 = -3.0_f64; - /// - /// // 3pi/4 radians (135 deg counter-clockwise) - /// let x2 = -3.0_f64; - /// let y2 = 3.0_f64; - /// - /// let abs_difference_1 = (y1.atan2(x1) - (-std::f64::consts::FRAC_PI_4)).abs(); - /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f64::consts::FRAC_PI_4)).abs(); - /// - /// assert!(abs_difference_1 < 1e-10); - /// assert!(abs_difference_2 < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atan2(self, other: f64) -> f64 { - cmath::atan2(self, other) - } - - /// Simultaneously computes the sine and cosine of the number, `x`. Returns - /// `(sin(x), cos(x))`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `(f64::sin(x), - /// f64::cos(x))`. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_4; - /// let f = x.sin_cos(); - /// - /// let abs_difference_0 = (f.0 - x.sin()).abs(); - /// let abs_difference_1 = (f.1 - x.cos()).abs(); - /// - /// assert!(abs_difference_0 < 1e-10); - /// assert!(abs_difference_1 < 1e-10); - /// ``` - #[doc(alias = "sincos")] - #[rustc_allow_incoherent_impl] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sin_cos(self) -> (f64, f64) { - (self.sin(), self.cos()) - } - - /// Returns `e^(self) - 1` in a way that is accurate even if the - /// number is close to zero. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `expm1` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 1e-16_f64; - /// - /// // for very small x, e^x is approximately 1 + x + x^2 / 2 - /// let approx = x + x * x / 2.0; - /// let abs_difference = (x.exp_m1() - approx).abs(); - /// - /// assert!(abs_difference < 1e-20); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn exp_m1(self) -> f64 { - cmath::expm1(self) - } - - /// Returns `ln(1+n)` (natural logarithm) more accurately than if - /// the operations were performed separately. - /// - /// This returns NaN when `n < -1.0`, and negative infinity when `n == -1.0`. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `log1p` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let x = 1e-16_f64; - /// - /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 - /// let approx = x - x * x / 2.0; - /// let abs_difference = (x.ln_1p() - approx).abs(); - /// - /// assert!(abs_difference < 1e-20); - /// ``` - /// - /// Out-of-range values: - /// ``` - /// assert_eq!((-1.0_f64).ln_1p(), f64::NEG_INFINITY); - /// assert!((-2.0_f64).ln_1p().is_nan()); - /// ``` - #[doc(alias = "log1p")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn ln_1p(self) -> f64 { - cmath::log1p(self) - } - - /// Hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `sinh` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// - /// let f = x.sinh(); - /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` - /// let g = ((e * e) - 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn sinh(self) -> f64 { - cmath::sinh(self) - } - - /// Hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `cosh` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// let f = x.cosh(); - /// // Solving cosh() at 1 gives this result - /// let g = ((e * e) + 1.0) / (2.0 * e); - /// let abs_difference = (f - g).abs(); - /// - /// // Same result - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn cosh(self) -> f64 { - cmath::cosh(self) - } - - /// Hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tanh` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// let e = std::f64::consts::E; - /// let x = 1.0_f64; - /// - /// let f = x.tanh(); - /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` - /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); - /// let abs_difference = (f - g).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn tanh(self) -> f64 { - cmath::tanh(self) - } - - /// Inverse hyperbolic sine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0_f64; - /// let f = x.sinh().asinh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[doc(alias = "arcsinh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn asinh(self) -> f64 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) - } - - /// Inverse hyperbolic cosine function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = 1.0_f64; - /// let f = x.cosh().acosh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[doc(alias = "arccosh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn acosh(self) -> f64 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } - } - - /// Inverse hyperbolic tangent function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// - /// # Examples - /// - /// ``` - /// let x = std::f64::consts::FRAC_PI_6; - /// let f = x.tanh().atanh(); - /// - /// let abs_difference = (f - x).abs(); - /// - /// assert!(abs_difference < 1.0e-10); - /// ``` - #[doc(alias = "arctanh")] - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn atanh(self) -> f64 { - 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() - } - - /// Gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `tgamma` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_gamma)] - /// let x = 5.0f64; - /// - /// let abs_difference = (x.gamma() - 24.0).abs(); - /// - /// assert!(abs_difference <= 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_gamma", issue = "99842")] - #[inline] - pub fn gamma(self) -> f64 { - cmath::tgamma(self) - } - - /// Natural logarithm of the absolute value of the gamma function - /// - /// The integer part of the tuple indicates the sign of the gamma function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. - /// This function currently corresponds to the `lgamma_r` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_gamma)] - /// let x = 2.0f64; - /// - /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); - /// - /// assert!(abs_difference <= f64::EPSILON); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_gamma", issue = "99842")] - #[inline] - pub fn ln_gamma(self) -> (f64, i32) { - let mut signgamp: i32 = 0; - let x = cmath::lgamma_r(self, &mut signgamp); - (x, signgamp) - } - - /// Error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erf` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_erf)] - /// /// The error function relates what percent of a normal distribution lies - /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). - /// fn within_standard_deviations(x: f64) -> f64 { - /// (x * std::f64::consts::FRAC_1_SQRT_2).erf() * 100.0 - /// } - /// - /// // 68% of a normal distribution is within one standard deviation - /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); - /// // 95% of a normal distribution is within two standard deviations - /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); - /// // 99.7% of a normal distribution is within three standard deviations - /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erf(self) -> f64 { - cmath::erf(self) - } - - /// Complementary error function. - /// - /// # Unspecified precision - /// - /// The precision of this function is non-deterministic. This means it varies by platform, - /// Rust version, and can even differ within the same execution from one invocation to the next. - /// - /// This function currently corresponds to the `erfc` from libc on Unix - /// and Windows. Note that this might change in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(float_erf)] - /// let x: f64 = 0.123; - /// - /// let one = x.erf() + x.erfc(); - /// let abs_difference = (one - 1.0).abs(); - /// - /// assert!(abs_difference <= 1e-10); - /// ``` - #[rustc_allow_incoherent_impl] - #[must_use = "method returns a new number and does not mutate the original value"] - #[unstable(feature = "float_erf", issue = "136321")] - #[inline] - pub fn erfc(self) -> f64 { - cmath::erfc(self) - } -} diff --git a/crates/std/src/num/mod.rs b/crates/std/src/num/mod.rs deleted file mode 100644 index ffb8789..0000000 --- a/crates/std/src/num/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Additional functionality for numerics. -//! -//! This module provides some extra types that are useful when doing numerical -//! work. See the individual documentation for each piece for more information. - -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -#[stable(feature = "int_error_matching", since = "1.55.0")] -pub use core::num::IntErrorKind; -#[stable(feature = "generic_nonzero", since = "1.79.0")] -pub use core::num::NonZero; -#[stable(feature = "saturating_int_impl", since = "1.74.0")] -pub use core::num::Saturating; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::num::Wrapping; -#[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -pub use core::num::ZeroablePrimitive; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}; -#[stable(feature = "signed_nonzero", since = "1.34.0")] -pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; -#[stable(feature = "nonzero", since = "1.28.0")] -pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; diff --git a/crates/std/src/prelude/mod.rs b/crates/std/src/prelude/mod.rs deleted file mode 100644 index 78eb79a..0000000 --- a/crates/std/src/prelude/mod.rs +++ /dev/null @@ -1,192 +0,0 @@ -//! # The Rust Prelude -//! -//! Rust comes with a variety of things in its standard library. However, if -//! you had to manually import every single thing that you used, it would be -//! very verbose. But importing a lot of things that a program never uses isn't -//! good either. A balance needs to be struck. -//! -//! The *prelude* is the list of things that Rust automatically imports into -//! every Rust program. It's kept as small as possible, and is focused on -//! things, particularly traits, which are used in almost every single Rust -//! program. -//! -//! # Other preludes -//! -//! Preludes can be seen as a pattern to make using multiple types more -//! convenient. As such, you'll find other preludes in the standard library, -//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may -//! also define their own preludes. -//! -//! [`std::io::prelude`]: crate::io::prelude -//! -//! The difference between 'the prelude' and these other preludes is that they -//! are not automatically `use`'d, and must be imported manually. This is still -//! easier than importing all of their constituent components. -//! -//! # Prelude contents -//! -//! The items included in the prelude depend on the edition of the crate. -//! The first version of the prelude is used in Rust 2015 and Rust 2018, -//! and lives in [`std::prelude::v1`]. -//! [`std::prelude::rust_2015`] and [`std::prelude::rust_2018`] re-export this prelude. -//! It re-exports the following: -//! -//! * [std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}, -//! marker traits that indicate fundamental properties of types. -//! * [std::ops]::{[Fn], [FnMut], [FnOnce]}, and their analogous -//! async traits, [std::ops]::{[AsyncFn], [AsyncFnMut], [AsyncFnOnce]}. -//! * [std::ops]::[Drop], for implementing destructors. -//! * [std::mem]::[drop], a convenience function for explicitly -//! dropping a value. -//! * [std::mem]::{[size_of], [size_of_val]}, to get the size of -//! a type or value. -//! * [std::mem]::{[align_of], [align_of_val]}, to get the -//! alignment of a type or value. -//! * [std::boxed]::[Box], a way to allocate values on the heap. -//! * [std::borrow]::[ToOwned], the conversion trait that defines -//! [`to_owned`], the generic method for creating an owned type from a -//! borrowed type. -//! * [std::clone]::[Clone], the ubiquitous trait that defines -//! [`clone`][Clone::clone], the method for producing a copy of a value. -//! * [std::cmp]::{[PartialEq], [PartialOrd], [Eq], [Ord]}, the -//! comparison traits, which implement the comparison operators and are often -//! seen in trait bounds. -//! * [std::convert]::{[AsRef], [AsMut], [Into], [From]}, generic -//! conversions, used by savvy API authors to create overloaded methods. -//! * [std::default]::[Default], types that have default values. -//! * [std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], -//! [ExactSizeIterator]}, iterators of various kinds. -//! * Most of the standard macros. -//! * [std::option]::[Option]::{[self][Option], [Some], [None]}, a -//! type which expresses the presence or absence of a value. This type is so -//! commonly used, its variants are also exported. -//! * [std::result]::[Result]::{[self][Result], [Ok], [Err]}, a type -//! for functions that may succeed or fail. Like [`Option`], its variants are -//! exported as well. -//! * [std::string]::{[String], [ToString]}, heap-allocated strings. -//! * [std::vec]::[Vec], a growable, heap-allocated vector. -//! -//! The prelude used in Rust 2021, [`std::prelude::rust_2021`], includes all of the above, -//! and in addition re-exports: -//! -//! * [std::convert]::{[TryFrom], [TryInto]}. -//! * [std::iter]::[FromIterator]. -//! -//! The prelude used in Rust 2024, [`std::prelude::rust_2024`], includes all of the above, -//! and in addition re-exports: -//! -//! * [std::future]::{[Future], [IntoFuture]}. -//! -//! [std::borrow]: crate::borrow -//! [std::boxed]: crate::boxed -//! [std::clone]: crate::clone -//! [std::cmp]: crate::cmp -//! [std::convert]: crate::convert -//! [std::default]: crate::default -//! [std::future]: crate::future -//! [std::iter]: crate::iter -//! [std::marker]: crate::marker -//! [std::mem]: crate::mem -//! [std::ops]: crate::ops -//! [std::option]: crate::option -//! [`std::prelude::v1`]: v1 -//! [`std::prelude::rust_2015`]: rust_2015 -//! [`std::prelude::rust_2018`]: rust_2018 -//! [`std::prelude::rust_2021`]: rust_2021 -//! [`std::prelude::rust_2024`]: rust_2024 -//! [std::result]: crate::result -//! [std::slice]: crate::slice -//! [std::string]: crate::string -//! [std::vec]: mod@crate::vec -//! [`to_owned`]: crate::borrow::ToOwned::to_owned -//! [book-closures]: ../../book/ch13-01-closures.html -//! [book-dtor]: ../../book/ch15-03-drop.html -//! [book-enums]: ../../book/ch06-01-defining-an-enum.html -//! [book-iter]: ../../book/ch13-02-iterators.html -//! [Future]: crate::future::Future -//! [IntoFuture]: crate::future::IntoFuture - -// No formatting: this file is nothing but re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -#![stable(feature = "rust1", since = "1.0.0")] - -pub mod v1; - -/// The 2015 version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2015", since = "1.55.0")] -pub mod rust_2015 { - #[stable(feature = "prelude_2015", since = "1.55.0")] - #[doc(no_inline)] - pub use super::v1::*; -} - -/// The 2018 version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2018", since = "1.55.0")] -pub mod rust_2018 { - #[stable(feature = "prelude_2018", since = "1.55.0")] - #[doc(no_inline)] - pub use super::v1::*; -} - -/// The 2021 version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2021", since = "1.55.0")] -pub mod rust_2021 { - #[stable(feature = "prelude_2021", since = "1.55.0")] - #[doc(no_inline)] - pub use super::v1::*; - - #[stable(feature = "prelude_2021", since = "1.55.0")] - #[doc(no_inline)] - pub use core::prelude::rust_2021::*; - - // There are two different panic macros, one in `core` and one in `std`. They are slightly - // different. For `std` we explicitly want the one defined in `std`. - #[stable(feature = "prelude_2021", since = "1.55.0")] - pub use super::v1::panic; -} - -/// The 2024 version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "1.85.0")] -pub mod rust_2024 { - #[stable(feature = "rust1", since = "1.0.0")] - #[doc(no_inline)] - pub use super::v1::*; - - #[stable(feature = "prelude_2024", since = "1.85.0")] - #[doc(no_inline)] - pub use core::prelude::rust_2024::*; - - // There are two different panic macros, one in `core` and one in `std`. They are slightly - // different. For `std` we explicitly want the one defined in `std`. - #[stable(feature = "prelude_2024", since = "1.85.0")] - pub use super::v1::panic; -} - -/// The Future version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[doc(hidden)] -#[unstable(feature = "prelude_future", issue = "none")] -pub mod rust_future { - #[stable(feature = "rust1", since = "1.0.0")] - #[doc(no_inline)] - pub use super::v1::*; - - #[unstable(feature = "prelude_next", issue = "none")] - #[doc(no_inline)] - pub use core::prelude::rust_future::*; - - // There are two different panic macros, one in `core` and one in `std`. They are slightly - // different. For `std` we explicitly want the one defined in `std`. - #[unstable(feature = "prelude_next", issue = "none")] - pub use super::v1::panic; -} diff --git a/crates/std/src/prelude/v1.rs b/crates/std/src/prelude/v1.rs deleted file mode 100644 index ee57e03..0000000 --- a/crates/std/src/prelude/v1.rs +++ /dev/null @@ -1,186 +0,0 @@ -//! The first version of the prelude of The Rust Standard Library. -//! -//! See the [module-level documentation](super) for more. - -#![stable(feature = "rust1", since = "1.0.0")] - -// No formatting: this file is nothing but re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -// Re-exported core operators -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::marker::{Send, Sized, Sync, Unpin}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[stable(feature = "async_closure", since = "1.85.0")] -#[doc(no_inline)] -pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; - -// Re-exported functions -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::mem::drop; -#[stable(feature = "size_of_prelude", since = "1.80.0")] -#[doc(no_inline)] -pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; - -// Re-exported types and traits -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::convert::{AsMut, AsRef, From, Into}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{Extend, IntoIterator, Iterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::option::Option::{self, None, Some}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::result::Result::{self, Err, Ok}; - -// Re-exported built-in macros and traits -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -#[expect(deprecated)] -pub use core::prelude::v1::{ - assert, assert_eq, assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq, - debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line, matches, - module_path, option_env, stringify, todo, r#try, unimplemented, unreachable, write, - writeln, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, -}; - -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::{ - dbg, eprint, eprintln, format, is_x86_feature_detected, print, println, thread_local -}; - -// These macros need special handling, so that we don't export them *and* the modules of the same -// name. We only want the macros in the prelude so we shadow the original modules with private -// modules with the same names. -mod ambiguous_macros_only { - #[expect(hidden_glob_reexports)] - mod vec {} - #[expect(hidden_glob_reexports)] - mod panic {} - // Building std without the expect exported_private_dependencies will create warnings, but then - // clippy claims its a useless_attribute. So silence both. - #[expect(clippy::useless_attribute)] - #[expect(exported_private_dependencies)] - #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] - pub use crate::*; -} -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use self::ambiguous_macros_only::{vec, panic}; - -#[stable(feature = "cfg_select", since = "1.95.0")] -#[doc(no_inline)] -pub use core::prelude::v1::cfg_select; - -#[unstable( - feature = "concat_bytes", - issue = "87555", - reason = "`concat_bytes` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::concat_bytes; - -#[unstable(feature = "const_format_args", issue = "none")] -#[doc(no_inline)] -pub use core::prelude::v1::const_format_args; - -#[unstable( - feature = "log_syntax", - issue = "29598", - reason = "`log_syntax!` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::log_syntax; - -#[unstable( - feature = "trace_macros", - issue = "29598", - reason = "`trace_macros` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::trace_macros; - -// Do not `doc(no_inline)` so that they become doc items on their own -// (no public module for them to be re-exported from). -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -pub use core::prelude::v1::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, -}; - -#[unstable(feature = "derive_const", issue = "118304")] -pub use core::prelude::v1::derive_const; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" -)] -pub use core::prelude::v1::cfg_accessible; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_eval", - issue = "82679", - reason = "`cfg_eval` is a recently implemented feature" -)] -pub use core::prelude::v1::cfg_eval; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "type_ascription", - issue = "23416", - reason = "placeholder syntax for type ascription" -)] -pub use core::prelude::v1::type_ascribe; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "deref_patterns", - issue = "87121", - reason = "placeholder syntax for deref patterns" -)] -pub use core::prelude::v1::deref; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "type_alias_impl_trait", - issue = "63063", - reason = "`type_alias_impl_trait` has open design concerns" -)] -pub use core::prelude::v1::define_opaque; - -#[unstable(feature = "extern_item_impls", issue = "125418")] -pub use core::prelude::v1::{eii, unsafe_eii}; - -#[unstable(feature = "eii_internals", issue = "none")] -pub use core::prelude::v1::eii_declaration; - -// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated -// rather than glob imported because we want docs to show these re-exports as -// pointing to within `std`. -// Below are the items from the alloc crate. - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::borrow::ToOwned; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::boxed::Box; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::string::{String, ToString}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::vec::Vec; diff --git a/crates/std/src/process/tests.rs b/crates/std/src/process/tests.rs deleted file mode 100644 index 12c5130..0000000 --- a/crates/std/src/process/tests.rs +++ /dev/null @@ -1,669 +0,0 @@ -use super::{Command, Output, Stdio}; -use crate::io::prelude::*; -use crate::io::{BorrowedBuf, ErrorKind}; -use crate::mem::MaybeUninit; -use crate::str; - -fn known_command() -> Command { - if cfg!(windows) { - Command::new("help") - } else if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) { - // iOS/tvOS/watchOS/visionOS have a very limited set of commandline - // binaries available. - Command::new("log") - } else { - Command::new("echo") - } -} - -#[cfg(target_os = "android")] -fn shell_cmd() -> Command { - Command::new("/system/bin/sh") -} - -#[cfg(not(target_os = "android"))] -fn shell_cmd() -> Command { - Command::new("/bin/sh") -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn smoke() { - let p = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 0"]).spawn() - } else { - shell_cmd().arg("-c").arg("true").spawn() - }; - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().success()); -} - -#[test] -#[cfg_attr(target_os = "android", ignore)] -fn smoke_failure() { - match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { - Ok(..) => panic!(), - Err(..) => {} - } -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn exit_reported_right() { - let p = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn() - } else { - shell_cmd().arg("-c").arg("false").spawn() - }; - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.wait().unwrap().code() == Some(1)); - drop(p.wait()); -} - -#[test] -#[cfg(unix)] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn signal_reported_right() { - use crate::os::unix::process::ExitStatusExt; - - let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap(); - p.kill().unwrap(); - match p.wait().unwrap().signal() { - Some(9) => {} - result => panic!("not terminated by signal 9 (instead, {result:?})"), - } -} - -pub fn run_output(mut cmd: Command) -> String { - let p = cmd.spawn(); - assert!(p.is_ok()); - let mut p = p.unwrap(); - assert!(p.stdout.is_some()); - let mut ret = String::new(); - p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap(); - assert!(p.wait().unwrap().success()); - return ret; -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn stdout_works() { - if cfg!(target_os = "windows") { - let mut cmd = Command::new("cmd"); - cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "foobar\r\n"); - } else { - let mut cmd = shell_cmd(); - cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "foobar\n"); - } -} - -#[test] -#[cfg_attr(windows, ignore)] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn set_current_dir_works() { - // On many Unix platforms this will use the posix_spawn path. - let mut cmd = shell_cmd(); - cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped()); - assert_eq!(run_output(cmd), "/\n"); - - // Also test the fork/exec path by setting a pre_exec function. - #[cfg(unix)] - { - use crate::os::unix::process::CommandExt; - - let mut cmd = shell_cmd(); - cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped()); - unsafe { - cmd.pre_exec(|| Ok(())); - } - assert_eq!(run_output(cmd), "/\n"); - } -} - -#[test] -#[cfg_attr(windows, ignore)] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn stdin_works() { - let mut p = shell_cmd() - .arg("-c") - .arg("read line; echo $line") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .unwrap(); - p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); - drop(p.stdin.take()); - let mut out = String::new(); - p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap(); - assert!(p.wait().unwrap().success()); - assert_eq!(out, "foobar\n"); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn child_stdout_read_buf() { - let mut cmd = if cfg!(target_os = "windows") { - let mut cmd = Command::new("cmd"); - cmd.arg("/C").arg("echo abc"); - cmd - } else { - let mut cmd = shell_cmd(); - cmd.arg("-c").arg("echo abc"); - cmd - }; - cmd.stdin(Stdio::null()); - cmd.stdout(Stdio::piped()); - let child = cmd.spawn().unwrap(); - - let mut stdout = child.stdout.unwrap(); - let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; - let mut buf = BorrowedBuf::from(buf.as_mut_slice()); - stdout.read_buf(buf.unfilled()).unwrap(); - - // ChildStdout::read_buf should omit buffer initialization. - if cfg!(target_os = "windows") { - assert_eq!(buf.filled(), b"abc\r\n"); - assert_eq!(buf.init_len(), 5); - } else { - assert_eq!(buf.filled(), b"abc\n"); - assert_eq!(buf.init_len(), 4); - }; -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_process_status() { - let mut status = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() - } else { - shell_cmd().arg("-c").arg("false").status().unwrap() - }; - assert!(status.code() == Some(1)); - - status = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap() - } else { - shell_cmd().arg("-c").arg("true").status().unwrap() - }; - assert!(status.success()); -} - -#[test] -fn test_process_output_fail_to_start() { - match Command::new("/no-binary-by-this-name-should-exist").output() { - Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), - Ok(..) => panic!(), - } -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_process_output_output() { - let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() - } else { - shell_cmd().arg("-c").arg("echo hello").output().unwrap() - }; - let output_str = str::from_utf8(&stdout).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - assert_eq!(stderr, Vec::new()); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_process_output_error() { - let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() - } else { - Command::new("mkdir").arg("./").output().unwrap() - }; - - assert!(status.code().is_some()); - assert!(status.code() != Some(0)); - assert_eq!(stdout, Vec::new()); - assert!(!stderr.is_empty()); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_finish_once() { - let mut prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() - } else { - shell_cmd().arg("-c").arg("false").spawn().unwrap() - }; - assert!(prog.wait().unwrap().code() == Some(1)); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_finish_twice() { - let mut prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() - } else { - shell_cmd().arg("-c").arg("false").spawn().unwrap() - }; - assert!(prog.wait().unwrap().code() == Some(1)); - assert!(prog.wait().unwrap().code() == Some(1)); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_wait_with_output_once() { - let prog = if cfg!(target_os = "windows") { - Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() - } else { - shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap() - }; - - let Output { status, stdout, stderr } = prog.wait_with_output().unwrap(); - let output_str = str::from_utf8(&stdout).unwrap(); - - assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello"); - assert_eq!(stderr, Vec::new()); -} - -#[cfg(all(unix, not(target_os = "android")))] -pub fn env_cmd() -> Command { - Command::new("env") -} -#[cfg(target_os = "android")] -pub fn env_cmd() -> Command { - let mut cmd = Command::new("/system/bin/sh"); - cmd.arg("-c").arg("set"); - cmd -} - -#[cfg(windows)] -pub fn env_cmd() -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("set"); - cmd -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_override_env() { - use crate::env; - - // In some build environments (such as chrooted Nix builds), `env` can - // only be found in the explicitly-provided PATH env variable, not in - // default places such as /bin or /usr/bin. So we need to pass through - // PATH to our sub-process. - let mut cmd = env_cmd(); - cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); - if let Some(p) = env::var_os("PATH") { - cmd.env("PATH", &p); - } - let result = cmd.output().unwrap(); - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}", - ); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_add_to_env() { - let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV=123"), - "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}" - ); -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no shell available" -)] -fn test_capture_env_at_spawn() { - use crate::env; - - let mut cmd = env_cmd(); - cmd.env("RUN_TEST_NEW_ENV1", "123"); - - // This variable will not be present if the environment has already - // been captured above. - unsafe { - env::set_var("RUN_TEST_NEW_ENV2", "456"); - } - let result = cmd.output().unwrap(); - unsafe { - env::remove_var("RUN_TEST_NEW_ENV2"); - } - - let output = String::from_utf8_lossy(&result.stdout).to_string(); - - assert!( - output.contains("RUN_TEST_NEW_ENV1=123"), - "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}" - ); - assert!( - output.contains("RUN_TEST_NEW_ENV2=456"), - "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}" - ); -} - -// Regression tests for #30858. -#[test] -fn test_interior_nul_in_progname_is_error() { - match Command::new("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -#[test] -fn test_interior_nul_in_arg_is_error() { - match known_command().arg("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -#[test] -fn test_interior_nul_in_args_is_error() { - match known_command().args(&["has-some-\0\0s-inside"]).spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -#[test] -fn test_interior_nul_in_current_dir_is_error() { - match known_command().current_dir("has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -// Regression tests for #30862. -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no `env` cmd available" -)] -fn test_interior_nul_in_env_key_is_error() { - match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -#[test] -#[cfg_attr( - any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), - ignore = "no `env` cmd available" -)] -fn test_interior_nul_in_env_value_is_error() { - match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { - Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), - Ok(_) => panic!(), - } -} - -#[test] -fn test_command_implements_send_sync() { - fn take_send_sync_type(_: T) {} - take_send_sync_type(Command::new("")) -} - -// Ensure that starting a process with no environment variables works on Windows. -// This will fail if the environment block is ill-formed. -#[test] -#[cfg(windows)] -fn env_empty() { - let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn(); - assert!(p.is_ok()); -} - -#[test] -#[cfg(not(windows))] -#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] -fn debug_print() { - const PIDFD: &'static str = - if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" }; - - let mut command = Command::new("some-boring-name"); - - assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#)); - - assert_eq!( - format!("{command:#?}"), - format!( - r#"Command {{ - program: "some-boring-name", - args: [ - "some-boring-name", - ], -{PIDFD}}}"# - ) - ); - - command.args(&["1", "2", "3"]); - - assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#)); - - assert_eq!( - format!("{command:#?}"), - format!( - r#"Command {{ - program: "some-boring-name", - args: [ - "some-boring-name", - "1", - "2", - "3", - ], -{PIDFD}}}"# - ) - ); - - crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name"); - - assert_eq!( - format!("{command:?}"), - format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#) - ); - - assert_eq!( - format!("{command:#?}"), - format!( - r#"Command {{ - program: "some-boring-name", - args: [ - "exciting-name", - "1", - "2", - "3", - ], -{PIDFD}}}"# - ) - ); - - let mut command_with_env_and_cwd = Command::new("boring-name"); - command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar"); - assert_eq!( - format!("{command_with_env_and_cwd:?}"), - r#"cd "/some/path" && FOO="bar" "boring-name""# - ); - assert_eq!( - format!("{command_with_env_and_cwd:#?}"), - format!( - r#"Command {{ - program: "boring-name", - args: [ - "boring-name", - ], - env: CommandEnv {{ - clear: false, - vars: {{ - "FOO": Some( - "bar", - ), - }}, - }}, - cwd: Some( - "/some/path", - ), -{PIDFD}}}"# - ) - ); - - let mut command_with_removed_env = Command::new("boring-name"); - command_with_removed_env.env_remove("FOO").env_remove("BAR"); - assert_eq!(format!("{command_with_removed_env:?}"), r#"env -u BAR -u FOO "boring-name""#); - assert_eq!( - format!("{command_with_removed_env:#?}"), - format!( - r#"Command {{ - program: "boring-name", - args: [ - "boring-name", - ], - env: CommandEnv {{ - clear: false, - vars: {{ - "BAR": None, - "FOO": None, - }}, - }}, -{PIDFD}}}"# - ) - ); - - let mut command_with_cleared_env = Command::new("boring-name"); - command_with_cleared_env.env_clear().env("BAR", "val").env_remove("FOO"); - assert_eq!(format!("{command_with_cleared_env:?}"), r#"env -i BAR="val" "boring-name""#); - assert_eq!( - format!("{command_with_cleared_env:#?}"), - format!( - r#"Command {{ - program: "boring-name", - args: [ - "boring-name", - ], - env: CommandEnv {{ - clear: true, - vars: {{ - "BAR": Some( - "val", - ), - }}, - }}, -{PIDFD}}}"# - ) - ); -} - -// See issue #91991 -#[test] -#[cfg(windows)] -fn run_bat_script() { - let tempdir = crate::test_helpers::tmpdir(); - let script_path = tempdir.join("hello.cmd"); - - crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); - let output = Command::new(&script_path) - .arg("fellow Rustaceans") - .stdout(crate::process::Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); -} - -// See issue #95178 -#[test] -#[cfg(windows)] -fn run_canonical_bat_script() { - let tempdir = crate::test_helpers::tmpdir(); - let script_path = tempdir.join("hello.cmd"); - - crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); - - // Try using a canonical path - let output = Command::new(&script_path.canonicalize().unwrap()) - .arg("fellow Rustaceans") - .stdout(crate::process::Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); -} - -#[test] -fn terminate_exited_process() { - let mut cmd = if cfg!(target_os = "android") { - let mut p = shell_cmd(); - p.args(&["-c", "true"]); - p - } else { - known_command() - }; - let mut p = cmd.stdout(Stdio::null()).spawn().unwrap(); - p.wait().unwrap(); - assert!(p.kill().is_ok()); - assert!(p.kill().is_ok()); -} diff --git a/crates/std/src/sync/barrier.rs b/crates/std/src/sync/barrier.rs deleted file mode 100644 index 6a5cc9b..0000000 --- a/crates/std/src/sync/barrier.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::fmt; -use crate::panic::RefUnwindSafe; -use crate::sync::nonpoison::{Condvar, Mutex}; - -/// A barrier enables multiple threads to synchronize the beginning -/// of some computation. -/// -/// # Examples -/// -/// ``` -/// use std::sync::Barrier; -/// use std::thread; -/// -/// let n = 10; -/// let barrier = Barrier::new(n); -/// thread::scope(|s| { -/// for _ in 0..n { -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// s.spawn(|| { -/// println!("before wait"); -/// barrier.wait(); -/// println!("after wait"); -/// }); -/// } -/// }); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Barrier { - lock: Mutex, - cvar: Condvar, - num_threads: usize, -} - -#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] -impl RefUnwindSafe for Barrier {} - -// The inner state of a double barrier -struct BarrierState { - count: usize, - generation_id: usize, -} - -/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads -/// in the [`Barrier`] have rendezvoused. -/// -/// # Examples -/// -/// ``` -/// use std::sync::Barrier; -/// -/// let barrier = Barrier::new(1); -/// let barrier_wait_result = barrier.wait(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BarrierWaitResult(bool); - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Barrier { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Barrier").finish_non_exhaustive() - } -} - -impl Barrier { - /// Creates a new barrier that can block a given number of threads. - /// - /// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`], - /// and then wake up all threads at once. - /// - /// [`wait()`]: Barrier::wait - /// - /// # Examples - /// - /// ``` - /// use std::sync::Barrier; - /// - /// let barrier = Barrier::new(10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_barrier", since = "1.78.0")] - #[must_use] - #[inline] - pub const fn new(n: usize) -> Barrier { - Barrier { - lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }), - cvar: Condvar::new(), - num_threads: n, - } - } - - /// Blocks the current thread until all threads have rendezvoused here. - /// - /// Barriers are re-usable after all threads have rendezvoused once, and can - /// be used continuously. - /// - /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that - /// returns `true` from [`BarrierWaitResult::is_leader()`] when returning - /// from this function, and all other threads will receive a result that - /// will return `false` from [`BarrierWaitResult::is_leader()`]. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Barrier; - /// use std::thread; - /// - /// let n = 10; - /// let barrier = Barrier::new(n); - /// thread::scope(|s| { - /// for _ in 0..n { - /// // The same messages will be printed together. - /// // You will NOT see any interleaving. - /// s.spawn(|| { - /// println!("before wait"); - /// barrier.wait(); - /// println!("after wait"); - /// }); - /// } - /// }); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn wait(&self) -> BarrierWaitResult { - let mut lock = self.lock.lock(); - let local_gen = lock.generation_id; - lock.count += 1; - if lock.count < self.num_threads { - self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id); - BarrierWaitResult(false) - } else { - lock.count = 0; - lock.generation_id = lock.generation_id.wrapping_add(1); - self.cvar.notify_all(); - BarrierWaitResult(true) - } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for BarrierWaitResult { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish() - } -} - -impl BarrierWaitResult { - /// Returns `true` if this thread is the "leader thread" for the call to - /// [`Barrier::wait()`]. - /// - /// Only one thread will have `true` returned from their result, all other - /// threads will have `false` returned. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Barrier; - /// - /// let barrier = Barrier::new(1); - /// let barrier_wait_result = barrier.wait(); - /// println!("{:?}", barrier_wait_result.is_leader()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn is_leader(&self) -> bool { - self.0 - } -} diff --git a/crates/std/src/sync/lazy_lock.rs b/crates/std/src/sync/lazy_lock.rs deleted file mode 100644 index 9bdde0a..0000000 --- a/crates/std/src/sync/lazy_lock.rs +++ /dev/null @@ -1,426 +0,0 @@ -use super::once::OnceExclusiveState; -use crate::cell::UnsafeCell; -use crate::mem::ManuallyDrop; -use crate::ops::{Deref, DerefMut}; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::Once; -use crate::{fmt, ptr}; - -// We use the state of a Once as discriminant value. Upon creation, the state is -// "incomplete" and `f` contains the initialization closure. In the first call to -// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state -// is changed to "complete". If it panics, the Once is poisoned, so none of the -// two fields is initialized. -union Data { - value: ManuallyDrop, - f: ManuallyDrop, -} - -/// A value which is initialized on the first access. -/// -/// This type is a thread-safe [`LazyCell`], and can be used in statics. -/// Since initialization may be called from multiple threads, any -/// dereferencing call will block the calling thread if another -/// initialization routine is currently running. -/// -/// [`LazyCell`]: crate::cell::LazyCell -/// -/// # Poisoning -/// -/// If the initialization closure passed to [`LazyLock::new`] panics, the lock will be poisoned. -/// Once the lock is poisoned, any threads that attempt to access this lock (via a dereference -/// or via an explicit call to [`force()`]) will panic. -/// -/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key -/// difference, however, is that poisoning in `LazyLock` is _unrecoverable_. All future accesses of -/// the lock from other threads will panic, whereas a type in [`std::sync::poison`] like -/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`]. -/// -/// [`force()`]: LazyLock::force -/// [`std::sync::poison`]: crate::sync::poison -/// [`std::sync::poison::Mutex`]: crate::sync::poison::Mutex -/// [`PoisonError::into_inner()`]: crate::sync::poison::PoisonError::into_inner -/// -/// # Examples -/// -/// Initialize static variables with `LazyLock`. -/// ``` -/// use std::sync::LazyLock; -/// -/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated. -/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory -/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. -/// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { -/// # mod another_crate { -/// # pub fn great_question() -> String { "42".to_string() } -/// # } -/// // M3 Ultra takes about 16 million years in --release config -/// another_crate::great_question() -/// }); -/// -/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. -/// let _ = &*DEEP_THOUGHT; -/// ``` -/// -/// Initialize fields with `LazyLock`. -/// ``` -/// use std::sync::LazyLock; -/// -/// #[derive(Debug)] -/// struct UseCellLock { -/// number: LazyLock, -/// } -/// fn main() { -/// let lock: LazyLock = LazyLock::new(|| 0u32); -/// -/// let data = UseCellLock { number: lock }; -/// println!("{}", *data.number); -/// } -/// ``` -#[stable(feature = "lazy_cell", since = "1.80.0")] -pub struct LazyLock T> { - // FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available - once: Once, - data: UnsafeCell>, -} - -impl T> LazyLock { - /// Creates a new lazy value with the given initializing function. - /// - /// # Examples - /// - /// ``` - /// use std::sync::LazyLock; - /// - /// let hello = "Hello, World!".to_string(); - /// - /// let lazy = LazyLock::new(|| hello.to_uppercase()); - /// - /// assert_eq!(&*lazy, "HELLO, WORLD!"); - /// ``` - #[inline] - #[stable(feature = "lazy_cell", since = "1.80.0")] - #[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")] - pub const fn new(f: F) -> LazyLock { - LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } - } - - /// Consumes this `LazyLock` returning the stored value. - /// - /// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise. - /// - /// # Panics - /// - /// Panics if the lock is poisoned. - /// - /// # Examples - /// - /// ``` - /// #![feature(lazy_cell_into_inner)] - /// - /// use std::sync::LazyLock; - /// - /// let hello = "Hello, World!".to_string(); - /// - /// let lazy = LazyLock::new(|| hello.to_uppercase()); - /// - /// assert_eq!(&*lazy, "HELLO, WORLD!"); - /// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string())); - /// ``` - #[unstable(feature = "lazy_cell_into_inner", issue = "125623")] - pub fn into_inner(mut this: Self) -> Result { - let state = this.once.state(); - match state { - OnceExclusiveState::Poisoned => panic_poisoned(), - state => { - let this = ManuallyDrop::new(this); - let data = unsafe { ptr::read(&this.data) }.into_inner(); - match state { - OnceExclusiveState::Incomplete => { - Err(ManuallyDrop::into_inner(unsafe { data.f })) - } - OnceExclusiveState::Complete => { - Ok(ManuallyDrop::into_inner(unsafe { data.value })) - } - OnceExclusiveState::Poisoned => unreachable!(), - } - } - } - } - - /// Forces the evaluation of this lazy value and returns a mutable reference to - /// the result. - /// - /// # Panics - /// - /// If the initialization closure panics (the one that is passed to the [`new()`] method), the - /// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future - /// accesses of the lock (via [`force()`] or a dereference) to panic. - /// - /// [`new()`]: LazyLock::new - /// [`force()`]: LazyLock::force - /// - /// # Examples - /// - /// ``` - /// use std::sync::LazyLock; - /// - /// let mut lazy = LazyLock::new(|| 92); - /// - /// let p = LazyLock::force_mut(&mut lazy); - /// assert_eq!(*p, 92); - /// *p = 44; - /// assert_eq!(*lazy, 44); - /// ``` - #[inline] - #[stable(feature = "lazy_get", since = "1.94.0")] - pub fn force_mut(this: &mut LazyLock) -> &mut T { - #[cold] - /// # Safety - /// May only be called when the state is `Incomplete`. - unsafe fn really_init_mut T>(this: &mut LazyLock) -> &mut T { - struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock); - impl Drop for PoisonOnPanic<'_, T, F> { - #[inline] - fn drop(&mut self) { - self.0.once.set_state(OnceExclusiveState::Poisoned); - } - } - - // SAFETY: We always poison if the initializer panics (then we never check the data), - // or set the data on success. - let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) }; - // INVARIANT: Initiated from mutable reference, don't drop because we read it. - let guard = PoisonOnPanic(this); - let data = f(); - guard.0.data.get_mut().value = ManuallyDrop::new(data); - guard.0.once.set_state(OnceExclusiveState::Complete); - core::mem::forget(guard); - // SAFETY: We put the value there above. - unsafe { &mut this.data.get_mut().value } - } - - let state = this.once.state(); - match state { - OnceExclusiveState::Poisoned => panic_poisoned(), - // SAFETY: The `Once` states we completed the initialization. - OnceExclusiveState::Complete => unsafe { &mut this.data.get_mut().value }, - // SAFETY: The state is `Incomplete`. - OnceExclusiveState::Incomplete => unsafe { really_init_mut(this) }, - } - } - - /// Forces the evaluation of this lazy value and returns a reference to - /// result. This is equivalent to the `Deref` impl, but is explicit. - /// - /// This method will block the calling thread if another initialization - /// routine is currently running. - /// - /// # Panics - /// - /// If the initialization closure panics (the one that is passed to the [`new()`] method), the - /// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future - /// accesses of the lock (via [`force()`] or a dereference) to panic. - /// - /// [`new()`]: LazyLock::new - /// [`force()`]: LazyLock::force - /// - /// # Examples - /// - /// ``` - /// use std::sync::LazyLock; - /// - /// let lazy = LazyLock::new(|| 92); - /// - /// assert_eq!(LazyLock::force(&lazy), &92); - /// assert_eq!(&*lazy, &92); - /// ``` - #[inline] - #[stable(feature = "lazy_cell", since = "1.80.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn force(this: &LazyLock) -> &T { - this.once.call_once_force(|state| { - if state.is_poisoned() { - panic_poisoned(); - } - - // SAFETY: `call_once` only runs this closure once, ever. - let data = unsafe { &mut *this.data.get() }; - let f = unsafe { ManuallyDrop::take(&mut data.f) }; - let value = f(); - data.value = ManuallyDrop::new(value); - }); - - // SAFETY: - // There are four possible scenarios: - // * the closure was called and initialized `value`. - // * the closure was called and panicked, so this point is never reached. - // * the closure was not called, but a previous call initialized `value`. - // * the closure was not called because the Once is poisoned, which we handled above. - // So `value` has definitely been initialized and will not be modified again. - unsafe { &*(*this.data.get()).value } - } -} - -impl LazyLock { - /// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or - /// poisoned), returns `None`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::LazyLock; - /// - /// let mut lazy = LazyLock::new(|| 92); - /// - /// assert_eq!(LazyLock::get_mut(&mut lazy), None); - /// let _ = LazyLock::force(&lazy); - /// *LazyLock::get_mut(&mut lazy).unwrap() = 44; - /// assert_eq!(*lazy, 44); - /// ``` - #[inline] - #[stable(feature = "lazy_get", since = "1.94.0")] - pub fn get_mut(this: &mut LazyLock) -> Option<&mut T> { - // `state()` does not perform an atomic load, so prefer it over `is_complete()`. - let state = this.once.state(); - match state { - // SAFETY: - // The closure has been run successfully, so `value` has been initialized. - OnceExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }), - _ => None, - } - } - - /// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned), - /// returns `None`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::LazyLock; - /// - /// let lazy = LazyLock::new(|| 92); - /// - /// assert_eq!(LazyLock::get(&lazy), None); - /// let _ = LazyLock::force(&lazy); - /// assert_eq!(LazyLock::get(&lazy), Some(&92)); - /// ``` - #[inline] - #[stable(feature = "lazy_get", since = "1.94.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn get(this: &LazyLock) -> Option<&T> { - if this.once.is_completed() { - // SAFETY: - // The closure has been run successfully, so `value` has been initialized - // and will not be modified again. - Some(unsafe { &(*this.data.get()).value }) - } else { - None - } - } -} - -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl Drop for LazyLock { - fn drop(&mut self) { - match self.once.state() { - OnceExclusiveState::Incomplete => unsafe { - ManuallyDrop::drop(&mut self.data.get_mut().f) - }, - OnceExclusiveState::Complete => unsafe { - ManuallyDrop::drop(&mut self.data.get_mut().value) - }, - OnceExclusiveState::Poisoned => {} - } - } -} - -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl T> Deref for LazyLock { - type Target = T; - - /// Dereferences the value. - /// - /// This method will block the calling thread if another initialization - /// routine is currently running. - /// - /// # Panics - /// - /// If the initialization closure panics (the one that is passed to the [`new()`] method), the - /// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future - /// accesses of the lock (via [`force()`] or a dereference) to panic. - /// - /// [`new()`]: LazyLock::new - /// [`force()`]: LazyLock::force - #[inline] - fn deref(&self) -> &T { - LazyLock::force(self) - } -} - -#[stable(feature = "lazy_deref_mut", since = "1.89.0")] -impl T> DerefMut for LazyLock { - /// # Panics - /// - /// If the initialization closure panics (the one that is passed to the [`new()`] method), the - /// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future - /// accesses of the lock (via [`force()`] or a dereference) to panic. - /// - /// [`new()`]: LazyLock::new - /// [`force()`]: LazyLock::force - #[inline] - fn deref_mut(&mut self) -> &mut T { - LazyLock::force_mut(self) - } -} - -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl Default for LazyLock { - /// Creates a new lazy value using `Default` as the initializing function. - #[inline] - fn default() -> LazyLock { - LazyLock::new(T::default) - } -} - -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl fmt::Debug for LazyLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_tuple("LazyLock"); - match LazyLock::get(self) { - Some(v) => d.field(v), - None => d.field(&format_args!("")), - }; - d.finish() - } -} - -#[stable(feature = "from_wrapper_impls", since = "CURRENT_RUSTC_VERSION")] -impl From for LazyLock { - /// Constructs a `LazyLock` that starts already initialized - /// with the provided value. - #[inline] - fn from(value: T) -> Self { - LazyLock { - once: Once::new_complete(), - data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }), - } - } -} - -#[cold] -#[inline(never)] -fn panic_poisoned() -> ! { - panic!("LazyLock instance has previously been poisoned") -} - -// We never create a `&F` from a `&LazyLock` so it is fine -// to not impl `Sync` for `F`. -#[stable(feature = "lazy_cell", since = "1.80.0")] -unsafe impl Sync for LazyLock {} -// auto-derived `Send` impl is OK. - -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl RefUnwindSafe for LazyLock {} -#[stable(feature = "lazy_cell", since = "1.80.0")] -impl UnwindSafe for LazyLock {} diff --git a/crates/std/src/sync/mod.rs b/crates/std/src/sync/mod.rs deleted file mode 100644 index 5da5048..0000000 --- a/crates/std/src/sync/mod.rs +++ /dev/null @@ -1,307 +0,0 @@ -//! Useful synchronization primitives. -//! -//! ## The need for synchronization -//! -//! Conceptually, a Rust program is a series of operations which will -//! be executed on a computer. The timeline of events happening in the -//! program is consistent with the order of the operations in the code. -//! -//! Consider the following code, operating on some global static variables: -//! -//! ```rust -//! // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -//! #![allow(static_mut_refs)] -//! -//! static mut A: u32 = 0; -//! static mut B: u32 = 0; -//! static mut C: u32 = 0; -//! -//! fn main() { -//! unsafe { -//! A = 3; -//! B = 4; -//! A = A + B; -//! C = B; -//! println!("{A} {B} {C}"); -//! C = A; -//! } -//! } -//! ``` -//! -//! It appears as if some variables stored in memory are changed, an addition -//! is performed, result is stored in `A` and the variable `C` is -//! modified twice. -//! -//! When only a single thread is involved, the results are as expected: -//! the line `7 4 4` gets printed. -//! -//! As for what happens behind the scenes, when optimizations are enabled the -//! final generated machine code might look very different from the code: -//! -//! - The first store to `C` might be moved before the store to `A` or `B`, -//! _as if_ we had written `C = 4; A = 3; B = 4`. -//! -//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored -//! in a temporary location until it gets printed, with the global variable -//! never getting updated. -//! -//! - The final result could be determined just by looking at the code -//! at compile time, so [constant folding] might turn the whole -//! block into a simple `println!("7 4 4")`. -//! -//! The compiler is allowed to perform any combination of these -//! optimizations, as long as the final optimized code, when executed, -//! produces the same results as the one without optimizations. -//! -//! Due to the [concurrency] involved in modern computers, assumptions -//! about the program's execution order are often wrong. Access to -//! global variables can lead to nondeterministic results, **even if** -//! compiler optimizations are disabled, and it is **still possible** -//! to introduce synchronization bugs. -//! -//! Note that thanks to Rust's safety guarantees, accessing global (static) -//! variables requires `unsafe` code, assuming we don't use any of the -//! synchronization primitives in this module. -//! -//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding -//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science) -//! -//! ## Out-of-order execution -//! -//! Instructions can execute in a different order from the one we define, due to -//! various reasons: -//! -//! - The **compiler** reordering instructions: If the compiler can issue an -//! instruction at an earlier point, it will try to do so. For example, it -//! might hoist memory loads at the top of a code block, so that the CPU can -//! start [prefetching] the values from memory. -//! -//! In single-threaded scenarios, this can cause issues when writing -//! signal handlers or certain kinds of low-level code. -//! Use [compiler fences] to prevent this reordering. -//! -//! - A **single processor** executing instructions [out-of-order]: -//! Modern CPUs are capable of [superscalar] execution, -//! i.e., multiple instructions might be executing at the same time, -//! even though the machine code describes a sequential process. -//! -//! This kind of reordering is handled transparently by the CPU. -//! -//! - A **multiprocessor** system executing multiple hardware threads -//! at the same time: In multi-threaded scenarios, you can use two -//! kinds of primitives to deal with synchronization: -//! - [memory fences] to ensure memory accesses are made visible to -//! other CPUs in the right order. -//! - [atomic operations] to ensure simultaneous access to the same -//! memory location doesn't lead to undefined behavior. -//! -//! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching -//! [compiler fences]: crate::sync::atomic::compiler_fence -//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution -//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor -//! [memory fences]: crate::sync::atomic::fence -//! [atomic operations]: crate::sync::atomic -//! -//! ## Higher-level synchronization objects -//! -//! Most of the low-level synchronization primitives are quite error-prone and -//! inconvenient to use, which is why the standard library also exposes some -//! higher-level synchronization objects. -//! -//! These abstractions can be built out of lower-level primitives. -//! For efficiency, the sync objects in the standard library are usually -//! implemented with help from the operating system's kernel, which is -//! able to reschedule the threads while they are blocked on acquiring -//! a lock. -//! -//! The following is an overview of the available synchronization -//! objects: -//! -//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used -//! in multithreaded environments to prolong the lifetime of some -//! data until all the threads have finished using it. -//! -//! - [`Barrier`]: Ensures multiple threads will wait for each other -//! to reach a point in the program, before continuing execution all -//! together. -//! -//! - [`Condvar`]: Condition Variable, providing the ability to block -//! a thread while waiting for an event to occur. -//! -//! - [`mpsc`]: Multi-producer, single-consumer queues, used for -//! message-based communication. Can provide a lightweight -//! inter-thread synchronisation mechanism, at the cost of some -//! extra memory. -//! -//! - [`mpmc`]: Multi-producer, multi-consumer queues, used for -//! message-based communication. Can provide a lightweight -//! inter-thread synchronisation mechanism, at the cost of some -//! extra memory. -//! -//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at -//! most one thread at a time is able to access some data. -//! -//! - [`Once`]: Used for a thread-safe, one-time global initialization routine. -//! Mostly useful for implementing other types like [`OnceLock`]. -//! -//! - [`OnceLock`]: Used for thread-safe, one-time initialization of a -//! variable, with potentially different initializers based on the caller. -//! -//! - [`LazyLock`]: Used for thread-safe, one-time initialization of a -//! variable, using one nullary initializer function provided at creation. -//! -//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows -//! multiple readers at the same time, while allowing only one -//! writer at a time. In some cases, this can be more efficient than -//! a mutex. -//! -//! [`Arc`]: crate::sync::Arc -//! [`Barrier`]: crate::sync::Barrier -//! [`Condvar`]: crate::sync::Condvar -//! [`mpmc`]: crate::sync::mpmc -//! [`mpsc`]: crate::sync::mpsc -//! [`Mutex`]: crate::sync::Mutex -//! [`Once`]: crate::sync::Once -//! [`OnceLock`]: crate::sync::OnceLock -//! [`RwLock`]: crate::sync::RwLock - -#![stable(feature = "rust1", since = "1.0.0")] - -// No formatting: this file is just re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -// These come from `core` & `alloc` and only in one flavor: no poisoning. -#[unstable(feature = "exclusive_wrapper", issue = "98407")] -pub use core::sync::Exclusive; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::sync::atomic; - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -pub use alloc_crate::sync::UniqueArc; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::sync::{Arc, Weak}; - -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub mod mpmc; -pub mod mpsc; -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub mod oneshot; - -pub(crate) mod once; // `pub(crate)` for the `sys::sync::once` implementations and `LazyLock`. - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::once::{Once, OnceState}; - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -#[expect(deprecated)] -pub use self::once::ONCE_INIT; - -mod barrier; -mod lazy_lock; -mod once_lock; -mod reentrant_lock; - -// These exist only in one flavor: no poisoning. -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::barrier::{Barrier, BarrierWaitResult}; -#[stable(feature = "lazy_cell", since = "1.80.0")] -pub use self::lazy_lock::LazyLock; -#[stable(feature = "once_cell", since = "1.70.0")] -pub use self::once_lock::OnceLock; -#[unstable(feature = "reentrant_lock", issue = "121440")] -pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; - -// Note: in the future we will change the default version in `std::sync` to the non-poisoning -// version over an edition. -// See https://github.com/rust-lang/rust/issues/134645#issuecomment-3324577500 for more details. - -#[unstable(feature = "sync_nonpoison", issue = "134645")] -pub mod nonpoison; -#[unstable(feature = "sync_poison_mod", issue = "134646")] -pub mod poison; - -// FIXME(sync_poison_mod): remove all `#[doc(inline)]` once the modules are stabilized. - -// These exist only with poisoning. -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::poison::{LockResult, PoisonError}; - -// These exist in both flavors: with and without poisoning. -// The historical default is the version with poisoning. -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::poison::{ - TryLockError, TryLockResult, - Mutex, MutexGuard, - RwLock, RwLockReadGuard, RwLockWriteGuard, - Condvar, -}; - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -#[doc(inline)] -pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard}; - -/// A type indicating whether a timed wait on a condition variable returned -/// due to a time out or not. -/// -/// It is returned by the [`wait_timeout`] method. -/// -/// [`wait_timeout`]: Condvar::wait_timeout -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[stable(feature = "wait_timeout", since = "1.5.0")] -pub struct WaitTimeoutResult(bool); - -impl WaitTimeoutResult { - /// Returns `true` if the wait was known to have timed out. - /// - /// # Examples - /// - /// This example spawns a thread which will sleep 20 milliseconds before - /// updating a boolean value and then notifying the condvar. - /// - /// The main thread will wait with a 10 millisecond timeout on the condvar - /// and will leave the loop upon timeout. - /// - /// ``` - /// use std::sync::{Arc, Condvar, Mutex}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// # let handle = - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// - /// // Let's wait 20 milliseconds before notifying the condvar. - /// thread::sleep(Duration::from_millis(20)); - /// - /// let mut started = lock.lock().unwrap(); - /// // We update the boolean value. - /// *started = true; - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// loop { - /// // Let's put a timeout on the condvar's wait. - /// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed. - /// if result.1.timed_out() { - /// // timed out now and we can leave. - /// break - /// } - /// } - /// # // Prevent leaks for Miri. - /// # let _ = handle.join(); - /// ``` - #[must_use] - #[stable(feature = "wait_timeout", since = "1.5.0")] - pub fn timed_out(&self) -> bool { - self.0 - } -} diff --git a/crates/std/src/sync/mpmc/array.rs b/crates/std/src/sync/mpmc/array.rs deleted file mode 100644 index 880d8b5..0000000 --- a/crates/std/src/sync/mpmc/array.rs +++ /dev/null @@ -1,569 +0,0 @@ -//! Bounded channel based on a preallocated array. -//! -//! This flavor has a fixed, positive capacity. -//! -//! The implementation is based on Dmitry Vyukov's bounded MPMC queue. -//! -//! Source: -//! - -//! - - -use super::context::Context; -use super::error::*; -use super::select::{Operation, Selected, Token}; -use super::utils::{Backoff, CachePadded}; -use super::waker::SyncWaker; -use crate::cell::UnsafeCell; -use crate::mem::MaybeUninit; -use crate::ptr; -use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering}; -use crate::time::Instant; - -/// A slot in a channel. -struct Slot { - /// The current stamp. - stamp: Atomic, - - /// The message in this slot. Either read out in `read` or dropped through - /// `discard_all_messages`. - msg: UnsafeCell>, -} - -/// The token type for the array flavor. -#[derive(Debug)] -pub(crate) struct ArrayToken { - /// Slot to read from or write to. - slot: *const u8, - - /// Stamp to store into the slot after reading or writing. - stamp: usize, -} - -impl Default for ArrayToken { - #[inline] - fn default() -> Self { - ArrayToken { slot: ptr::null(), stamp: 0 } - } -} - -/// Bounded channel based on a preallocated array. -pub(crate) struct Channel { - /// The head of the channel. - /// - /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but - /// packed into a single `usize`. The lower bits represent the index, while the upper bits - /// represent the lap. The mark bit in the head is always zero. - /// - /// Messages are popped from the head of the channel. - head: CachePadded>, - - /// The tail of the channel. - /// - /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but - /// packed into a single `usize`. The lower bits represent the index, while the upper bits - /// represent the lap. The mark bit indicates that the channel is disconnected. - /// - /// Messages are pushed into the tail of the channel. - tail: CachePadded>, - - /// The buffer holding slots. - buffer: Box<[Slot]>, - - /// The channel capacity. - cap: usize, - - /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`. - one_lap: usize, - - /// If this bit is set in the tail, that means the channel is disconnected. - mark_bit: usize, - - /// Senders waiting while the channel is full. - senders: SyncWaker, - - /// Receivers waiting while the channel is empty and not disconnected. - receivers: SyncWaker, -} - -impl Channel { - /// Creates a bounded channel of capacity `cap`. - pub(crate) fn with_capacity(cap: usize) -> Self { - assert!(cap > 0, "capacity must be positive"); - - // Compute constants `mark_bit` and `one_lap`. - let mark_bit = (cap + 1).next_power_of_two(); - let one_lap = mark_bit * 2; - - // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`. - let head = 0; - // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`. - let tail = 0; - - // Allocate a buffer of `cap` slots initialized - // with stamps. - let buffer: Box<[Slot]> = (0..cap) - .map(|i| { - // Set the stamp to `{ lap: 0, mark: 0, index: i }`. - Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) } - }) - .collect(); - - Channel { - buffer, - cap, - one_lap, - mark_bit, - head: CachePadded::new(AtomicUsize::new(head)), - tail: CachePadded::new(AtomicUsize::new(tail)), - senders: SyncWaker::new(), - receivers: SyncWaker::new(), - } - } - - /// Attempts to reserve a slot for sending a message. - fn start_send(&self, token: &mut Token) -> bool { - let backoff = Backoff::new(); - let mut tail = self.tail.load(Ordering::Relaxed); - - loop { - // Check if the channel is disconnected. - if tail & self.mark_bit != 0 { - token.array.slot = ptr::null(); - token.array.stamp = 0; - return true; - } - - // Deconstruct the tail. - let index = tail & (self.mark_bit - 1); - let lap = tail & !(self.one_lap - 1); - - // Inspect the corresponding slot. - debug_assert!(index < self.buffer.len()); - let slot = unsafe { self.buffer.get_unchecked(index) }; - let stamp = slot.stamp.load(Ordering::Acquire); - - // If the tail and the stamp match, we may attempt to push. - if tail == stamp { - let new_tail = if index + 1 < self.cap { - // Same lap, incremented index. - // Set to `{ lap: lap, mark: 0, index: index + 1 }`. - tail + 1 - } else { - // One lap forward, index wraps around to zero. - // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. - lap.wrapping_add(self.one_lap) - }; - - // Try moving the tail. - match self.tail.compare_exchange_weak( - tail, - new_tail, - Ordering::SeqCst, - Ordering::Relaxed, - ) { - Ok(_) => { - // Prepare the token for the follow-up call to `write`. - token.array.slot = slot as *const Slot as *const u8; - token.array.stamp = tail + 1; - return true; - } - Err(_) => { - backoff.spin_light(); - tail = self.tail.load(Ordering::Relaxed); - } - } - } else if stamp.wrapping_add(self.one_lap) == tail + 1 { - atomic::fence(Ordering::SeqCst); - let head = self.head.load(Ordering::Relaxed); - - // If the head lags one lap behind the tail as well... - if head.wrapping_add(self.one_lap) == tail { - // ...then the channel is full. - return false; - } - - backoff.spin_light(); - tail = self.tail.load(Ordering::Relaxed); - } else { - // Snooze because we need to wait for the stamp to get updated. - backoff.spin_heavy(); - tail = self.tail.load(Ordering::Relaxed); - } - } - } - - /// Writes a message into the channel. - pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { - // If there is no slot, the channel is disconnected. - if token.array.slot.is_null() { - return Err(msg); - } - - // Write the message into the slot and update the stamp. - unsafe { - let slot: &Slot = &*(token.array.slot as *const Slot); - slot.msg.get().write(MaybeUninit::new(msg)); - slot.stamp.store(token.array.stamp, Ordering::Release); - } - - // Wake a sleeping receiver. - self.receivers.notify(); - Ok(()) - } - - /// Attempts to reserve a slot for receiving a message. - fn start_recv(&self, token: &mut Token) -> bool { - let backoff = Backoff::new(); - let mut head = self.head.load(Ordering::Relaxed); - - loop { - // Deconstruct the head. - let index = head & (self.mark_bit - 1); - let lap = head & !(self.one_lap - 1); - - // Inspect the corresponding slot. - debug_assert!(index < self.buffer.len()); - let slot = unsafe { self.buffer.get_unchecked(index) }; - let stamp = slot.stamp.load(Ordering::Acquire); - - // If the stamp is ahead of the head by 1, we may attempt to pop. - if head + 1 == stamp { - let new = if index + 1 < self.cap { - // Same lap, incremented index. - // Set to `{ lap: lap, mark: 0, index: index + 1 }`. - head + 1 - } else { - // One lap forward, index wraps around to zero. - // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. - lap.wrapping_add(self.one_lap) - }; - - // Try moving the head. - match self.head.compare_exchange_weak( - head, - new, - Ordering::SeqCst, - Ordering::Relaxed, - ) { - Ok(_) => { - // Prepare the token for the follow-up call to `read`. - token.array.slot = slot as *const Slot as *const u8; - token.array.stamp = head.wrapping_add(self.one_lap); - return true; - } - Err(_) => { - backoff.spin_light(); - head = self.head.load(Ordering::Relaxed); - } - } - } else if stamp == head { - atomic::fence(Ordering::SeqCst); - let tail = self.tail.load(Ordering::Relaxed); - - // If the tail equals the head, that means the channel is empty. - if (tail & !self.mark_bit) == head { - // If the channel is disconnected... - if tail & self.mark_bit != 0 { - // ...then receive an error. - token.array.slot = ptr::null(); - token.array.stamp = 0; - return true; - } else { - // Otherwise, the receive operation is not ready. - return false; - } - } - - backoff.spin_light(); - head = self.head.load(Ordering::Relaxed); - } else { - // Snooze because we need to wait for the stamp to get updated. - backoff.spin_heavy(); - head = self.head.load(Ordering::Relaxed); - } - } - } - - /// Reads a message from the channel. - pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { - if token.array.slot.is_null() { - // The channel is disconnected. - return Err(()); - } - - // Read the message from the slot and update the stamp. - let msg = unsafe { - let slot: &Slot = &*(token.array.slot as *const Slot); - - let msg = slot.msg.get().read().assume_init(); - slot.stamp.store(token.array.stamp, Ordering::Release); - msg - }; - - // Wake a sleeping sender. - self.senders.notify(); - Ok(msg) - } - - /// Attempts to send a message into the channel. - pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { - let token = &mut Token::default(); - if self.start_send(token) { - unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) } - } else { - Err(TrySendError::Full(msg)) - } - } - - /// Sends a message into the channel. - pub(crate) fn send( - &self, - msg: T, - deadline: Option, - ) -> Result<(), SendTimeoutError> { - let token = &mut Token::default(); - loop { - // Try sending a message. - if self.start_send(token) { - let res = unsafe { self.write(token, msg) }; - return res.map_err(SendTimeoutError::Disconnected); - } - - if let Some(d) = deadline { - if Instant::now() >= d { - return Err(SendTimeoutError::Timeout(msg)); - } - } - - Context::with(|cx| { - // Prepare for blocking until a receiver wakes us up. - let oper = Operation::hook(token); - self.senders.register(oper, cx); - - // Has the channel become ready just now? - if !self.is_full() || self.is_disconnected() { - let _ = cx.try_select(Selected::Aborted); - } - - // Block the current thread. - // SAFETY: the context belongs to the current thread. - let sel = unsafe { cx.wait_until(deadline) }; - - match sel { - Selected::Waiting => unreachable!(), - Selected::Aborted | Selected::Disconnected => { - self.senders.unregister(oper).unwrap(); - } - Selected::Operation(_) => {} - } - }); - } - } - - /// Attempts to receive a message without blocking. - pub(crate) fn try_recv(&self) -> Result { - let token = &mut Token::default(); - - if self.start_recv(token) { - unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } - } else { - Err(TryRecvError::Empty) - } - } - - /// Receives a message from the channel. - pub(crate) fn recv(&self, deadline: Option) -> Result { - let token = &mut Token::default(); - loop { - // Try receiving a message. - if self.start_recv(token) { - let res = unsafe { self.read(token) }; - return res.map_err(|_| RecvTimeoutError::Disconnected); - } - - if let Some(d) = deadline { - if Instant::now() >= d { - return Err(RecvTimeoutError::Timeout); - } - } - - Context::with(|cx| { - // Prepare for blocking until a sender wakes us up. - let oper = Operation::hook(token); - self.receivers.register(oper, cx); - - // Has the channel become ready just now? - if !self.is_empty() || self.is_disconnected() { - let _ = cx.try_select(Selected::Aborted); - } - - // Block the current thread. - // SAFETY: the context belongs to the current thread. - let sel = unsafe { cx.wait_until(deadline) }; - - match sel { - Selected::Waiting => unreachable!(), - Selected::Aborted | Selected::Disconnected => { - self.receivers.unregister(oper).unwrap(); - // If the channel was disconnected, we still have to check for remaining - // messages. - } - Selected::Operation(_) => {} - } - }); - } - } - - /// Returns the current number of messages inside the channel. - pub(crate) fn len(&self) -> usize { - loop { - // Load the tail, then load the head. - let tail = self.tail.load(Ordering::SeqCst); - let head = self.head.load(Ordering::SeqCst); - - // If the tail didn't change, we've got consistent values to work with. - if self.tail.load(Ordering::SeqCst) == tail { - let hix = head & (self.mark_bit - 1); - let tix = tail & (self.mark_bit - 1); - - return if hix < tix { - tix - hix - } else if hix > tix { - self.cap - hix + tix - } else if (tail & !self.mark_bit) == head { - 0 - } else { - self.cap - }; - } - } - } - - /// Returns the capacity of the channel. - #[allow(clippy::unnecessary_wraps)] // This is intentional. - pub(crate) fn capacity(&self) -> Option { - Some(self.cap) - } - - /// Disconnects senders and wakes up all blocked receivers. - /// - /// Returns `true` if this call disconnected the channel. - pub(crate) fn disconnect_senders(&self) -> bool { - let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); - - if tail & self.mark_bit == 0 { - self.receivers.disconnect(); - true - } else { - false - } - } - - /// Disconnects receivers and wakes up all blocked senders. - /// - /// Returns `true` if this call disconnected the channel. - /// - /// # Safety - /// May only be called once upon dropping the last receiver. The - /// destruction of all other receivers must have been observed with acquire - /// ordering or stronger. - pub(crate) unsafe fn disconnect_receivers(&self) -> bool { - let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst); - let disconnected = if tail & self.mark_bit == 0 { - self.senders.disconnect(); - true - } else { - false - }; - - unsafe { self.discard_all_messages(tail) }; - disconnected - } - - /// Discards all messages. - /// - /// `tail` should be the current (and therefore last) value of `tail`. - /// - /// # Panicking - /// If a destructor panics, the remaining messages are leaked, matching the - /// behavior of the unbounded channel. - /// - /// # Safety - /// This method must only be called when dropping the last receiver. The - /// destruction of all other receivers must have been observed with acquire - /// ordering or stronger. - unsafe fn discard_all_messages(&self, tail: usize) { - debug_assert!(self.is_disconnected()); - - // Only receivers modify `head`, so since we are the last one, - // this value will not change and will not be observed (since - // no new messages can be sent after disconnection). - let mut head = self.head.load(Ordering::Relaxed); - let tail = tail & !self.mark_bit; - - let backoff = Backoff::new(); - loop { - // Deconstruct the head. - let index = head & (self.mark_bit - 1); - let lap = head & !(self.one_lap - 1); - - // Inspect the corresponding slot. - debug_assert!(index < self.buffer.len()); - let slot = unsafe { self.buffer.get_unchecked(index) }; - let stamp = slot.stamp.load(Ordering::Acquire); - - // If the stamp is ahead of the head by 1, we may drop the message. - if head + 1 == stamp { - head = if index + 1 < self.cap { - // Same lap, incremented index. - // Set to `{ lap: lap, mark: 0, index: index + 1 }`. - head + 1 - } else { - // One lap forward, index wraps around to zero. - // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`. - lap.wrapping_add(self.one_lap) - }; - - unsafe { - (*slot.msg.get()).assume_init_drop(); - } - // If the tail equals the head, that means the channel is empty. - } else if tail == head { - return; - // Otherwise, a sender is about to write into the slot, so we need - // to wait for it to update the stamp. - } else { - backoff.spin_heavy(); - } - } - } - - /// Returns `true` if the channel is disconnected. - pub(crate) fn is_disconnected(&self) -> bool { - self.tail.load(Ordering::SeqCst) & self.mark_bit != 0 - } - - /// Returns `true` if the channel is empty. - pub(crate) fn is_empty(&self) -> bool { - let head = self.head.load(Ordering::SeqCst); - let tail = self.tail.load(Ordering::SeqCst); - - // Is the tail equal to the head? - // - // Note: If the head changes just before we load the tail, that means there was a moment - // when the channel was not empty, so it is safe to just return `false`. - (tail & !self.mark_bit) == head - } - - /// Returns `true` if the channel is full. - pub(crate) fn is_full(&self) -> bool { - let tail = self.tail.load(Ordering::SeqCst); - let head = self.head.load(Ordering::SeqCst); - - // Is the head lagging one lap behind tail? - // - // Note: If the tail changes just before we load the head, that means there was a moment - // when the channel was not full, so it is safe to just return `false`. - head.wrapping_add(self.one_lap) == tail & !self.mark_bit - } -} diff --git a/crates/std/src/sync/mpmc/context.rs b/crates/std/src/sync/mpmc/context.rs deleted file mode 100644 index 6b2f4cb..0000000 --- a/crates/std/src/sync/mpmc/context.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! Thread-local channel context. - -use super::select::Selected; -use super::waker::current_thread_id; -use crate::cell::Cell; -use crate::ptr; -use crate::sync::Arc; -use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering}; -use crate::thread::{self, Thread}; -use crate::time::Instant; - -/// Thread-local context. -#[derive(Debug, Clone)] -pub struct Context { - inner: Arc, -} - -/// Inner representation of `Context`. -#[derive(Debug)] -struct Inner { - /// Selected operation. - select: Atomic, - - /// A slot into which another thread may store a pointer to its `Packet`. - packet: Atomic<*mut ()>, - - /// Thread handle. - thread: Thread, - - /// Thread id. - thread_id: usize, -} - -impl Context { - /// Creates a new context for the duration of the closure. - #[inline] - pub fn with(f: F) -> R - where - F: FnOnce(&Context) -> R, - { - thread_local! { - /// Cached thread-local context. - static CONTEXT: Cell> = Cell::new(Some(Context::new())); - } - - let mut f = Some(f); - let mut f = |cx: &Context| -> R { - let f = f.take().unwrap(); - f(cx) - }; - - CONTEXT - .try_with(|cell| match cell.take() { - None => f(&Context::new()), - Some(cx) => { - cx.reset(); - let res = f(&cx); - cell.set(Some(cx)); - res - } - }) - .unwrap_or_else(|_| f(&Context::new())) - } - - /// Creates a new `Context`. - #[cold] - fn new() -> Context { - Context { - inner: Arc::new(Inner { - select: AtomicUsize::new(Selected::Waiting.into()), - packet: AtomicPtr::new(ptr::null_mut()), - thread: thread::current_or_unnamed(), - thread_id: current_thread_id(), - }), - } - } - - /// Resets `select` and `packet`. - #[inline] - fn reset(&self) { - self.inner.select.store(Selected::Waiting.into(), Ordering::Release); - self.inner.packet.store(ptr::null_mut(), Ordering::Release); - } - - /// Attempts to select an operation. - /// - /// On failure, the previously selected operation is returned. - #[inline] - pub fn try_select(&self, select: Selected) -> Result<(), Selected> { - self.inner - .select - .compare_exchange( - Selected::Waiting.into(), - select.into(), - Ordering::AcqRel, - Ordering::Acquire, - ) - .map(|_| ()) - .map_err(|e| e.into()) - } - - /// Stores a packet. - /// - /// This method must be called after `try_select` succeeds and there is a packet to provide. - #[inline] - pub fn store_packet(&self, packet: *mut ()) { - if !packet.is_null() { - self.inner.packet.store(packet, Ordering::Release); - } - } - - /// Waits until an operation is selected and returns it. - /// - /// If the deadline is reached, `Selected::Aborted` will be selected. - /// - /// # Safety - /// This may only be called from the thread this `Context` belongs to. - #[inline] - pub unsafe fn wait_until(&self, deadline: Option) -> Selected { - loop { - // Check whether an operation has been selected. - let sel = Selected::from(self.inner.select.load(Ordering::Acquire)); - if sel != Selected::Waiting { - return sel; - } - - // If there's a deadline, park the current thread until the deadline is reached. - if let Some(end) = deadline { - let now = Instant::now(); - - if now < end { - // SAFETY: guaranteed by caller. - unsafe { self.inner.thread.park_timeout(end - now) }; - } else { - // The deadline has been reached. Try aborting select. - return match self.try_select(Selected::Aborted) { - Ok(()) => Selected::Aborted, - Err(s) => s, - }; - } - } else { - // SAFETY: guaranteed by caller. - unsafe { self.inner.thread.park() }; - } - } - } - - /// Unparks the thread this context belongs to. - #[inline] - pub fn unpark(&self) { - self.inner.thread.unpark(); - } - - /// Returns the id of the thread this context belongs to. - #[inline] - pub fn thread_id(&self) -> usize { - self.inner.thread_id - } -} diff --git a/crates/std/src/sync/mpmc/counter.rs b/crates/std/src/sync/mpmc/counter.rs deleted file mode 100644 index efa6af1..0000000 --- a/crates/std/src/sync/mpmc/counter.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; -use crate::{ops, process}; - -/// Reference counter internals. -struct Counter { - /// The number of senders associated with the channel. - senders: Atomic, - - /// The number of receivers associated with the channel. - receivers: Atomic, - - /// Set to `true` if the last sender or the last receiver reference deallocates the channel. - destroy: Atomic, - - /// The internal channel. - chan: C, -} - -/// Wraps a channel into the reference counter. -pub(crate) fn new(chan: C) -> (Sender, Receiver) { - let counter = Box::into_raw(Box::new(Counter { - senders: AtomicUsize::new(1), - receivers: AtomicUsize::new(1), - destroy: AtomicBool::new(false), - chan, - })); - let s = Sender { counter }; - let r = Receiver { counter }; - (s, r) -} - -/// The sending side. -pub(crate) struct Sender { - counter: *mut Counter, -} - -impl Sender { - /// Returns the internal `Counter`. - fn counter(&self) -> &Counter { - unsafe { &*self.counter } - } - - /// Acquires another sender reference. - pub(crate) fn acquire(&self) -> Sender { - let count = self.counter().senders.fetch_add(1, Ordering::Relaxed); - - // Cloning senders and calling `mem::forget` on the clones could potentially overflow the - // counter. It's very difficult to recover sensibly from such degenerate scenarios so we - // just abort when the count becomes very large. - if count > isize::MAX as usize { - process::abort(); - } - - Sender { counter: self.counter } - } - - /// Releases the sender reference. - /// - /// Function `disconnect` will be called if this is the last sender reference. - pub(crate) unsafe fn release bool>(&self, disconnect: F) { - if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 { - disconnect(&self.counter().chan); - - if self.counter().destroy.swap(true, Ordering::AcqRel) { - drop(unsafe { Box::from_raw(self.counter) }); - } - } - } -} - -impl ops::Deref for Sender { - type Target = C; - - fn deref(&self) -> &C { - &self.counter().chan - } -} - -impl PartialEq for Sender { - fn eq(&self, other: &Sender) -> bool { - self.counter == other.counter - } -} - -/// The receiving side. -pub(crate) struct Receiver { - counter: *mut Counter, -} - -impl Receiver { - /// Returns the internal `Counter`. - fn counter(&self) -> &Counter { - unsafe { &*self.counter } - } - - /// Acquires another receiver reference. - pub(crate) fn acquire(&self) -> Receiver { - let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed); - - // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the - // counter. It's very difficult to recover sensibly from such degenerate scenarios so we - // just abort when the count becomes very large. - if count > isize::MAX as usize { - process::abort(); - } - - Receiver { counter: self.counter } - } - - /// Releases the receiver reference. - /// - /// Function `disconnect` will be called if this is the last receiver reference. - pub(crate) unsafe fn release bool>(&self, disconnect: F) { - if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 { - disconnect(&self.counter().chan); - - if self.counter().destroy.swap(true, Ordering::AcqRel) { - drop(unsafe { Box::from_raw(self.counter) }); - } - } - } -} - -impl ops::Deref for Receiver { - type Target = C; - - fn deref(&self) -> &C { - &self.counter().chan - } -} - -impl PartialEq for Receiver { - fn eq(&self, other: &Receiver) -> bool { - self.counter == other.counter - } -} diff --git a/crates/std/src/sync/mpmc/error.rs b/crates/std/src/sync/mpmc/error.rs deleted file mode 100644 index e34b56d..0000000 --- a/crates/std/src/sync/mpmc/error.rs +++ /dev/null @@ -1,49 +0,0 @@ -pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError}; -use crate::{error, fmt}; - -/// An error returned from the [`send_timeout`] method. -/// -/// The error contains the message being sent so it can be recovered. -/// -/// [`send_timeout`]: super::Sender::send_timeout -#[derive(PartialEq, Eq, Clone, Copy)] -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub enum SendTimeoutError { - /// The message could not be sent because the channel is full and the operation timed out. - /// - /// If this is a zero-capacity channel, then the error indicates that there was no receiver - /// available to receive the message and the operation timed out. - Timeout(T), - - /// The message could not be sent because the channel is disconnected. - Disconnected(T), -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl fmt::Debug for SendTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "SendTimeoutError(..)".fmt(f) - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl fmt::Display for SendTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f), - SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f), - } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl error::Error for SendTimeoutError {} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl From> for SendTimeoutError { - fn from(err: SendError) -> SendTimeoutError { - match err { - SendError(e) => SendTimeoutError::Disconnected(e), - } - } -} diff --git a/crates/std/src/sync/mpmc/list.rs b/crates/std/src/sync/mpmc/list.rs deleted file mode 100644 index 050f26b..0000000 --- a/crates/std/src/sync/mpmc/list.rs +++ /dev/null @@ -1,668 +0,0 @@ -//! Unbounded channel implemented as a linked list. - -use super::context::Context; -use super::error::*; -use super::select::{Operation, Selected, Token}; -use super::utils::{Backoff, CachePadded}; -use super::waker::SyncWaker; -use crate::cell::UnsafeCell; -use crate::marker::PhantomData; -use crate::mem::MaybeUninit; -use crate::ptr; -use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering}; -use crate::time::Instant; - -// Bits indicating the state of a slot: -// * If a message has been written into the slot, `WRITE` is set. -// * If a message has been read from the slot, `READ` is set. -// * If the block is being destroyed, `DESTROY` is set. -const WRITE: usize = 1; -const READ: usize = 2; -const DESTROY: usize = 4; - -// Each block covers one "lap" of indices. -const LAP: usize = 32; -// The maximum number of messages a block can hold. -const BLOCK_CAP: usize = LAP - 1; -// How many lower bits are reserved for metadata. -const SHIFT: usize = 1; -// Has two different purposes: -// * If set in head, indicates that the block is not the last one. -// * If set in tail, indicates that the channel is disconnected. -const MARK_BIT: usize = 1; - -/// A slot in a block. -struct Slot { - /// The message. - msg: UnsafeCell>, - - /// The state of the slot. - state: Atomic, -} - -impl Slot { - /// Waits until a message is written into the slot. - fn wait_write(&self) { - let backoff = Backoff::new(); - while self.state.load(Ordering::Acquire) & WRITE == 0 { - backoff.spin_heavy(); - } - } -} - -/// A block in a linked list. -/// -/// Each block in the list can hold up to `BLOCK_CAP` messages. -struct Block { - /// The next block in the linked list. - next: Atomic<*mut Block>, - - /// Slots for messages. - slots: [Slot; BLOCK_CAP], -} - -impl Block { - /// Creates an empty block. - fn new() -> Box> { - // SAFETY: This is safe because: - // [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized. - // [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4]. - // [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it - // holds a MaybeUninit. - // [4] `Slot::state` (Atomic) may be safely zero initialized. - unsafe { Box::new_zeroed().assume_init() } - } - - /// Waits until the next pointer is set. - fn wait_next(&self) -> *mut Block { - let backoff = Backoff::new(); - loop { - let next = self.next.load(Ordering::Acquire); - if !next.is_null() { - return next; - } - backoff.spin_heavy(); - } - } - - /// Sets the `DESTROY` bit in slots starting from `start` and destroys the block. - unsafe fn destroy(this: *mut Block, start: usize) { - // It is not necessary to set the `DESTROY` bit in the last slot because that slot has - // begun destruction of the block. - for i in start..BLOCK_CAP - 1 { - let slot = unsafe { (*this).slots.get_unchecked(i) }; - - // Mark the `DESTROY` bit if a thread is still using the slot. - if slot.state.load(Ordering::Acquire) & READ == 0 - && slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0 - { - // If a thread is still using the slot, it will continue destruction of the block. - return; - } - } - - // No thread is using the block, now it is safe to destroy it. - drop(unsafe { Box::from_raw(this) }); - } -} - -/// A position in a channel. -#[derive(Debug)] -struct Position { - /// The index in the channel. - index: Atomic, - - /// The block in the linked list. - block: Atomic<*mut Block>, -} - -/// The token type for the list flavor. -#[derive(Debug)] -pub(crate) struct ListToken { - /// The block of slots. - block: *const u8, - - /// The offset into the block. - offset: usize, -} - -impl Default for ListToken { - #[inline] - fn default() -> Self { - ListToken { block: ptr::null(), offset: 0 } - } -} - -/// Unbounded channel implemented as a linked list. -/// -/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are -/// represented as numbers of type `usize` and wrap on overflow. -/// -/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and -/// improve cache efficiency. -pub(crate) struct Channel { - /// The head of the channel. - head: CachePadded>, - - /// The tail of the channel. - tail: CachePadded>, - - /// Receivers waiting while the channel is empty and not disconnected. - receivers: SyncWaker, - - /// Indicates that dropping a `Channel` may drop messages of type `T`. - _marker: PhantomData, -} - -impl Channel { - /// Creates a new unbounded channel. - pub(crate) fn new() -> Self { - Channel { - head: CachePadded::new(Position { - block: AtomicPtr::new(ptr::null_mut()), - index: AtomicUsize::new(0), - }), - tail: CachePadded::new(Position { - block: AtomicPtr::new(ptr::null_mut()), - index: AtomicUsize::new(0), - }), - receivers: SyncWaker::new(), - _marker: PhantomData, - } - } - - /// Attempts to reserve a slot for sending a message. - fn start_send(&self, token: &mut Token) -> bool { - let backoff = Backoff::new(); - let mut tail = self.tail.index.load(Ordering::Acquire); - let mut block = self.tail.block.load(Ordering::Acquire); - let mut next_block = None; - - loop { - // Check if the channel is disconnected. - if tail & MARK_BIT != 0 { - token.list.block = ptr::null(); - return true; - } - - // Calculate the offset of the index into the block. - let offset = (tail >> SHIFT) % LAP; - - // If we reached the end of the block, wait until the next one is installed. - if offset == BLOCK_CAP { - backoff.spin_heavy(); - tail = self.tail.index.load(Ordering::Acquire); - block = self.tail.block.load(Ordering::Acquire); - continue; - } - - // If we're going to have to install the next block, allocate it in advance in order to - // make the wait for other threads as short as possible. - if offset + 1 == BLOCK_CAP && next_block.is_none() { - next_block = Some(Block::::new()); - } - - // If this is the first message to be sent into the channel, we need to allocate the - // first block and install it. - if block.is_null() { - let new = Box::into_raw(Block::::new()); - - if self - .tail - .block - .compare_exchange(block, new, Ordering::Release, Ordering::Relaxed) - .is_ok() - { - // This yield point leaves the channel in a half-initialized state where the - // tail.block pointer is set but the head.block is not. This is used to - // facilitate the test in src/tools/miri/tests/pass/issues/issue-139553.rs - #[cfg(miri)] - crate::thread::yield_now(); - self.head.block.store(new, Ordering::Release); - block = new; - } else { - next_block = unsafe { Some(Box::from_raw(new)) }; - tail = self.tail.index.load(Ordering::Acquire); - block = self.tail.block.load(Ordering::Acquire); - continue; - } - } - - let new_tail = tail + (1 << SHIFT); - - // Try advancing the tail forward. - match self.tail.index.compare_exchange_weak( - tail, - new_tail, - Ordering::SeqCst, - Ordering::Acquire, - ) { - Ok(_) => unsafe { - // If we've reached the end of the block, install the next one. - if offset + 1 == BLOCK_CAP { - let next_block = Box::into_raw(next_block.unwrap()); - self.tail.block.store(next_block, Ordering::Release); - self.tail.index.fetch_add(1 << SHIFT, Ordering::Release); - (*block).next.store(next_block, Ordering::Release); - } - - token.list.block = block as *const u8; - token.list.offset = offset; - return true; - }, - Err(_) => { - backoff.spin_light(); - tail = self.tail.index.load(Ordering::Acquire); - block = self.tail.block.load(Ordering::Acquire); - } - } - } - } - - /// Writes a message into the channel. - pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { - // If there is no slot, the channel is disconnected. - if token.list.block.is_null() { - return Err(msg); - } - - // Write the message into the slot. - let block = token.list.block as *mut Block; - let offset = token.list.offset; - unsafe { - let slot = (*block).slots.get_unchecked(offset); - slot.msg.get().write(MaybeUninit::new(msg)); - slot.state.fetch_or(WRITE, Ordering::Release); - } - - // Wake a sleeping receiver. - self.receivers.notify(); - Ok(()) - } - - /// Attempts to reserve a slot for receiving a message. - fn start_recv(&self, token: &mut Token) -> bool { - let backoff = Backoff::new(); - let mut head = self.head.index.load(Ordering::Acquire); - let mut block = self.head.block.load(Ordering::Acquire); - - loop { - // Calculate the offset of the index into the block. - let offset = (head >> SHIFT) % LAP; - - // If we reached the end of the block, wait until the next one is installed. - if offset == BLOCK_CAP { - backoff.spin_heavy(); - head = self.head.index.load(Ordering::Acquire); - block = self.head.block.load(Ordering::Acquire); - continue; - } - - let mut new_head = head + (1 << SHIFT); - - if new_head & MARK_BIT == 0 { - atomic::fence(Ordering::SeqCst); - let tail = self.tail.index.load(Ordering::Relaxed); - - // If the tail equals the head, that means the channel is empty. - if head >> SHIFT == tail >> SHIFT { - // If the channel is disconnected... - if tail & MARK_BIT != 0 { - // ...then receive an error. - token.list.block = ptr::null(); - return true; - } else { - // Otherwise, the receive operation is not ready. - return false; - } - } - - // If head and tail are not in the same block, set `MARK_BIT` in head. - if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP { - new_head |= MARK_BIT; - } - } - - // The block can be null here only if the first message is being sent into the channel. - // In that case, just wait until it gets initialized. - if block.is_null() { - backoff.spin_heavy(); - head = self.head.index.load(Ordering::Acquire); - block = self.head.block.load(Ordering::Acquire); - continue; - } - - // Try moving the head index forward. - match self.head.index.compare_exchange_weak( - head, - new_head, - Ordering::SeqCst, - Ordering::Acquire, - ) { - Ok(_) => unsafe { - // If we've reached the end of the block, move to the next one. - if offset + 1 == BLOCK_CAP { - let next = (*block).wait_next(); - let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT); - if !(*next).next.load(Ordering::Relaxed).is_null() { - next_index |= MARK_BIT; - } - - self.head.block.store(next, Ordering::Release); - self.head.index.store(next_index, Ordering::Release); - } - - token.list.block = block as *const u8; - token.list.offset = offset; - return true; - }, - Err(_) => { - backoff.spin_light(); - head = self.head.index.load(Ordering::Acquire); - block = self.head.block.load(Ordering::Acquire); - } - } - } - } - - /// Reads a message from the channel. - pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { - if token.list.block.is_null() { - // The channel is disconnected. - return Err(()); - } - - // Read the message. - let block = token.list.block as *mut Block; - let offset = token.list.offset; - unsafe { - let slot = (*block).slots.get_unchecked(offset); - slot.wait_write(); - let msg = slot.msg.get().read().assume_init(); - - // Destroy the block if we've reached the end, or if another thread wanted to destroy but - // couldn't because we were busy reading from the slot. - if offset + 1 == BLOCK_CAP { - Block::destroy(block, 0); - } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 { - Block::destroy(block, offset + 1); - } - - Ok(msg) - } - } - - /// Attempts to send a message into the channel. - pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { - self.send(msg, None).map_err(|err| match err { - SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg), - SendTimeoutError::Timeout(_) => unreachable!(), - }) - } - - /// Sends a message into the channel. - pub(crate) fn send( - &self, - msg: T, - _deadline: Option, - ) -> Result<(), SendTimeoutError> { - let token = &mut Token::default(); - assert!(self.start_send(token)); - unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) } - } - - /// Attempts to receive a message without blocking. - pub(crate) fn try_recv(&self) -> Result { - let token = &mut Token::default(); - - if self.start_recv(token) { - unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } - } else { - Err(TryRecvError::Empty) - } - } - - /// Receives a message from the channel. - pub(crate) fn recv(&self, deadline: Option) -> Result { - let token = &mut Token::default(); - loop { - if self.start_recv(token) { - unsafe { - return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); - } - } - - if let Some(d) = deadline { - if Instant::now() >= d { - return Err(RecvTimeoutError::Timeout); - } - } - - // Prepare for blocking until a sender wakes us up. - Context::with(|cx| { - let oper = Operation::hook(token); - self.receivers.register(oper, cx); - - // Has the channel become ready just now? - if !self.is_empty() || self.is_disconnected() { - let _ = cx.try_select(Selected::Aborted); - } - - // Block the current thread. - // SAFETY: the context belongs to the current thread. - let sel = unsafe { cx.wait_until(deadline) }; - - match sel { - Selected::Waiting => unreachable!(), - Selected::Aborted | Selected::Disconnected => { - self.receivers.unregister(oper).unwrap(); - // If the channel was disconnected, we still have to check for remaining - // messages. - } - Selected::Operation(_) => {} - } - }); - } - } - - /// Returns the current number of messages inside the channel. - pub(crate) fn len(&self) -> usize { - loop { - // Load the tail index, then load the head index. - let mut tail = self.tail.index.load(Ordering::SeqCst); - let mut head = self.head.index.load(Ordering::SeqCst); - - // If the tail index didn't change, we've got consistent indices to work with. - if self.tail.index.load(Ordering::SeqCst) == tail { - // Erase the lower bits. - tail &= !((1 << SHIFT) - 1); - head &= !((1 << SHIFT) - 1); - - // Fix up indices if they fall onto block ends. - if (tail >> SHIFT) & (LAP - 1) == LAP - 1 { - tail = tail.wrapping_add(1 << SHIFT); - } - if (head >> SHIFT) & (LAP - 1) == LAP - 1 { - head = head.wrapping_add(1 << SHIFT); - } - - // Rotate indices so that head falls into the first block. - let lap = (head >> SHIFT) / LAP; - tail = tail.wrapping_sub((lap * LAP) << SHIFT); - head = head.wrapping_sub((lap * LAP) << SHIFT); - - // Remove the lower bits. - tail >>= SHIFT; - head >>= SHIFT; - - // Return the difference minus the number of blocks between tail and head. - return tail - head - tail / LAP; - } - } - } - - /// Returns the capacity of the channel. - pub(crate) fn capacity(&self) -> Option { - None - } - - /// Disconnects senders and wakes up all blocked receivers. - /// - /// Returns `true` if this call disconnected the channel. - pub(crate) fn disconnect_senders(&self) -> bool { - let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); - - if tail & MARK_BIT == 0 { - self.receivers.disconnect(); - true - } else { - false - } - } - - /// Disconnects receivers. - /// - /// Returns `true` if this call disconnected the channel. - pub(crate) fn disconnect_receivers(&self) -> bool { - let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst); - - if tail & MARK_BIT == 0 { - // If receivers are dropped first, discard all messages to free - // memory eagerly. - self.discard_all_messages(); - true - } else { - false - } - } - - /// Discards all messages. - /// - /// This method should only be called when all receivers are dropped. - fn discard_all_messages(&self) { - let backoff = Backoff::new(); - let mut tail = self.tail.index.load(Ordering::Acquire); - loop { - let offset = (tail >> SHIFT) % LAP; - if offset != BLOCK_CAP { - break; - } - - // New updates to tail will be rejected by MARK_BIT and aborted unless it's - // at boundary. We need to wait for the updates take affect otherwise there - // can be memory leaks. - backoff.spin_heavy(); - tail = self.tail.index.load(Ordering::Acquire); - } - - let mut head = self.head.index.load(Ordering::Acquire); - // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts - // to initialize the first block before noticing that the receivers disconnected. Late allocations - // will be deallocated by the sender in Drop. - let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); - - // If we're going to be dropping messages we need to synchronize with initialization - if head >> SHIFT != tail >> SHIFT { - // The block can be null here only if a sender is in the process of initializing the - // channel while another sender managed to send a message by inserting it into the - // semi-initialized channel and advanced the tail. - // In that case, just wait until it gets initialized. - while block.is_null() { - backoff.spin_heavy(); - block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); - } - } - // After this point `head.block` is not modified again and it will be deallocated if it's - // non-null. The `Drop` code of the channel, which runs after this function, also attempts - // to deallocate `head.block` if it's non-null. Therefore this function must maintain the - // invariant that if a deallocation of head.block is attempted then it must also be set to - // NULL. Failing to do so will lead to the Drop code attempting a double free. For this - // reason both reads above do an atomic swap instead of a simple atomic load. - - unsafe { - // Drop all messages between head and tail and deallocate the heap-allocated blocks. - while head >> SHIFT != tail >> SHIFT { - let offset = (head >> SHIFT) % LAP; - - if offset < BLOCK_CAP { - // Drop the message in the slot. - let slot = (*block).slots.get_unchecked(offset); - slot.wait_write(); - let p = &mut *slot.msg.get(); - p.as_mut_ptr().drop_in_place(); - } else { - (*block).wait_next(); - // Deallocate the block and move to the next one. - let next = (*block).next.load(Ordering::Acquire); - drop(Box::from_raw(block)); - block = next; - } - - head = head.wrapping_add(1 << SHIFT); - } - - // Deallocate the last remaining block. - if !block.is_null() { - drop(Box::from_raw(block)); - } - } - - head &= !MARK_BIT; - self.head.index.store(head, Ordering::Release); - } - - /// Returns `true` if the channel is disconnected. - pub(crate) fn is_disconnected(&self) -> bool { - self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0 - } - - /// Returns `true` if the channel is empty. - pub(crate) fn is_empty(&self) -> bool { - let head = self.head.index.load(Ordering::SeqCst); - let tail = self.tail.index.load(Ordering::SeqCst); - head >> SHIFT == tail >> SHIFT - } - - /// Returns `true` if the channel is full. - pub(crate) fn is_full(&self) -> bool { - false - } -} - -impl Drop for Channel { - fn drop(&mut self) { - let mut head = self.head.index.load(Ordering::Relaxed); - let mut tail = self.tail.index.load(Ordering::Relaxed); - let mut block = self.head.block.load(Ordering::Relaxed); - - // Erase the lower bits. - head &= !((1 << SHIFT) - 1); - tail &= !((1 << SHIFT) - 1); - - unsafe { - // Drop all messages between head and tail and deallocate the heap-allocated blocks. - while head != tail { - let offset = (head >> SHIFT) % LAP; - - if offset < BLOCK_CAP { - // Drop the message in the slot. - let slot = (*block).slots.get_unchecked(offset); - let p = &mut *slot.msg.get(); - p.as_mut_ptr().drop_in_place(); - } else { - // Deallocate the block and move to the next one. - let next = (*block).next.load(Ordering::Relaxed); - drop(Box::from_raw(block)); - block = next; - } - - head = head.wrapping_add(1 << SHIFT); - } - - // Deallocate the last remaining block. - if !block.is_null() { - drop(Box::from_raw(block)); - } - } - } -} diff --git a/crates/std/src/sync/mpmc/mod.rs b/crates/std/src/sync/mpmc/mod.rs deleted file mode 100644 index 8df81a5..0000000 --- a/crates/std/src/sync/mpmc/mod.rs +++ /dev/null @@ -1,1388 +0,0 @@ -//! Multi-producer, multi-consumer FIFO queue communication primitives. -//! -//! This module provides message-based communication over channels, concretely -//! defined by two types: -//! -//! * [`Sender`] -//! * [`Receiver`] -//! -//! [`Sender`]s are used to send data to a set of [`Receiver`]s where each item -//! sent is delivered to (at most) one receiver. Both sender and receiver are -//! cloneable (multi-producer) such that many threads can send simultaneously -//! to receivers (multi-consumer). -//! -//! These channels come in two flavors: -//! -//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function -//! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. -//! -//! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(Sender, Receiver)` tuple where the storage for pending -//! messages is a pre-allocated buffer of a fixed size. All sends will be -//! **synchronous** by blocking until there is buffer space available. Note -//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" -//! channel where each sender atomically hands off a message to a receiver. -//! -//! [`send`]: Sender::send -//! -//! ## Disconnection -//! -//! The send and receive operations on channels will all return a [`Result`] -//! indicating whether the operation succeeded or not. An unsuccessful operation -//! is normally indicative of the other half of a channel having "hung up" by -//! being dropped in its corresponding thread. -//! -//! Once half of a channel has been deallocated, most operations can no longer -//! continue to make progress, so [`Err`] will be returned. Many applications -//! will continue to [`unwrap`] the results returned from this module, -//! instigating a propagation of failure among threads if one unexpectedly dies. -//! -//! [`unwrap`]: Result::unwrap -//! -//! # Examples -//! -//! Simple usage: -//! -//! ``` -//! #![feature(mpmc_channel)] -//! -//! use std::thread; -//! use std::sync::mpmc::channel; -//! -//! // Create a simple streaming channel -//! let (tx, rx) = channel(); -//! thread::spawn(move || { -//! tx.send(10).unwrap(); -//! }); -//! assert_eq!(rx.recv().unwrap(), 10); -//! ``` -//! -//! Shared usage: -//! -//! ``` -//! #![feature(mpmc_channel)] -//! -//! use std::thread; -//! use std::sync::mpmc::channel; -//! -//! thread::scope(|s| { -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! s.spawn(move || { -//! tx.send(i).unwrap(); -//! }); -//! } -//! -//! for _ in 0..5 { -//! let rx1 = rx.clone(); -//! let rx2 = rx.clone(); -//! s.spawn(move || { -//! let j = rx1.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! s.spawn(move || { -//! let j = rx2.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! }); -//! } -//! }) -//! ``` -//! -//! Propagating panics: -//! -//! ``` -//! #![feature(mpmc_channel)] -//! -//! use std::sync::mpmc::channel; -//! -//! // The call to recv() will return an error because the channel has already -//! // hung up (or been deallocated) -//! let (tx, rx) = channel::(); -//! drop(tx); -//! assert!(rx.recv().is_err()); -//! ``` - -// This module is used as the implementation for the channels in `sync::mpsc`. -// The implementation comes from the crossbeam-channel crate: -// -// Copyright (c) 2019 The Crossbeam Project Developers -// -// Permission is hereby granted, free of charge, to any -// person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the -// Software without restriction, including without -// limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice -// shall be included in all copies or substantial portions -// of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -mod array; -mod context; -mod counter; -mod error; -mod list; -mod select; -mod utils; -mod waker; -mod zero; - -pub use error::*; - -use crate::fmt; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::time::{Duration, Instant}; - -/// Creates a new asynchronous channel, returning the sender/receiver halves. -/// -/// All data sent on the [`Sender`] will become available on the [`Receiver`] in -/// the same order as it was sent, and no [`send`] will block the calling thread -/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will -/// block after its buffer limit is reached). [`recv`] will block until a message -/// is available while there is at least one [`Sender`] alive (including clones). -/// -/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times. -/// The [`Receiver`] also can be cloned to have multi receivers. -/// -/// If the [`Receiver`] is disconnected while trying to [`send`] with the -/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the -/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will -/// return a [`RecvError`]. -/// -/// [`send`]: Sender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// -/// // Spawn off an expensive computation -/// thread::spawn(move || { -/// # fn expensive_computation() {} -/// sender.send(expensive_computation()).unwrap(); -/// }); -/// -/// // Do some useful work for a while -/// -/// // Let's see what that answer was -/// println!("{:?}", receiver.recv().unwrap()); -/// ``` -#[must_use] -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub fn channel() -> (Sender, Receiver) { - let (s, r) = counter::new(list::Channel::new()); - let s = Sender { flavor: SenderFlavor::List(s) }; - let r = Receiver { flavor: ReceiverFlavor::List(r) }; - (s, r) -} - -/// Creates a new synchronous, bounded channel. -/// -/// All data sent on the [`Sender`] will become available on the [`Receiver`] -/// in the same order as it was sent. Like asynchronous [`channel`]s, the -/// [`Receiver`] will block until a message becomes available. `sync_channel` -/// differs greatly in the semantics of the sender, however. -/// -/// This channel has an internal buffer on which messages will be queued. -/// `bound` specifies the buffer size. When the internal buffer becomes full, -/// future sends will *block* waiting for the buffer to open up. Note that a -/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" -/// where each [`send`] will not return until a [`recv`] is paired with it. -/// -/// The [`Sender`] can be cloned to [`send`] to the same channel multiple -/// times. The [`Receiver`] also can be cloned to have multi receivers. -/// -/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying -/// to [`send`] with the [`Sender`], the [`send`] method will return a -/// [`SendError`]. Similarly, If the [`Sender`] is disconnected while trying -/// to [`recv`], the [`recv`] method will return a [`RecvError`]. -/// -/// [`send`]: Sender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// let (sender, receiver) = sync_channel(1); -/// -/// // this returns immediately -/// sender.send(1).unwrap(); -/// -/// thread::spawn(move || { -/// // this will block until the previous message has been received -/// sender.send(2).unwrap(); -/// }); -/// -/// assert_eq!(receiver.recv().unwrap(), 1); -/// assert_eq!(receiver.recv().unwrap(), 2); -/// ``` -#[must_use] -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub fn sync_channel(cap: usize) -> (Sender, Receiver) { - if cap == 0 { - let (s, r) = counter::new(zero::Channel::new()); - let s = Sender { flavor: SenderFlavor::Zero(s) }; - let r = Receiver { flavor: ReceiverFlavor::Zero(r) }; - (s, r) - } else { - let (s, r) = counter::new(array::Channel::with_capacity(cap)); - let s = Sender { flavor: SenderFlavor::Array(s) }; - let r = Receiver { flavor: ReceiverFlavor::Array(r) }; - (s, r) - } -} - -/// The sending-half of Rust's synchronous [`channel`] type. -/// -/// Messages can be sent through this channel with [`send`]. -/// -/// Note: all senders (the original and its clones) need to be dropped for the receiver -/// to stop blocking to receive messages with [`Receiver::recv`]. -/// -/// [`send`]: Sender::send -/// -/// # Examples -/// -/// ```rust -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// let sender2 = sender.clone(); -/// -/// // First thread owns sender -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// }); -/// -/// // Second thread owns sender2 -/// thread::spawn(move || { -/// sender2.send(2).unwrap(); -/// }); -/// -/// let msg = receiver.recv().unwrap(); -/// let msg2 = receiver.recv().unwrap(); -/// -/// assert_eq!(3, msg + msg2); -/// ``` -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub struct Sender { - flavor: SenderFlavor, -} - -/// Sender flavors. -enum SenderFlavor { - /// Bounded channel based on a preallocated array. - Array(counter::Sender>), - - /// Unbounded channel implemented as a linked list. - List(counter::Sender>), - - /// Zero-capacity channel. - Zero(counter::Sender>), -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -unsafe impl Send for Sender {} -#[unstable(feature = "mpmc_channel", issue = "126840")] -unsafe impl Sync for Sender {} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl UnwindSafe for Sender {} -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl RefUnwindSafe for Sender {} - -impl Sender { - /// Attempts to send a message into the channel without blocking. - /// - /// This method will either send a message into the channel immediately or return an error if - /// the channel is full or disconnected. The returned error contains the original message. - /// - /// If called on a zero-capacity channel, this method will send the message only if there - /// happens to be a receive operation on the other side of the channel at the same time. - /// - /// # Examples - /// - /// ```rust - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::{channel, Receiver, Sender}; - /// - /// let (sender, _receiver): (Sender, Receiver) = channel(); - /// - /// assert!(sender.try_send(1).is_ok()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn try_send(&self, msg: T) -> Result<(), TrySendError> { - match &self.flavor { - SenderFlavor::Array(chan) => chan.try_send(msg), - SenderFlavor::List(chan) => chan.try_send(msg), - SenderFlavor::Zero(chan) => chan.try_send(msg), - } - } - - /// Attempts to send a value on this channel, returning it back if it could - /// not be sent. - /// - /// A successful send occurs when it is determined that the other end of - /// the channel has not hung up already. An unsuccessful send would be one - /// where the corresponding receiver has already been deallocated. Note - /// that a return value of [`Err`] means that the data will never be - /// received, but a return value of [`Ok`] does *not* mean that the data - /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. However, if - /// the channel is zero-capacity, it acts as a rendezvous channel and a - /// return value of [`Ok`] means that the data has been received. - /// - /// If the channel is full and not disconnected, this call will block until - /// the send operation can proceed. If the channel becomes disconnected, - /// this call will wake up and return an error. The returned error contains - /// the original message. - /// - /// If called on a zero-capacity channel, this method will wait for a receive - /// operation to appear on the other side of the channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::channel; - /// - /// let (tx, rx) = channel(); - /// - /// // This send is always successful - /// tx.send(1).unwrap(); - /// - /// // This send will fail because the receiver is gone - /// drop(rx); - /// assert!(tx.send(1).is_err()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn send(&self, msg: T) -> Result<(), SendError> { - match &self.flavor { - SenderFlavor::Array(chan) => chan.send(msg, None), - SenderFlavor::List(chan) => chan.send(msg, None), - SenderFlavor::Zero(chan) => chan.send(msg, None), - } - .map_err(|err| match err { - SendTimeoutError::Disconnected(msg) => SendError(msg), - SendTimeoutError::Timeout(_) => unreachable!(), - }) - } -} - -impl Sender { - /// Waits for a message to be sent into the channel, but only for a limited time. - /// - /// If the channel is full and not disconnected, this call will block until the send operation - /// can proceed or the operation times out. If the channel becomes disconnected, this call will - /// wake up and return an error. The returned error contains the original message. - /// - /// If called on a zero-capacity channel, this method will wait for a receive operation to - /// appear on the other side of the channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::channel; - /// use std::time::Duration; - /// - /// let (tx, rx) = channel(); - /// - /// tx.send_timeout(1, Duration::from_millis(400)).unwrap(); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError> { - match Instant::now().checked_add(timeout) { - Some(deadline) => self.send_deadline(msg, deadline), - // So far in the future that it's practically the same as waiting indefinitely. - None => self.send(msg).map_err(SendTimeoutError::from), - } - } - - /// Waits for a message to be sent into the channel, but only until a given deadline. - /// - /// If the channel is full and not disconnected, this call will block until the send operation - /// can proceed or the operation times out. If the channel becomes disconnected, this call will - /// wake up and return an error. The returned error contains the original message. - /// - /// If called on a zero-capacity channel, this method will wait for a receive operation to - /// appear on the other side of the channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::channel; - /// use std::time::{Duration, Instant}; - /// - /// let (tx, rx) = channel(); - /// - /// let t = Instant::now() + Duration::from_millis(400); - /// tx.send_deadline(1, t).unwrap(); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError> { - match &self.flavor { - SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)), - SenderFlavor::List(chan) => chan.send(msg, Some(deadline)), - SenderFlavor::Zero(chan) => chan.send(msg, Some(deadline)), - } - } - - /// Returns `true` if the channel is empty. - /// - /// Note: Zero-capacity channels are always empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, _recv) = mpmc::channel(); - /// - /// let tx1 = send.clone(); - /// let tx2 = send.clone(); - /// - /// assert!(tx1.is_empty()); - /// - /// let handle = thread::spawn(move || { - /// tx2.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert!(!tx1.is_empty()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn is_empty(&self) -> bool { - match &self.flavor { - SenderFlavor::Array(chan) => chan.is_empty(), - SenderFlavor::List(chan) => chan.is_empty(), - SenderFlavor::Zero(chan) => chan.is_empty(), - } - } - - /// Returns `true` if the channel is full. - /// - /// Note: Zero-capacity channels are always full. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, _recv) = mpmc::sync_channel(1); - /// - /// let (tx1, tx2) = (send.clone(), send.clone()); - /// assert!(!tx1.is_full()); - /// - /// let handle = thread::spawn(move || { - /// tx2.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert!(tx1.is_full()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn is_full(&self) -> bool { - match &self.flavor { - SenderFlavor::Array(chan) => chan.is_full(), - SenderFlavor::List(chan) => chan.is_full(), - SenderFlavor::Zero(chan) => chan.is_full(), - } - } - - /// Returns the number of messages in the channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, _recv) = mpmc::channel(); - /// let (tx1, tx2) = (send.clone(), send.clone()); - /// - /// assert_eq!(tx1.len(), 0); - /// - /// let handle = thread::spawn(move || { - /// tx2.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(tx1.len(), 1); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn len(&self) -> usize { - match &self.flavor { - SenderFlavor::Array(chan) => chan.len(), - SenderFlavor::List(chan) => chan.len(), - SenderFlavor::Zero(chan) => chan.len(), - } - } - - /// If the channel is bounded, returns its capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, _recv) = mpmc::sync_channel(3); - /// let (tx1, tx2) = (send.clone(), send.clone()); - /// - /// assert_eq!(tx1.capacity(), Some(3)); - /// - /// let handle = thread::spawn(move || { - /// tx2.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(tx1.capacity(), Some(3)); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn capacity(&self) -> Option { - match &self.flavor { - SenderFlavor::Array(chan) => chan.capacity(), - SenderFlavor::List(chan) => chan.capacity(), - SenderFlavor::Zero(chan) => chan.capacity(), - } - } - - /// Returns `true` if senders belong to the same channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// - /// let (tx1, _) = mpmc::channel::(); - /// let (tx2, _) = mpmc::channel::(); - /// - /// assert!(tx1.same_channel(&tx1)); - /// assert!(!tx1.same_channel(&tx2)); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn same_channel(&self, other: &Sender) -> bool { - match (&self.flavor, &other.flavor) { - (SenderFlavor::Array(a), SenderFlavor::Array(b)) => a == b, - (SenderFlavor::List(a), SenderFlavor::List(b)) => a == b, - (SenderFlavor::Zero(a), SenderFlavor::Zero(b)) => a == b, - _ => false, - } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl Drop for Sender { - fn drop(&mut self) { - unsafe { - match &self.flavor { - SenderFlavor::Array(chan) => chan.release(|c| c.disconnect_senders()), - SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()), - SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()), - } - } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl Clone for Sender { - fn clone(&self) -> Self { - let flavor = match &self.flavor { - SenderFlavor::Array(chan) => SenderFlavor::Array(chan.acquire()), - SenderFlavor::List(chan) => SenderFlavor::List(chan.acquire()), - SenderFlavor::Zero(chan) => SenderFlavor::Zero(chan.acquire()), - }; - - Sender { flavor } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish_non_exhaustive() - } -} - -/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Receiver`] by cloning it. -/// -/// Messages sent to the channel can be retrieved using [`recv`]. -/// -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ```rust -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (send, recv) = channel(); -/// -/// let tx_thread = thread::spawn(move || { -/// send.send("Hello world!").unwrap(); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// send.send("Delayed for 2 seconds").unwrap(); -/// }); -/// -/// let (rx1, rx2) = (recv.clone(), recv.clone()); -/// let rx_thread_1 = thread::spawn(move || { -/// println!("{}", rx1.recv().unwrap()); // Received immediately -/// }); -/// let rx_thread_2 = thread::spawn(move || { -/// println!("{}", rx2.recv().unwrap()); // Received after 2 seconds -/// }); -/// -/// tx_thread.join().unwrap(); -/// rx_thread_1.join().unwrap(); -/// rx_thread_2.join().unwrap(); -/// ``` -#[unstable(feature = "mpmc_channel", issue = "126840")] -pub struct Receiver { - flavor: ReceiverFlavor, -} - -/// An iterator over messages on a [`Receiver`], created by [`iter`]. -/// -/// This iterator will block whenever [`next`] is called, -/// waiting for a new message, and [`None`] will be returned -/// when the corresponding channel has hung up. -/// -/// [`iter`]: Receiver::iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[unstable(feature = "mpmc_channel", issue = "126840")] -#[derive(Debug)] -pub struct Iter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An iterator that attempts to yield all pending values for a [`Receiver`], -/// created by [`try_iter`]. -/// -/// [`None`] will be returned when there are no pending values remaining or -/// if the corresponding channel has hung up. -/// -/// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. -/// -/// [`try_iter`]: Receiver::try_iter -/// -/// # Examples -/// -/// ```rust -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = channel(); -/// -/// // Nothing is in the buffer yet -/// assert!(receiver.try_iter().next().is_none()); -/// println!("Nothing in the buffer..."); -/// -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// sender.send(2).unwrap(); -/// sender.send(3).unwrap(); -/// }); -/// -/// println!("Going to sleep..."); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// -/// for x in receiver.try_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[unstable(feature = "mpmc_channel", issue = "126840")] -#[derive(Debug)] -pub struct TryIter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An owning iterator over messages on a [`Receiver`], -/// created by [`into_iter`]. -/// -/// This iterator will block whenever [`next`] -/// is called, waiting for a new message, and [`None`] will be -/// returned if the corresponding channel has hung up. -/// -/// [`into_iter`]: Receiver::into_iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// #![feature(mpmc_channel)] -/// -/// use std::sync::mpmc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.into_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[unstable(feature = "mpmc_channel", issue = "126840")] -#[derive(Debug)] -pub struct IntoIter { - rx: Receiver, -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl<'a, T> Iterator for TryIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.try_recv().ok() - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl<'a, T> IntoIterator for &'a Receiver { - type Item = T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl Iterator for IntoIter { - type Item = T; - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl IntoIterator for Receiver { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter { rx: self } - } -} - -/// Receiver flavors. -enum ReceiverFlavor { - /// Bounded channel based on a preallocated array. - Array(counter::Receiver>), - - /// Unbounded channel implemented as a linked list. - List(counter::Receiver>), - - /// Zero-capacity channel. - Zero(counter::Receiver>), -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -unsafe impl Send for Receiver {} -#[unstable(feature = "mpmc_channel", issue = "126840")] -unsafe impl Sync for Receiver {} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl UnwindSafe for Receiver {} -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl RefUnwindSafe for Receiver {} - -impl Receiver { - /// Attempts to receive a message from the channel without blocking. - /// - /// This method will never block the caller in order to wait for data to - /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. - /// - /// If called on a zero-capacity channel, this method will receive a message only if there - /// happens to be a send operation on the other side of the channel at the same time. - /// - /// This is useful for a flavor of "optimistic check" before deciding to - /// block on a receiver. - /// - /// Compared with [`recv`], this function has two failure cases instead of one - /// (one for disconnection, one for an empty buffer). - /// - /// [`recv`]: Self::recv - /// - /// # Examples - /// - /// ```rust - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::{Receiver, channel}; - /// - /// let (_, receiver): (_, Receiver) = channel(); - /// - /// assert!(receiver.try_recv().is_err()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn try_recv(&self) -> Result { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.try_recv(), - ReceiverFlavor::List(chan) => chan.try_recv(), - ReceiverFlavor::Zero(chan) => chan.try_recv(), - } - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`], - /// this receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, recv) = mpmc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// ``` - /// - /// Buffering behavior: - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// use std::sync::mpmc::RecvError; - /// - /// let (send, recv) = mpmc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// drop(send); - /// }); - /// - /// // wait for the thread to join so we ensure the sender is dropped - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// assert_eq!(Ok(2), recv.recv()); - /// assert_eq!(Ok(3), recv.recv()); - /// assert_eq!(Err(RecvError), recv.recv()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn recv(&self) -> Result { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.recv(None), - ReceiverFlavor::List(chan) => chan.recv(None), - ReceiverFlavor::Zero(chan) => chan.recv(None), - } - .map_err(|_| RecvError) - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if it waits more than `timeout`. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`], - /// this receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before encountering timeout: - /// - /// ```no_run - /// #![feature(mpmc_channel)] - /// - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpmc; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching timeout: - /// - /// ```no_run - /// #![feature(mpmc_channel)] - /// - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpmc; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Err(mpmc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn recv_timeout(&self, timeout: Duration) -> Result { - match Instant::now().checked_add(timeout) { - Some(deadline) => self.recv_deadline(deadline), - // So far in the future that it's practically the same as waiting indefinitely. - None => self.recv().map_err(RecvTimeoutError::from), - } - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if `deadline` is reached. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`], then this receiver will wake up - /// and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before reaching deadline: - /// - /// ```no_run - /// #![feature(mpmc_channel)] - /// - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpmc; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching deadline: - /// - /// ```no_run - /// #![feature(mpmc_channel)] - /// - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpmc; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Err(mpmc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn recv_deadline(&self, deadline: Instant) -> Result { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)), - ReceiverFlavor::List(chan) => chan.recv(Some(deadline)), - ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)), - } - } - - /// Returns an iterator that will attempt to yield all pending values. - /// It will return `None` if there are no more pending values or if the - /// channel has hung up. The iterator will never [`panic!`] or block the - /// user by waiting for values. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, receiver) = channel(); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_secs(1)); - /// sender.send(1).unwrap(); - /// sender.send(2).unwrap(); - /// sender.send(3).unwrap(); - /// }); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// // block for two seconds - /// thread::sleep(Duration::from_secs(2)); - /// - /// let mut iter = receiver.try_iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn try_iter(&self) -> TryIter<'_, T> { - TryIter { rx: self } - } -} - -impl Receiver { - /// Returns `true` if the channel is empty. - /// - /// Note: Zero-capacity channels are always empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// assert!(recv.is_empty()); - /// - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert!(!recv.is_empty()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn is_empty(&self) -> bool { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.is_empty(), - ReceiverFlavor::List(chan) => chan.is_empty(), - ReceiverFlavor::Zero(chan) => chan.is_empty(), - } - } - - /// Returns `true` if the channel is full. - /// - /// Note: Zero-capacity channels are always full. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, recv) = mpmc::sync_channel(1); - /// - /// assert!(!recv.is_full()); - /// - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert!(recv.is_full()); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn is_full(&self) -> bool { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.is_full(), - ReceiverFlavor::List(chan) => chan.is_full(), - ReceiverFlavor::Zero(chan) => chan.is_full(), - } - } - - /// Returns the number of messages in the channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, recv) = mpmc::channel(); - /// - /// assert_eq!(recv.len(), 0); - /// - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(recv.len(), 1); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn len(&self) -> usize { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.len(), - ReceiverFlavor::List(chan) => chan.len(), - ReceiverFlavor::Zero(chan) => chan.len(), - } - } - - /// If the channel is bounded, returns its capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// use std::thread; - /// - /// let (send, recv) = mpmc::sync_channel(3); - /// - /// assert_eq!(recv.capacity(), Some(3)); - /// - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(recv.capacity(), Some(3)); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn capacity(&self) -> Option { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.capacity(), - ReceiverFlavor::List(chan) => chan.capacity(), - ReceiverFlavor::Zero(chan) => chan.capacity(), - } - } - - /// Returns `true` if receivers belong to the same channel. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc; - /// - /// let (_, rx1) = mpmc::channel::(); - /// let (_, rx2) = mpmc::channel::(); - /// - /// assert!(rx1.same_channel(&rx1)); - /// assert!(!rx1.same_channel(&rx2)); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn same_channel(&self, other: &Receiver) -> bool { - match (&self.flavor, &other.flavor) { - (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b, - (ReceiverFlavor::List(a), ReceiverFlavor::List(b)) => a == b, - (ReceiverFlavor::Zero(a), ReceiverFlavor::Zero(b)) => a == b, - _ => false, - } - } - - /// Returns an iterator that will block waiting for messages, but never - /// [`panic!`]. It will return [`None`] when the channel has hung up. - /// - /// # Examples - /// - /// ```rust - /// #![feature(mpmc_channel)] - /// - /// use std::sync::mpmc::channel; - /// use std::thread; - /// - /// let (send, recv) = channel(); - /// - /// thread::spawn(move || { - /// send.send(1).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// }); - /// - /// let mut iter = recv.iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[unstable(feature = "mpmc_channel", issue = "126840")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { rx: self } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl Drop for Receiver { - fn drop(&mut self) { - unsafe { - match &self.flavor { - ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect_receivers()), - ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()), - ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()), - } - } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl Clone for Receiver { - fn clone(&self) -> Self { - let flavor = match &self.flavor { - ReceiverFlavor::Array(chan) => ReceiverFlavor::Array(chan.acquire()), - ReceiverFlavor::List(chan) => ReceiverFlavor::List(chan.acquire()), - ReceiverFlavor::Zero(chan) => ReceiverFlavor::Zero(chan.acquire()), - }; - - Receiver { flavor } - } -} - -#[unstable(feature = "mpmc_channel", issue = "126840")] -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish_non_exhaustive() - } -} - -#[cfg(test)] -mod tests; diff --git a/crates/std/src/sync/mpmc/select.rs b/crates/std/src/sync/mpmc/select.rs deleted file mode 100644 index 56a83fe..0000000 --- a/crates/std/src/sync/mpmc/select.rs +++ /dev/null @@ -1,71 +0,0 @@ -/// Temporary data that gets initialized during a blocking operation, and is consumed by -/// `read` or `write`. -/// -/// Each field contains data associated with a specific channel flavor. -#[derive(Debug, Default)] -pub struct Token { - pub(crate) array: super::array::ArrayToken, - pub(crate) list: super::list::ListToken, - #[allow(dead_code)] - pub(crate) zero: super::zero::ZeroToken, -} - -/// Identifier associated with an operation by a specific thread on a specific channel. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Operation(usize); - -impl Operation { - /// Creates an operation identifier from a mutable reference. - /// - /// This function essentially just turns the address of the reference into a number. The - /// reference should point to a variable that is specific to the thread and the operation, - /// and is alive for the entire duration of a blocking operation. - #[inline] - pub fn hook(r: &mut T) -> Operation { - let val = r as *mut T as usize; - // Make sure that the pointer address doesn't equal the numerical representation of - // `Selected::{Waiting, Aborted, Disconnected}`. - assert!(val > 2); - Operation(val) - } -} - -/// Current state of a blocking operation. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Selected { - /// Still waiting for an operation. - Waiting, - - /// The attempt to block the current thread has been aborted. - Aborted, - - /// An operation became ready because a channel is disconnected. - Disconnected, - - /// An operation became ready because a message can be sent or received. - Operation(Operation), -} - -impl From for Selected { - #[inline] - fn from(val: usize) -> Selected { - match val { - 0 => Selected::Waiting, - 1 => Selected::Aborted, - 2 => Selected::Disconnected, - oper => Selected::Operation(Operation(oper)), - } - } -} - -impl Into for Selected { - #[inline] - fn into(self) -> usize { - match self { - Selected::Waiting => 0, - Selected::Aborted => 1, - Selected::Disconnected => 2, - Selected::Operation(Operation(val)) => val, - } - } -} diff --git a/crates/std/src/sync/mpmc/tests.rs b/crates/std/src/sync/mpmc/tests.rs deleted file mode 100644 index 6deb4dc..0000000 --- a/crates/std/src/sync/mpmc/tests.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Ensure that thread_local init with `const { 0 }` still has unique address at run-time -#[test] -fn waker_current_thread_id() { - let first = super::waker::current_thread_id(); - let t = crate::thread::spawn(move || { - let second = super::waker::current_thread_id(); - assert_ne!(first, second); - assert_eq!(second, super::waker::current_thread_id()); - }); - - assert_eq!(first, super::waker::current_thread_id()); - t.join().unwrap(); - assert_eq!(first, super::waker::current_thread_id()); -} diff --git a/crates/std/src/sync/mpmc/utils.rs b/crates/std/src/sync/mpmc/utils.rs deleted file mode 100644 index e3bcb14..0000000 --- a/crates/std/src/sync/mpmc/utils.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::cell::Cell; -use crate::ops::{Deref, DerefMut}; - -/// Pads and aligns a value to the length of a cache line. -#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)] -// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache -// lines at a time, so we have to align to 128 bytes rather than 64. -// -// Sources: -// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf -// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107 -// -// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size. -// -// Sources: -// - https://www.mono-project.com/news/2016/09/12/arm64-icache/ -// -// powerpc64 has 128-byte cache line size. -// -// Sources: -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9 -#[cfg_attr( - any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",), - repr(align(128)) -)] -// arm, mips and mips64 have 32-byte cache line size. -// -// Sources: -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9 -#[cfg_attr( - any( - target_arch = "arm", - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "mips64", - target_arch = "mips64r6", - ), - repr(align(32)) -)] -// s390x has 256-byte cache line size. -// -// Sources: -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7 -#[cfg_attr(target_arch = "s390x", repr(align(256)))] -// x86, wasm and riscv have 64-byte cache line size. -// -// Sources: -// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7 -// - https://github.com/golang/go/blob/5e31f78c8a4ed1b872ddc194f0cd1ae931b37d7e/src/internal/cpu/cpu_riscv64.go#L7 -// -// All others are assumed to have 64-byte cache line size. -#[cfg_attr( - not(any( - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "powerpc64", - target_arch = "arm", - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "mips64", - target_arch = "mips64r6", - target_arch = "s390x", - )), - repr(align(64)) -)] -pub struct CachePadded { - value: T, -} - -impl CachePadded { - /// Pads and aligns a value to the length of a cache line. - pub fn new(value: T) -> CachePadded { - CachePadded:: { value } - } -} - -impl Deref for CachePadded { - type Target = T; - - fn deref(&self) -> &T { - &self.value - } -} - -impl DerefMut for CachePadded { - fn deref_mut(&mut self) -> &mut T { - &mut self.value - } -} - -const SPIN_LIMIT: u32 = 6; - -/// Performs quadratic backoff in spin loops. -pub struct Backoff { - step: Cell, -} - -impl Backoff { - /// Creates a new `Backoff`. - pub fn new() -> Self { - Backoff { step: Cell::new(0) } - } - - /// Backs off using lightweight spinning. - /// - /// This method should be used for retrying an operation because another thread made - /// progress. i.e. on CAS failure. - #[inline] - pub fn spin_light(&self) { - let step = self.step.get().min(SPIN_LIMIT); - for _ in 0..step.pow(2) { - crate::hint::spin_loop(); - } - - self.step.set(self.step.get() + 1); - } - - /// Backs off using heavyweight spinning. - /// - /// This method should be used in blocking loops where parking the thread is not an option. - #[inline] - pub fn spin_heavy(&self) { - if self.step.get() <= SPIN_LIMIT { - for _ in 0..self.step.get().pow(2) { - crate::hint::spin_loop() - } - } else { - crate::thread::yield_now(); - } - - self.step.set(self.step.get() + 1); - } -} diff --git a/crates/std/src/sync/mpmc/waker.rs b/crates/std/src/sync/mpmc/waker.rs deleted file mode 100644 index 4216fb7..0000000 --- a/crates/std/src/sync/mpmc/waker.rs +++ /dev/null @@ -1,209 +0,0 @@ -//! Waking mechanism for threads blocked on channel operations. - -use super::context::Context; -use super::select::{Operation, Selected}; -use crate::ptr; -use crate::sync::Mutex; -use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; - -/// Represents a thread blocked on a specific channel operation. -pub(crate) struct Entry { - /// The operation. - pub(crate) oper: Operation, - - /// Optional packet. - pub(crate) packet: *mut (), - - /// Context associated with the thread owning this operation. - pub(crate) cx: Context, -} - -/// A queue of threads blocked on channel operations. -/// -/// This data structure is used by threads to register blocking operations and get woken up once -/// an operation becomes ready. -pub(crate) struct Waker { - /// A list of select operations. - selectors: Vec, - - /// A list of operations waiting to be ready. - observers: Vec, -} - -impl Waker { - /// Creates a new `Waker`. - #[inline] - pub(crate) fn new() -> Self { - Waker { selectors: Vec::new(), observers: Vec::new() } - } - - /// Registers a select operation. - #[inline] - pub(crate) fn register(&mut self, oper: Operation, cx: &Context) { - self.register_with_packet(oper, ptr::null_mut(), cx); - } - - /// Registers a select operation and a packet. - #[inline] - pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) { - self.selectors.push(Entry { oper, packet, cx: cx.clone() }); - } - - /// Unregisters a select operation. - #[inline] - pub(crate) fn unregister(&mut self, oper: Operation) -> Option { - if let Some((i, _)) = - self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper) - { - let entry = self.selectors.remove(i); - Some(entry) - } else { - None - } - } - - /// Attempts to find another thread's entry, select the operation, and wake it up. - #[inline] - pub(crate) fn try_select(&mut self) -> Option { - if self.selectors.is_empty() { - None - } else { - let thread_id = current_thread_id(); - - self.selectors - .iter() - .position(|selector| { - // Does the entry belong to a different thread? - selector.cx.thread_id() != thread_id - && selector // Try selecting this operation. - .cx - .try_select(Selected::Operation(selector.oper)) - .is_ok() - && { - // Provide the packet. - selector.cx.store_packet(selector.packet); - // Wake the thread up. - selector.cx.unpark(); - true - } - }) - // Remove the entry from the queue to keep it clean and improve - // performance. - .map(|pos| self.selectors.remove(pos)) - } - } - - /// Notifies all operations waiting to be ready. - #[inline] - pub(crate) fn notify(&mut self) { - for entry in self.observers.drain(..) { - if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() { - entry.cx.unpark(); - } - } - } - - /// Notifies all registered operations that the channel is disconnected. - #[inline] - pub(crate) fn disconnect(&mut self) { - for entry in self.selectors.iter() { - if entry.cx.try_select(Selected::Disconnected).is_ok() { - // Wake the thread up. - // - // Here we don't remove the entry from the queue. Registered threads must - // unregister from the waker by themselves. They might also want to recover the - // packet value and destroy it, if necessary. - entry.cx.unpark(); - } - } - - self.notify(); - } -} - -impl Drop for Waker { - #[inline] - fn drop(&mut self) { - debug_assert_eq!(self.selectors.len(), 0); - debug_assert_eq!(self.observers.len(), 0); - } -} - -/// A waker that can be shared among threads without locking. -/// -/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization. -pub(crate) struct SyncWaker { - /// The inner `Waker`. - inner: Mutex, - - /// `true` if the waker is empty. - is_empty: Atomic, -} - -impl SyncWaker { - /// Creates a new `SyncWaker`. - #[inline] - pub(crate) fn new() -> Self { - SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) } - } - - /// Registers the current thread with an operation. - #[inline] - pub(crate) fn register(&self, oper: Operation, cx: &Context) { - let mut inner = self.inner.lock().unwrap(); - inner.register(oper, cx); - self.is_empty - .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); - } - - /// Unregisters an operation previously registered by the current thread. - #[inline] - pub(crate) fn unregister(&self, oper: Operation) -> Option { - let mut inner = self.inner.lock().unwrap(); - let entry = inner.unregister(oper); - self.is_empty - .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); - entry - } - - /// Attempts to find one thread (not the current one), select its operation, and wake it up. - #[inline] - pub(crate) fn notify(&self) { - if !self.is_empty.load(Ordering::SeqCst) { - let mut inner = self.inner.lock().unwrap(); - if !self.is_empty.load(Ordering::SeqCst) { - inner.try_select(); - inner.notify(); - self.is_empty.store( - inner.selectors.is_empty() && inner.observers.is_empty(), - Ordering::SeqCst, - ); - } - } - } - - /// Notifies all threads that the channel is disconnected. - #[inline] - pub(crate) fn disconnect(&self) { - let mut inner = self.inner.lock().unwrap(); - inner.disconnect(); - self.is_empty - .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst); - } -} - -impl Drop for SyncWaker { - #[inline] - fn drop(&mut self) { - debug_assert!(self.is_empty.load(Ordering::SeqCst)); - } -} - -/// Returns a unique id for the current thread. -#[inline] -pub fn current_thread_id() -> usize { - // `u8` is not drop so this variable will be available during thread destruction, - // whereas `thread::current()` would not be - thread_local! { static DUMMY: u8 = const { 0 } } - DUMMY.with(|x| (x as *const u8).addr()) -} diff --git a/crates/std/src/sync/mpmc/zero.rs b/crates/std/src/sync/mpmc/zero.rs deleted file mode 100644 index f1ecf80..0000000 --- a/crates/std/src/sync/mpmc/zero.rs +++ /dev/null @@ -1,319 +0,0 @@ -//! Zero-capacity channel. -//! -//! This kind of channel is also known as *rendezvous* channel. - -use super::context::Context; -use super::error::*; -use super::select::{Operation, Selected, Token}; -use super::utils::Backoff; -use super::waker::Waker; -use crate::cell::UnsafeCell; -use crate::marker::PhantomData; -use crate::sync::Mutex; -use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; -use crate::time::Instant; -use crate::{fmt, ptr}; - -/// A pointer to a packet. -pub(crate) struct ZeroToken(*mut ()); - -impl Default for ZeroToken { - fn default() -> Self { - Self(ptr::null_mut()) - } -} - -impl fmt::Debug for ZeroToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&(self.0 as usize), f) - } -} - -/// A slot for passing one message from a sender to a receiver. -struct Packet { - /// Equals `true` if the packet is allocated on the stack. - on_stack: bool, - - /// Equals `true` once the packet is ready for reading or writing. - ready: Atomic, - - /// The message. - msg: UnsafeCell>, -} - -impl Packet { - /// Creates an empty packet on the stack. - fn empty_on_stack() -> Packet { - Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) } - } - - /// Creates a packet on the stack, containing a message. - fn message_on_stack(msg: T) -> Packet { - Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) } - } - - /// Waits until the packet becomes ready for reading or writing. - fn wait_ready(&self) { - let backoff = Backoff::new(); - while !self.ready.load(Ordering::Acquire) { - backoff.spin_heavy(); - } - } -} - -/// Inner representation of a zero-capacity channel. -struct Inner { - /// Senders waiting to pair up with a receive operation. - senders: Waker, - - /// Receivers waiting to pair up with a send operation. - receivers: Waker, - - /// Equals `true` when the channel is disconnected. - is_disconnected: bool, -} - -/// Zero-capacity channel. -pub(crate) struct Channel { - /// Inner representation of the channel. - inner: Mutex, - - /// Indicates that dropping a `Channel` may drop values of type `T`. - _marker: PhantomData, -} - -impl Channel { - /// Constructs a new zero-capacity channel. - pub(crate) fn new() -> Self { - Channel { - inner: Mutex::new(Inner { - senders: Waker::new(), - receivers: Waker::new(), - is_disconnected: false, - }), - _marker: PhantomData, - } - } - - /// Writes a message into the packet. - pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> { - // If there is no packet, the channel is disconnected. - if token.zero.0.is_null() { - return Err(msg); - } - - unsafe { - let packet = &*(token.zero.0 as *const Packet); - packet.msg.get().write(Some(msg)); - packet.ready.store(true, Ordering::Release); - } - Ok(()) - } - - /// Reads a message from the packet. - pub(crate) unsafe fn read(&self, token: &mut Token) -> Result { - // If there is no packet, the channel is disconnected. - if token.zero.0.is_null() { - return Err(()); - } - - let packet = unsafe { &*(token.zero.0 as *const Packet) }; - - if packet.on_stack { - // The message has been in the packet from the beginning, so there is no need to wait - // for it. However, after reading the message, we need to set `ready` to `true` in - // order to signal that the packet can be destroyed. - let msg = unsafe { packet.msg.get().replace(None) }.unwrap(); - packet.ready.store(true, Ordering::Release); - Ok(msg) - } else { - // Wait until the message becomes available, then read it and destroy the - // heap-allocated packet. - packet.wait_ready(); - unsafe { - let msg = packet.msg.get().replace(None).unwrap(); - drop(Box::from_raw(token.zero.0 as *mut Packet)); - Ok(msg) - } - } - } - - /// Attempts to send a message into the channel. - pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError> { - let token = &mut Token::default(); - let mut inner = self.inner.lock().unwrap(); - - // If there's a waiting receiver, pair up with it. - if let Some(operation) = inner.receivers.try_select() { - token.zero.0 = operation.packet; - drop(inner); - unsafe { - self.write(token, msg).ok().unwrap(); - } - Ok(()) - } else if inner.is_disconnected { - Err(TrySendError::Disconnected(msg)) - } else { - Err(TrySendError::Full(msg)) - } - } - - /// Sends a message into the channel. - pub(crate) fn send( - &self, - msg: T, - deadline: Option, - ) -> Result<(), SendTimeoutError> { - let token = &mut Token::default(); - let mut inner = self.inner.lock().unwrap(); - - // If there's a waiting receiver, pair up with it. - if let Some(operation) = inner.receivers.try_select() { - token.zero.0 = operation.packet; - drop(inner); - unsafe { - self.write(token, msg).ok().unwrap(); - } - return Ok(()); - } - - if inner.is_disconnected { - return Err(SendTimeoutError::Disconnected(msg)); - } - - Context::with(|cx| { - // Prepare for blocking until a receiver wakes us up. - let oper = Operation::hook(token); - let mut packet = Packet::::message_on_stack(msg); - inner.senders.register_with_packet(oper, (&raw mut packet) as *mut (), cx); - inner.receivers.notify(); - drop(inner); - - // Block the current thread. - // SAFETY: the context belongs to the current thread. - let sel = unsafe { cx.wait_until(deadline) }; - - match sel { - Selected::Waiting => unreachable!(), - Selected::Aborted => { - self.inner.lock().unwrap().senders.unregister(oper).unwrap(); - let msg = unsafe { packet.msg.get().replace(None).unwrap() }; - Err(SendTimeoutError::Timeout(msg)) - } - Selected::Disconnected => { - self.inner.lock().unwrap().senders.unregister(oper).unwrap(); - let msg = unsafe { packet.msg.get().replace(None).unwrap() }; - Err(SendTimeoutError::Disconnected(msg)) - } - Selected::Operation(_) => { - // Wait until the message is read, then drop the packet. - packet.wait_ready(); - Ok(()) - } - } - }) - } - - /// Attempts to receive a message without blocking. - pub(crate) fn try_recv(&self) -> Result { - let token = &mut Token::default(); - let mut inner = self.inner.lock().unwrap(); - - // If there's a waiting sender, pair up with it. - if let Some(operation) = inner.senders.try_select() { - token.zero.0 = operation.packet; - drop(inner); - unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) } - } else if inner.is_disconnected { - Err(TryRecvError::Disconnected) - } else { - Err(TryRecvError::Empty) - } - } - - /// Receives a message from the channel. - pub(crate) fn recv(&self, deadline: Option) -> Result { - let token = &mut Token::default(); - let mut inner = self.inner.lock().unwrap(); - - // If there's a waiting sender, pair up with it. - if let Some(operation) = inner.senders.try_select() { - token.zero.0 = operation.packet; - drop(inner); - unsafe { - return self.read(token).map_err(|_| RecvTimeoutError::Disconnected); - } - } - - if inner.is_disconnected { - return Err(RecvTimeoutError::Disconnected); - } - - Context::with(|cx| { - // Prepare for blocking until a sender wakes us up. - let oper = Operation::hook(token); - let mut packet = Packet::::empty_on_stack(); - inner.receivers.register_with_packet(oper, (&raw mut packet) as *mut (), cx); - inner.senders.notify(); - drop(inner); - - // Block the current thread. - // SAFETY: the context belongs to the current thread. - let sel = unsafe { cx.wait_until(deadline) }; - - match sel { - Selected::Waiting => unreachable!(), - Selected::Aborted => { - self.inner.lock().unwrap().receivers.unregister(oper).unwrap(); - Err(RecvTimeoutError::Timeout) - } - Selected::Disconnected => { - self.inner.lock().unwrap().receivers.unregister(oper).unwrap(); - Err(RecvTimeoutError::Disconnected) - } - Selected::Operation(_) => { - // Wait until the message is provided, then read it. - packet.wait_ready(); - unsafe { Ok(packet.msg.get().replace(None).unwrap()) } - } - } - }) - } - - /// Disconnects the channel and wakes up all blocked senders and receivers. - /// - /// Returns `true` if this call disconnected the channel. - pub(crate) fn disconnect(&self) -> bool { - let mut inner = self.inner.lock().unwrap(); - - if !inner.is_disconnected { - inner.is_disconnected = true; - inner.senders.disconnect(); - inner.receivers.disconnect(); - true - } else { - false - } - } - - /// Returns the current number of messages inside the channel. - pub(crate) fn len(&self) -> usize { - 0 - } - - /// Returns the capacity of the channel. - #[allow(clippy::unnecessary_wraps)] // This is intentional. - pub(crate) fn capacity(&self) -> Option { - Some(0) - } - - /// Returns `true` if the channel is empty. - pub(crate) fn is_empty(&self) -> bool { - true - } - - /// Returns `true` if the channel is full. - pub(crate) fn is_full(&self) -> bool { - true - } -} diff --git a/crates/std/src/sync/mpsc.rs b/crates/std/src/sync/mpsc.rs deleted file mode 100644 index 0ae23f6..0000000 --- a/crates/std/src/sync/mpsc.rs +++ /dev/null @@ -1,1214 +0,0 @@ -//! Multi-producer, single-consumer FIFO queue communication primitives. -//! -//! This module provides message-based communication over channels, concretely -//! defined among three types: -//! -//! * [`Sender`] -//! * [`SyncSender`] -//! * [`Receiver`] -//! -//! A [`Sender`] or [`SyncSender`] is used to send data to a [`Receiver`]. Both -//! senders are clone-able (multi-producer) such that many threads can send -//! simultaneously to one receiver (single-consumer). -//! -//! These channels come in two flavors: -//! -//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function -//! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. -//! -//! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending -//! messages is a pre-allocated buffer of a fixed size. All sends will be -//! **synchronous** by blocking until there is buffer space available. Note -//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" -//! channel where each sender atomically hands off a message to a receiver. -//! -//! [`send`]: Sender::send -//! -//! ## Disconnection -//! -//! The send and receive operations on channels will all return a [`Result`] -//! indicating whether the operation succeeded or not. An unsuccessful operation -//! is normally indicative of the other half of a channel having "hung up" by -//! being dropped in its corresponding thread. -//! -//! Once half of a channel has been deallocated, most operations can no longer -//! continue to make progress, so [`Err`] will be returned. Many applications -//! will continue to [`unwrap`] the results returned from this module, -//! instigating a propagation of failure among threads if one unexpectedly dies. -//! -//! [`unwrap`]: Result::unwrap -//! -//! # Examples -//! -//! Simple usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a simple streaming channel -//! let (tx, rx) = channel(); -//! thread::spawn(move || { -//! tx.send(10).unwrap(); -//! }); -//! assert_eq!(rx.recv().unwrap(), 10); -//! ``` -//! -//! Shared usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! thread::spawn(move || { -//! tx.send(i).unwrap(); -//! }); -//! } -//! -//! for _ in 0..10 { -//! let j = rx.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! } -//! ``` -//! -//! Propagating panics: -//! -//! ``` -//! use std::sync::mpsc::channel; -//! -//! // The call to recv() will return an error because the channel has already -//! // hung up (or been deallocated) -//! let (tx, rx) = channel::(); -//! drop(tx); -//! assert!(rx.recv().is_err()); -//! ``` -//! -//! Synchronous channels: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::sync_channel; -//! -//! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move || { -//! // This will wait for the parent thread to start receiving -//! tx.send(53).unwrap(); -//! }); -//! rx.recv().unwrap(); -//! ``` -//! -//! Unbounded receive loop: -//! -//! ``` -//! use std::sync::mpsc::sync_channel; -//! use std::thread; -//! -//! let (tx, rx) = sync_channel(3); -//! -//! for _ in 0..3 { -//! // It would be the same without thread and clone here -//! // since there will still be one `tx` left. -//! let tx = tx.clone(); -//! // cloned tx dropped within thread -//! thread::spawn(move || tx.send("ok").unwrap()); -//! } -//! -//! // Drop the last sender to stop `rx` waiting for message. -//! // The program will not complete if we comment this out. -//! // **All** `tx` needs to be dropped for `rx` to have `Err`. -//! drop(tx); -//! -//! // Unbounded receiver waiting for all senders to complete. -//! while let Ok(msg) = rx.recv() { -//! println!("{msg}"); -//! } -//! -//! println!("completed"); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -// MPSC channels are built as a wrapper around MPMC channels, which -// were ported from the `crossbeam-channel` crate. MPMC channels are -// not exposed publicly, but if you are curious about the implementation, -// that's where everything is. - -use crate::sync::mpmc; -use crate::time::{Duration, Instant}; -use crate::{error, fmt}; - -/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// This half can only be owned by one thread. -/// -/// Messages sent to the channel can be retrieved using [`recv`]. -/// -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send("Hello world!").unwrap(); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// send.send("Delayed for 2 seconds").unwrap(); -/// }); -/// -/// println!("{}", recv.recv().unwrap()); // Received immediately -/// println!("Waiting..."); -/// println!("{}", recv.recv().unwrap()); // Received after 2 seconds -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")] -pub struct Receiver { - inner: mpmc::Receiver, -} - -// The receiver port can be sent from place to place, so long as it -// is not used to receive non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Receiver {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for Receiver {} - -/// An iterator over messages on a [`Receiver`], created by [`iter`]. -/// -/// This iterator will block whenever [`next`] is called, -/// waiting for a new message, and [`None`] will be returned -/// when the corresponding channel has hung up. -/// -/// [`iter`]: Receiver::iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Iter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An iterator that attempts to yield all pending values for a [`Receiver`], -/// created by [`try_iter`]. -/// -/// [`None`] will be returned when there are no pending values remaining or -/// if the corresponding channel has hung up. -/// -/// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. -/// -/// [`try_iter`]: Receiver::try_iter -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = channel(); -/// -/// // Nothing is in the buffer yet -/// assert!(receiver.try_iter().next().is_none()); -/// println!("Nothing in the buffer..."); -/// -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// sender.send(2).unwrap(); -/// sender.send(3).unwrap(); -/// }); -/// -/// println!("Going to sleep..."); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// -/// for x in receiver.try_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -#[derive(Debug)] -pub struct TryIter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An owning iterator over messages on a [`Receiver`], -/// created by [`into_iter`]. -/// -/// This iterator will block whenever [`next`] -/// is called, waiting for a new message, and [`None`] will be -/// returned if the corresponding channel has hung up. -/// -/// [`into_iter`]: Receiver::into_iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.into_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -#[derive(Debug)] -pub struct IntoIter { - rx: Receiver, -} - -/// The sending-half of Rust's asynchronous [`channel`] type. -/// -/// Messages can be sent through this channel with [`send`]. -/// -/// Note: all senders (the original and its clones) need to be dropped for the receiver -/// to stop blocking to receive messages with [`Receiver::recv`]. -/// -/// [`send`]: Sender::send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// let sender2 = sender.clone(); -/// -/// // First thread owns sender -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// }); -/// -/// // Second thread owns sender2 -/// thread::spawn(move || { -/// sender2.send(2).unwrap(); -/// }); -/// -/// let msg = receiver.recv().unwrap(); -/// let msg2 = receiver.recv().unwrap(); -/// -/// assert_eq!(3, msg + msg2); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Sender { - inner: mpmc::Sender, -} - -// The send port can be sent from place to place, so long as it -// is not used to send non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Sender {} - -#[stable(feature = "mpsc_sender_sync", since = "1.72.0")] -unsafe impl Sync for Sender {} - -/// The sending-half of Rust's synchronous [`sync_channel`] type. -/// -/// Messages can be sent through this channel with [`send`] or [`try_send`]. -/// -/// [`send`] will block if there is no space in the internal buffer. -/// -/// [`send`]: SyncSender::send -/// [`try_send`]: SyncSender::try_send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// // Create a sync_channel with buffer size 2 -/// let (sync_sender, receiver) = sync_channel(2); -/// let sync_sender2 = sync_sender.clone(); -/// -/// // First thread owns sync_sender -/// thread::spawn(move || { -/// sync_sender.send(1).unwrap(); -/// sync_sender.send(2).unwrap(); -/// }); -/// -/// // Second thread owns sync_sender2 -/// thread::spawn(move || { -/// sync_sender2.send(3).unwrap(); -/// // thread will now block since the buffer is full -/// println!("Thread unblocked!"); -/// }); -/// -/// let mut msg; -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {msg} received"); -/// -/// // "Thread unblocked!" will be printed now -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {msg} received"); -/// -/// msg = receiver.recv().unwrap(); -/// -/// println!("message {msg} received"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SyncSender { - inner: mpmc::Sender, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for SyncSender {} - -/// An error returned from the [`Sender::send`] or [`SyncSender::send`] -/// function on **channel**s. -/// -/// A **send** operation can only fail if the receiving end of a channel is -/// disconnected, implying that the data could never be received. The error -/// contains the data being sent as a payload so it can be recovered. -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); - -/// An error returned from the [`recv`] function on a [`Receiver`]. -/// -/// The [`recv`] operation can only fail if the sending half of a -/// [`channel`] (or [`sync_channel`]) is disconnected, implying that no further -/// messages will ever be received. -/// -/// [`recv`]: Receiver::recv -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RecvError; - -/// This enumeration is the list of the possible reasons that [`try_recv`] could -/// not return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`try_recv`]: Receiver::try_recv -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum TryRecvError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "rust1", since = "1.0.0")] - Empty, - - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected, -} - -/// This enumeration is the list of possible errors that made [`recv_timeout`] -/// unable to return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`recv_timeout`]: Receiver::recv_timeout -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] -pub enum RecvTimeoutError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Timeout, - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Disconnected, -} - -/// This enumeration is the list of the possible error outcomes for the -/// [`try_send`] method. -/// -/// [`try_send`]: SyncSender::try_send -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum TrySendError { - /// The data could not be sent on the [`sync_channel`] because it would require that - /// the callee block to send the data. - /// - /// If this is a buffered channel, then the buffer is full at this time. If - /// this is not a buffered channel, then there is no [`Receiver`] available to - /// acquire the data. - #[stable(feature = "rust1", since = "1.0.0")] - Full(#[stable(feature = "rust1", since = "1.0.0")] T), - - /// This [`sync_channel`]'s receiving half has disconnected, so the data could not be - /// sent. The data is returned back to the callee in this case. - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), -} - -/// Creates a new asynchronous channel, returning the sender/receiver halves. -/// -/// All data sent on the [`Sender`] will become available on the [`Receiver`] in -/// the same order as it was sent, and no [`send`] will block the calling thread -/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will -/// block after its buffer limit is reached). [`recv`] will block until a message -/// is available while there is at least one [`Sender`] alive (including clones). -/// -/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times, but -/// only one [`Receiver`] is supported. -/// -/// If the [`Receiver`] is disconnected while trying to [`send`] with the -/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the -/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will -/// return a [`RecvError`]. -/// -/// [`send`]: Sender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// -/// // Spawn off an expensive computation -/// thread::spawn(move || { -/// # fn expensive_computation() {} -/// sender.send(expensive_computation()).unwrap(); -/// }); -/// -/// // Do some useful work for a while -/// -/// // Let's see what that answer was -/// println!("{:?}", receiver.recv().unwrap()); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn channel() -> (Sender, Receiver) { - let (tx, rx) = mpmc::channel(); - (Sender { inner: tx }, Receiver { inner: rx }) -} - -/// Creates a new synchronous, bounded channel. -/// -/// All data sent on the [`SyncSender`] will become available on the [`Receiver`] -/// in the same order as it was sent. Like asynchronous [`channel`]s, the -/// [`Receiver`] will block until a message becomes available. `sync_channel` -/// differs greatly in the semantics of the sender, however. -/// -/// This channel has an internal buffer on which messages will be queued. -/// `bound` specifies the buffer size. When the internal buffer becomes full, -/// future sends will *block* waiting for the buffer to open up. Note that a -/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" -/// where each [`send`] will not return until a [`recv`] is paired with it. -/// -/// The [`SyncSender`] can be cloned to [`send`] to the same channel multiple -/// times, but only one [`Receiver`] is supported. -/// -/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying -/// to [`send`] with the [`SyncSender`], the [`send`] method will return a -/// [`SendError`]. Similarly, If the [`SyncSender`] is disconnected while trying -/// to [`recv`], the [`recv`] method will return a [`RecvError`]. -/// -/// [`send`]: SyncSender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// let (sender, receiver) = sync_channel(1); -/// -/// // this returns immediately -/// sender.send(1).unwrap(); -/// -/// thread::spawn(move || { -/// // this will block until the previous message has been received -/// sender.send(2).unwrap(); -/// }); -/// -/// assert_eq!(receiver.recv().unwrap(), 1); -/// assert_eq!(receiver.recv().unwrap(), 2); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { - let (tx, rx) = mpmc::sync_channel(bound); - (SyncSender { inner: tx }, Receiver { inner: rx }) -} - -//////////////////////////////////////////////////////////////////////////////// -// Sender -//////////////////////////////////////////////////////////////////////////////// - -impl Sender { - /// Attempts to send a value on this channel, returning it back if it could - /// not be sent. - /// - /// A successful send occurs when it is determined that the other end of - /// the channel has not hung up already. An unsuccessful send would be one - /// where the corresponding receiver has already been deallocated. Note - /// that a return value of [`Err`] means that the data will never be - /// received, but a return value of [`Ok`] does *not* mean that the data - /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. - /// - /// This method will never block the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc::channel; - /// - /// let (tx, rx) = channel(); - /// - /// // This send is always successful - /// tx.send(1).unwrap(); - /// - /// // This send will fail because the receiver is gone - /// drop(rx); - /// assert_eq!(tx.send(1).unwrap_err().0, 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Sender { - /// Clone a sender to send to other threads. - /// - /// Note, be aware of the lifetime of the sender because all senders - /// (including the original) need to be dropped in order for - /// [`Receiver::recv`] to stop blocking. - fn clone(&self) -> Sender { - Sender { inner: self.inner.clone() } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// SyncSender -//////////////////////////////////////////////////////////////////////////////// - -impl SyncSender { - /// Sends a value on this synchronous channel. - /// - /// This function will *block* until space in the internal buffer becomes - /// available or a receiver is available to hand off the message to. - /// - /// Note that a successful send does *not* guarantee that the receiver will - /// ever see the data if there is a buffer on this channel. Items may be - /// enqueued in the internal buffer for the receiver to receive at a later - /// time. If the buffer size is 0, however, the channel becomes a rendezvous - /// channel and it guarantees that the receiver has indeed received - /// the data if this function returns success. - /// - /// This function will never panic, but it may return [`Err`] if the - /// [`Receiver`] has disconnected and is no longer able to receive - /// information. - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a rendezvous sync_channel with buffer size 0 - /// let (sync_sender, receiver) = sync_channel(0); - /// - /// thread::spawn(move || { - /// println!("sending message..."); - /// sync_sender.send(1).unwrap(); - /// // Thread is now blocked until the message is received - /// - /// println!("...message received!"); - /// }); - /// - /// let msg = receiver.recv().unwrap(); - /// assert_eq!(1, msg); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t) - } - - /// Attempts to send a value on this channel without blocking. - /// - /// This method differs from [`send`] by returning immediately if the - /// channel's buffer is full or no receiver is waiting to acquire some - /// data. Compared with [`send`], this function has two failure cases - /// instead of one (one for disconnection, one for a full buffer). - /// - /// See [`send`] for notes about guarantees of whether the - /// receiver has received the data or not if this function is successful. - /// - /// [`send`]: Self::send - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a sync_channel with buffer size 1 - /// let (sync_sender, receiver) = sync_channel(1); - /// let sync_sender2 = sync_sender.clone(); - /// - /// // First thread owns sync_sender - /// let handle1 = thread::spawn(move || { - /// sync_sender.send(1).unwrap(); - /// sync_sender.send(2).unwrap(); - /// // Thread blocked - /// }); - /// - /// // Second thread owns sync_sender2 - /// let handle2 = thread::spawn(move || { - /// // This will return an error and send - /// // no message if the buffer is full - /// let _ = sync_sender2.try_send(3); - /// }); - /// - /// let mut msg; - /// msg = receiver.recv().unwrap(); - /// println!("message {msg} received"); - /// - /// msg = receiver.recv().unwrap(); - /// println!("message {msg} received"); - /// - /// // Third message may have never been sent - /// match receiver.try_recv() { - /// Ok(msg) => println!("message {msg} received"), - /// Err(_) => println!("the third message was never sent"), - /// } - /// - /// // Wait for threads to complete - /// handle1.join().unwrap(); - /// handle2.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_send(&self, t: T) -> Result<(), TrySendError> { - self.inner.try_send(t) - } - - // Attempts to send for a value on this receiver, returning an error if the - // corresponding channel has hung up, or if it waits more than `timeout`. - // - // This method is currently only used for tests. - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { - self.inner.send_timeout(t, timeout) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SyncSender { - fn clone(&self) -> SyncSender { - SyncSender { inner: self.inner.clone() } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for SyncSender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SyncSender").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Receiver -//////////////////////////////////////////////////////////////////////////////// - -impl Receiver { - /// Attempts to return a pending value on this receiver without blocking. - /// - /// This method will never block the caller in order to wait for data to - /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. - /// - /// This is useful for a flavor of "optimistic check" before deciding to - /// block on a receiver. - /// - /// Compared with [`recv`], this function has two failure cases instead of one - /// (one for disconnection, one for an empty buffer). - /// - /// [`recv`]: Self::recv - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::{Receiver, channel}; - /// - /// let (_, receiver): (_, Receiver) = channel(); - /// - /// assert!(receiver.try_recv().is_err()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_recv(&self) -> Result { - self.inner.try_recv() - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`] - /// (or [`SyncSender`]), this receiver will wake up and return that - /// message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// ``` - /// - /// Buffering behavior: - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// use std::sync::mpsc::RecvError; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// drop(send); - /// }); - /// - /// // wait for the thread to join so we ensure the sender is dropped - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// assert_eq!(Ok(2), recv.recv()); - /// assert_eq!(Ok(3), recv.recv()); - /// assert_eq!(Err(RecvError), recv.recv()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn recv(&self) -> Result { - self.inner.recv() - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if it waits more than `timeout`. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`] - /// (or [`SyncSender`]), this receiver will wake up and return that - /// message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before encountering timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - pub fn recv_timeout(&self, timeout: Duration) -> Result { - self.inner.recv_timeout(timeout) - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if `deadline` is reached. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this - /// receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[unstable(feature = "deadline_api", issue = "46316")] - pub fn recv_deadline(&self, deadline: Instant) -> Result { - self.inner.recv_deadline(deadline) - } - - /// Returns an iterator that will block waiting for messages, but never - /// [`panic!`]. It will return [`None`] when the channel has hung up. - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::channel; - /// use std::thread; - /// - /// let (send, recv) = channel(); - /// - /// thread::spawn(move || { - /// send.send(1).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// }); - /// - /// let mut iter = recv.iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { rx: self } - } - - /// Returns an iterator that will attempt to yield all pending values. - /// It will return `None` if there are no more pending values or if the - /// channel has hung up. The iterator will never [`panic!`] or block the - /// user by waiting for values. - /// - /// # Examples - /// - /// ```no_run - /// use std::sync::mpsc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, receiver) = channel(); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_secs(1)); - /// sender.send(1).unwrap(); - /// sender.send(2).unwrap(); - /// sender.send(3).unwrap(); - /// }); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// // block for two seconds - /// thread::sleep(Duration::from_secs(2)); - /// - /// let mut iter = receiver.try_iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "receiver_try_iter", since = "1.15.0")] - pub fn try_iter(&self) -> TryIter<'_, T> { - TryIter { rx: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -impl<'a, T> Iterator for TryIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.try_recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl<'a, T> IntoIterator for &'a Receiver { - type Item = T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl Iterator for IntoIter { - type Item = T; - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl IntoIterator for Receiver { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter { rx: self } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SendError").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "sending on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for SendError {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => f.debug_tuple("TrySendError::Full").finish_non_exhaustive(), - TrySendError::Disconnected(..) => { - f.debug_tuple("TrySendError::Disconnected").finish_non_exhaustive() - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => "sending on a full channel".fmt(f), - TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TrySendError {} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From> for TrySendError { - /// Converts a `SendError` into a `TrySendError`. - /// - /// This conversion always returns a `TrySendError::Disconnected` containing the data in the `SendError`. - /// - /// No data is allocated on the heap. - fn from(err: SendError) -> TrySendError { - match err { - SendError(t) => TrySendError::Disconnected(t), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "receiving on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for RecvError {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryRecvError::Empty => "receiving on an empty channel".fmt(f), - TryRecvError::Disconnected => "receiving on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TryRecvError {} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for TryRecvError { - /// Converts a `RecvError` into a `TryRecvError`. - /// - /// This conversion always returns `TryRecvError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> TryRecvError { - match err { - RecvError => TryRecvError::Disconnected, - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl fmt::Display for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f), - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f), - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl error::Error for RecvTimeoutError {} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for RecvTimeoutError { - /// Converts a `RecvError` into a `RecvTimeoutError`. - /// - /// This conversion always returns `RecvTimeoutError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> RecvTimeoutError { - match err { - RecvError => RecvTimeoutError::Disconnected, - } - } -} diff --git a/crates/std/src/sync/nonpoison.rs b/crates/std/src/sync/nonpoison.rs deleted file mode 100644 index ec35872..0000000 --- a/crates/std/src/sync/nonpoison.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Non-poisoning synchronous locks. -//! -//! The difference from the locks in the [`poison`] module is that the locks in this module will not -//! become poisoned when a thread panics while holding a guard. -//! -//! [`poison`]: super::poison - -use crate::fmt; - -/// A type alias for the result of a nonblocking locking method. -#[unstable(feature = "sync_nonpoison", issue = "134645")] -pub type TryLockResult = Result; - -/// A lock could not be acquired at this time because the operation would otherwise block. -#[unstable(feature = "sync_nonpoison", issue = "134645")] -pub struct WouldBlock; - -#[unstable(feature = "sync_nonpoison", issue = "134645")] -impl fmt::Debug for WouldBlock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "WouldBlock".fmt(f) - } -} - -#[unstable(feature = "sync_nonpoison", issue = "134645")] -impl fmt::Display for WouldBlock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "try_lock failed because the operation would block".fmt(f) - } -} - -#[unstable(feature = "nonpoison_condvar", issue = "134645")] -pub use self::condvar::Condvar; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::mutex::MappedMutexGuard; -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -pub use self::mutex::{Mutex, MutexGuard}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; - -mod condvar; -mod mutex; -mod rwlock; diff --git a/crates/std/src/sync/nonpoison/condvar.rs b/crates/std/src/sync/nonpoison/condvar.rs deleted file mode 100644 index d2b251d..0000000 --- a/crates/std/src/sync/nonpoison/condvar.rs +++ /dev/null @@ -1,444 +0,0 @@ -use crate::fmt; -use crate::ops::DerefMut; -use crate::sync::WaitTimeoutResult; -use crate::sync::nonpoison::{MutexGuard, mutex}; -use crate::sys::sync as sys; -use crate::time::{Duration, Instant}; - -/// A Condition Variable -/// -/// For more information about condition variables, check out the documentation for the poisoning -/// variant of this type at [`poison::Condvar`]. -/// -/// # Examples -/// -/// Note that this `Condvar` does **not** propagate information about threads that panic while -/// holding a lock. If you need this functionality, see [`poison::Mutex`] and [`poison::Condvar`]. -/// -/// ``` -/// #![feature(nonpoison_mutex)] -/// #![feature(nonpoison_condvar)] -/// -/// use std::sync::nonpoison::{Mutex, Condvar}; -/// use std::sync::Arc; -/// use std::thread; -/// -/// let pair = Arc::new((Mutex::new(false), Condvar::new())); -/// let pair2 = Arc::clone(&pair); -/// -/// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move || { -/// let (lock, cvar) = &*pair2; -/// let mut started = lock.lock(); -/// *started = true; -/// // We notify the condvar that the value has changed. -/// cvar.notify_one(); -/// }); -/// -/// // Wait for the thread to start up. -/// let (lock, cvar) = &*pair; -/// let mut started = lock.lock(); -/// while !*started { -/// cvar.wait(&mut started); -/// } -/// ``` -/// -/// [`poison::Mutex`]: crate::sync::poison::Mutex -/// [`poison::Condvar`]: crate::sync::poison::Condvar -#[unstable(feature = "nonpoison_condvar", issue = "134645")] -pub struct Condvar { - inner: sys::Condvar, -} - -impl Condvar { - /// Creates a new condition variable which is ready to be waited on and - /// notified. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Condvar; - /// - /// let condvar = Condvar::new(); - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - #[must_use] - #[inline] - pub const fn new() -> Condvar { - Condvar { inner: sys::Condvar::new() } - } - - /// Blocks the current thread until this condition variable receives a - /// notification. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// Note that this function is susceptible to spurious wakeups. Condition - /// variables normally have a boolean predicate associated with them, and - /// the predicate must always be checked each time this function returns to - /// protect against spurious wakeups. - /// - /// # Panics - /// - /// This function may [`panic!`] if it is used with more than one mutex - /// over time. - /// - /// [`notify_one`]: Self::notify_one - /// [`notify_all`]: Self::notify_all - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// cvar.wait(&mut started); - /// } - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait(&self, guard: &mut MutexGuard<'_, T>) { - unsafe { - let lock = mutex::guard_lock(guard); - self.inner.wait(lock); - } - } - - /// Blocks the current thread until the provided condition becomes false. - /// - /// `condition` is checked immediately; if not met (returns `true`), this - /// will [`wait`] for the next notification then check again. This repeats - /// until `condition` returns `false`, in which case this function returns. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// [`wait`]: Self::wait - /// [`notify_one`]: Self::notify_one - /// [`notify_all`]: Self::notify_all - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// // As long as the value inside the `Mutex` is `true`, we wait. - /// let mut guard = lock.lock(); - /// cvar.wait_while(&mut guard, |pending| { *pending }); - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_while(&self, guard: &mut MutexGuard<'_, T>, mut condition: F) - where - F: FnMut(&mut T) -> bool, - { - while condition(guard.deref_mut()) { - self.wait(guard); - } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait`] except that - /// the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that might not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. This function is susceptible to spurious wakeups. - /// Condition variables normally have a boolean predicate associated with - /// them, and the predicate must always be checked each time this function - /// returns to protect against spurious wakeups. Furthermore, since the timeout - /// is given relative to the moment this function is called, it needs to be adjusted - /// when this function is called in a loop. The [`wait_timeout_while`] method - /// lets you wait with a timeout while a predicate is true, taking care of all these concerns. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed. - /// - /// Like [`wait`], the lock specified will have been re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait`]: Self::wait - /// [`wait_timeout_while`]: Self::wait_timeout_while - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock(); - /// // as long as the value inside the `Mutex` is `false`, we wait - /// loop { - /// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10)); - /// // 10 milliseconds have passed, or maybe the value changed! - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout( - &self, - guard: &mut MutexGuard<'_, T>, - dur: Duration, - ) -> WaitTimeoutResult { - let success = unsafe { - let lock = mutex::guard_lock(guard); - self.inner.wait_timeout(lock, dur) - }; - WaitTimeoutResult(!success) - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait_while`] except - /// that the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that might not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed without the condition being met. - /// - /// Like [`wait_while`], the lock specified will have been re-acquired when this - /// function returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait_while`]: Self::wait_while - /// [`wait_timeout`]: Self::wait_timeout - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let mut guard = lock.lock(); - /// let result = cvar.wait_timeout_while( - /// &mut guard, - /// Duration::from_millis(100), - /// |&mut pending| pending, - /// ); - /// if result.timed_out() { - /// // timed-out without the condition ever evaluating to false. - /// } - /// // access the locked mutex via guard - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn wait_timeout_while( - &self, - guard: &mut MutexGuard<'_, T>, - dur: Duration, - mut condition: F, - ) -> WaitTimeoutResult - where - F: FnMut(&mut T) -> bool, - { - let start = Instant::now(); - - while condition(guard.deref_mut()) { - let timeout = match dur.checked_sub(start.elapsed()) { - Some(timeout) => timeout, - None => return WaitTimeoutResult(true), - }; - - self.wait_timeout(guard, timeout); - } - - WaitTimeoutResult(false) - } - - /// Wakes up one blocked thread on this condvar. - /// - /// If there is a blocked thread on this condition variable, then it will - /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to - /// `notify_one` are not buffered in any way. - /// - /// To wake up all threads, see [`notify_all`]. - /// - /// [`wait`]: Self::wait - /// [`wait_timeout`]: Self::wait_timeout - /// [`notify_all`]: Self::notify_all - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// cvar.wait(&mut started); - /// } - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn notify_one(&self) { - self.inner.notify_one() - } - - /// Wakes up all blocked threads on this condvar. - /// - /// This method will ensure that any current waiters on the condition - /// variable are awoken. Calls to `notify_all()` are not buffered in any - /// way. - /// - /// To wake up only one thread, see [`notify_one`]. - /// - /// [`notify_one`]: Self::notify_one - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_all(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// cvar.wait(&mut started); - /// } - /// ``` - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn notify_all(&self) { - self.inner.notify_all() - } -} - -#[unstable(feature = "nonpoison_condvar", issue = "134645")] -impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Condvar").finish_non_exhaustive() - } -} - -#[unstable(feature = "nonpoison_condvar", issue = "134645")] -impl Default for Condvar { - /// Creates a `Condvar` which is ready to be waited on and notified. - fn default() -> Condvar { - Condvar::new() - } -} diff --git a/crates/std/src/sync/nonpoison/mutex.rs b/crates/std/src/sync/nonpoison/mutex.rs deleted file mode 100644 index 307bf8e..0000000 --- a/crates/std/src/sync/nonpoison/mutex.rs +++ /dev/null @@ -1,649 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop}; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::NonNull; -use crate::sync::nonpoison::{TryLockResult, WouldBlock}; -use crate::sys::sync as sys; - -/// A mutual exclusion primitive useful for protecting shared data that does not keep track of -/// lock poisoning. -/// -/// For more information about mutexes, check out the documentation for the poisoning variant of -/// this lock at [`poison::Mutex`]. -/// -/// [`poison::Mutex`]: crate::sync::poison::Mutex -/// -/// # Examples -/// -/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via -/// poisoning. If you need this functionality, see [`poison::Mutex`]. -/// -/// ``` -/// #![feature(nonpoison_mutex)] -/// -/// use std::thread; -/// use std::sync::{Arc, nonpoison::Mutex}; -/// -/// let mutex = Arc::new(Mutex::new(0u32)); -/// let mut handles = Vec::new(); -/// -/// for n in 0..10 { -/// let m = Arc::clone(&mutex); -/// let handle = thread::spawn(move || { -/// let mut guard = m.lock(); -/// *guard += 1; -/// panic!("panic from thread {n} {guard}") -/// }); -/// handles.push(handle); -/// } -/// -/// for h in handles { -/// let _ = h.join(); -/// } -/// -/// println!("Finished, locked {} times", mutex.lock()); -/// ``` -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")] -pub struct Mutex { - inner: sys::Mutex, - data: UnsafeCell, -} - -/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire -/// the owned `T` from the `Mutex` via [`into_inner`]. -/// -/// [`into_inner`]: Mutex::into_inner -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -unsafe impl Send for Mutex {} - -/// `T` must be `Send` for [`Mutex`] to be `Sync`. -/// This ensures that the protected data can be accessed safely from multiple threads -/// without causing data races or other unsafe behavior. -/// -/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential -/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in -/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, -/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap -/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would -/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable -/// to potential data races. -/// -/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available -/// to one thread at a time if `T` is not `Sync`. -/// -/// [`Rc`]: crate::rc::Rc -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -unsafe impl Sync for Mutex {} - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] and [`DerefMut`] implementations. -/// -/// This structure is created by the [`lock`] and [`try_lock`] methods on -/// [`Mutex`]. -/// -/// [`lock`]: Mutex::lock -/// [`try_lock`]: Mutex::try_lock -#[must_use = "if unused the Mutex will immediately unlock"] -#[must_not_suspend = "holding a MutexGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")] -pub struct MutexGuard<'a, T: ?Sized + 'a> { - lock: &'a Mutex, -} - -/// A [`MutexGuard`] is not `Send` to maximize platform portability. -/// -/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to -/// release mutex locks on the same thread they were acquired. -/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from -/// another thread. -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl !Send for MutexGuard<'_, T> {} - -/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` -/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -unsafe impl Sync for MutexGuard<'_, T> {} - -/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a -/// subfield of the protected data. When this structure is dropped (falls out -/// of scope), the lock will be unlocked. -/// -/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the -/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the -/// locked object is modified by another thread while the `Mutex` is unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] and [`DerefMut`] implementations. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods on -/// [`MutexGuard`]. -/// -/// [`map`]: MutexGuard::map -/// [`filter_map`]: MutexGuard::filter_map -/// [`Condvar`]: crate::sync::nonpoison::Condvar -#[must_use = "if unused the Mutex will immediately unlock"] -#[must_not_suspend = "holding a MappedMutexGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_mutex", issue = "134645")] -#[clippy::has_significant_drop] -pub struct MappedMutexGuard<'a, T: ?Sized + 'a> { - // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a - // `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. - // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field - // below for the correct variance over `T` (invariance). - data: NonNull, - inner: &'a sys::Mutex, - _variance: PhantomData<&'a mut T>, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl !Send for MappedMutexGuard<'_, T> {} -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_mutex", issue = "134645")] -unsafe impl Sync for MappedMutexGuard<'_, T> {} - -impl Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mutex = Mutex::new(0); - /// ``` - #[unstable(feature = "nonpoison_mutex", issue = "134645")] - #[inline] - pub const fn new(t: T) -> Mutex { - Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) } - } - - /// Returns the contained value by cloning it. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn get_cloned(&self) -> T - where - T: Clone, - { - self.lock().clone() - } - - /// Sets the contained value. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned(), 7); - /// mutex.set(11); - /// assert_eq!(mutex.get_cloned(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn set(&self, value: T) { - if mem::needs_drop::() { - // If the contained value has a non-trivial destructor, we - // call that destructor after the lock has been released. - drop(self.replace(value)) - } else { - *self.lock() = value; - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.replace(11), 7); - /// assert_eq!(mutex.get_cloned(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn replace(&self, value: T) -> T { - let mut guard = self.lock(); - mem::replace(&mut *guard, value) - } -} - -impl Mutex { - /// Acquires a mutex, blocking the current thread until it is able to do so. - /// - /// This function will block the local thread until it is available to acquire - /// the mutex. Upon returning, the thread is the only thread with the lock - /// held. An RAII guard is returned to allow scoped unlock of the lock. When - /// the guard goes out of scope, the mutex will be unlocked. - /// - /// The exact behavior on locking a mutex in the thread which already holds - /// the lock is left unspecified. However, this function will not return on - /// the second call (it might panic or deadlock, for example). - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by - /// the current thread. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// - /// use std::sync::{Arc, nonpoison::Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// thread::spawn(move || { - /// *c_mutex.lock() = 10; - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock(), 10); - /// ``` - #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn lock(&self) -> MutexGuard<'_, T> { - unsafe { - self.inner.lock(); - MutexGuard::new(self) - } - } - - /// Attempts to acquire this lock. - /// - /// This function does not block. If the lock could not be acquired at this time, then - /// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned. - /// - /// The lock will be unlocked when the guard is dropped. - /// - /// # Errors - /// - /// If the mutex could not be acquired because it is already locked, then this call will return - /// the [`WouldBlock`] error. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// thread::spawn(move || { - /// let mut lock = c_mutex.try_lock(); - /// if let Ok(ref mut mutex) = lock { - /// **mutex = 10; - /// } else { - /// println!("try_lock failed"); - /// } - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn try_lock(&self) -> TryLockResult> { - unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } } - } - - /// Consumes this mutex, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mutex = Mutex::new(0); - /// assert_eq!(mutex.into_inner(), 0); - /// ``` - #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn into_inner(self) -> T - where - T: Sized, - { - self.data.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `Mutex` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no locks exist. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mut mutex = Mutex::new(0); - /// *mutex.get_mut() = 10; - /// assert_eq!(*mutex.lock(), 10); - /// ``` - #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn get_mut(&mut self) -> &mut T { - self.data.get_mut() - } - - /// Returns a raw pointer to the underlying data. - /// - /// The returned pointer is always non-null and properly aligned, but it is - /// the user's responsibility to ensure that any reads and writes through it - /// are properly synchronized to avoid data races, and that it is not read - /// or written through after the mutex is dropped. - #[unstable(feature = "mutex_data_ptr", issue = "140368")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub const fn data_ptr(&self) -> *mut T { - self.data.get() - } - - /// Acquires the mutex and provides mutable access to the underlying data by passing - /// a mutable reference to the given closure. - /// - /// This method acquires the lock, calls the provided closure with a mutable reference - /// to the data, and returns the result of the closure. The lock is released after - /// the closure completes, even if it panics. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors, nonpoison_mutex)] - /// - /// use std::sync::nonpoison::Mutex; - /// - /// let mutex = Mutex::new(2); - /// - /// let result = mutex.with_mut(|data| { - /// *data += 3; - /// - /// *data + 5 - /// }); - /// - /// assert_eq!(*mutex.lock(), 5); - /// assert_eq!(result, 10); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn with_mut(&self, f: F) -> R - where - F: FnOnce(&mut T) -> R, - { - f(&mut self.lock()) - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl From for Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// This is equivalent to [`Mutex::new`]. - fn from(t: T) -> Self { - Mutex::new(t) - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl Default for Mutex { - /// Creates a `Mutex`, with the `Default` value for T. - fn default() -> Mutex { - Mutex::new(Default::default()) - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("Mutex"); - match self.try_lock() { - Ok(guard) => { - d.field("data", &&*guard); - } - Err(WouldBlock) => { - d.field("data", &""); - } - } - d.finish_non_exhaustive() - } -} - -impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { - unsafe fn new(lock: &'mutex Mutex) -> MutexGuard<'mutex, T> { - return MutexGuard { lock }; - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl Deref for MutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.lock.data.get() } - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl DerefMut for MutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.lock.data.get() } - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl Drop for MutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.lock.inner.unlock(); - } - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -/// For use in [`nonpoison::condvar`](super::condvar). -pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { - &guard.lock.inner -} - -impl<'a, T: ?Sized> MutexGuard<'a, T> { - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MutexGuard::map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); - let orig = ManuallyDrop::new(orig); - MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData } - } - - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { &mut *orig.lock.data.get() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }) - } - None => Err(orig), - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Deref for MappedMutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl DerefMut for MappedMutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { self.data.as_mut() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Drop for MappedMutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.inner.unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Debug for MappedMutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Display for MappedMutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedMutexGuard::map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_mut() })); - let orig = ManuallyDrop::new(orig); - MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData } - } - - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_mutex", issue = "134645")] - pub fn filter_map(mut orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_mut() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }) - } - None => Err(orig), - } - } -} diff --git a/crates/std/src/sync/nonpoison/rwlock.rs b/crates/std/src/sync/nonpoison/rwlock.rs deleted file mode 100644 index dc5d947..0000000 --- a/crates/std/src/sync/nonpoison/rwlock.rs +++ /dev/null @@ -1,1140 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop, forget}; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::NonNull; -use crate::sync::nonpoison::{TryLockResult, WouldBlock}; -use crate::sys::sync as sys; - -/// A reader-writer lock that does not keep track of lock poisoning. -/// -/// For more information about reader-writer locks, check out the documentation for the poisoning -/// variant of this lock (which can be found at [`poison::RwLock`]). -/// -/// [`poison::RwLock`]: crate::sync::poison::RwLock -/// -/// # Examples -/// -/// ``` -/// #![feature(nonpoison_rwlock)] -/// -/// use std::sync::nonpoison::RwLock; -/// -/// let lock = RwLock::new(5); -/// -/// // many reader locks can be held at once -/// { -/// let r1 = lock.read(); -/// let r2 = lock.read(); -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// } // read locks are dropped at this point -/// -/// // only one write lock may be held, however -/// { -/// let mut w = lock.write(); -/// *w += 1; -/// assert_eq!(*w, 6); -/// } // write lock is dropped here -/// ``` -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLock")] -pub struct RwLock { - /// The inner [`sys::RwLock`] that synchronizes thread access to the protected data. - inner: sys::RwLock, - /// The lock-protected data. - data: UnsafeCell, -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Send for RwLock {} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Sync for RwLock {} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Guards -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// RAII structure used to release the shared read access of a lock when -/// dropped. -/// -/// This structure is created by the [`read`] and [`try_read`] methods on -/// [`RwLock`]. -/// -/// [`read`]: RwLock::read -/// [`try_read`]: RwLock::try_read -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a RwLockReadGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLockReadGuard")] -pub struct RwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `RwLockReadGuard` instance only holds - /// immutability until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also - /// covariant over `T`, just like we would have with `&T`. - data: NonNull, - /// A reference to the internal [`sys::RwLock`] that we have read-locked. - inner_lock: &'rwlock sys::RwLock, -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl !Send for RwLockReadGuard<'_, T> {} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Sync for RwLockReadGuard<'_, T> {} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped. -/// -/// This structure is created by the [`write`] and [`try_write`] methods -/// on [`RwLock`]. -/// -/// [`write`]: RwLock::write -/// [`try_write`]: RwLock::try_write -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a RwLockWriteGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Future's to not implement `Send`"] -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLockWriteGuard")] -pub struct RwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A reference to the [`RwLock`] that we have write-locked. - lock: &'rwlock RwLock, -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl !Send for RwLockWriteGuard<'_, T> {} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Sync for RwLockWriteGuard<'_, T> {} - -/// RAII structure used to release the shared read access of a lock when -/// dropped, which can point to a subfield of the protected data. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods -/// on [`RwLockReadGuard`]. -/// -/// [`map`]: RwLockReadGuard::map -/// [`filter_map`]: RwLockReadGuard::filter_map -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a MappedRwLockReadGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -#[clippy::has_significant_drop] -pub struct MappedRwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockReadGuard` instance only - /// holds immutability until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also - /// covariant over `T`, just like we would have with `&T`. - data: NonNull, - /// A reference to the internal [`sys::RwLock`] that we have read-locked. - inner_lock: &'rwlock sys::RwLock, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl !Send for MappedRwLockReadGuard<'_, T> {} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Sync for MappedRwLockReadGuard<'_, T> {} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped, which can point to a subfield of the protected data. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods -/// on [`RwLockWriteGuard`]. -/// -/// [`map`]: RwLockWriteGuard::map -/// [`filter_map`]: RwLockWriteGuard::filter_map -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a MappedRwLockWriteGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Future's to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -#[clippy::has_significant_drop] -pub struct MappedRwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockWriteGuard` instance only - /// holds uniquneness until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. - data: NonNull, - /// `NonNull` is covariant over `T`, so we add a `PhantomData<&'rwlock mut T>` field here to - /// enforce the correct invariance over `T`. - _variance: PhantomData<&'rwlock mut T>, - /// A reference to the internal [`sys::RwLock`] that we have write-locked. - inner_lock: &'rwlock sys::RwLock, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl !Send for MappedRwLockWriteGuard<'_, T> {} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -unsafe impl Sync for MappedRwLockWriteGuard<'_, T> {} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Implementations -//////////////////////////////////////////////////////////////////////////////////////////////////// - -impl RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let lock = RwLock::new(5); - /// ``` - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - #[inline] - pub const fn new(t: T) -> RwLock { - RwLock { inner: sys::RwLock::new(), data: UnsafeCell::new(t) } - } - - /// Returns the contained value by cloning it. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn get_cloned(&self) -> T - where - T: Clone, - { - self.read().clone() - } - - /// Sets the contained value. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned(), 7); - /// lock.set(11); - /// assert_eq!(lock.get_cloned(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn set(&self, value: T) { - if mem::needs_drop::() { - // If the contained value has a non-trivial destructor, we - // call that destructor after the lock has been released. - drop(self.replace(value)) - } else { - *self.write() = value; - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.replace(11), 7); - /// assert_eq!(lock.get_cloned(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn replace(&self, value: T) -> T { - let mut guard = self.write(); - mem::replace(&mut *guard, value) - } -} - -impl RwLock { - /// Locks this `RwLock` with shared read access, blocking the current thread - /// until it can be acquired. - /// - /// The calling thread will be blocked until there are no more writers which - /// hold the lock. There may be other readers currently inside the lock when - /// this method returns. This method does not provide any guarantees with - /// respect to the ordering of whether contentious readers or writers will - /// acquire the lock first. - /// - /// Returns an RAII guard which will release this thread's shared access - /// once it is dropped. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::Arc; - /// use std::sync::nonpoison::RwLock; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(1)); - /// let c_lock = Arc::clone(&lock); - /// - /// let n = lock.read(); - /// assert_eq!(*n, 1); - /// - /// thread::spawn(move || { - /// let r = c_lock.read(); - /// }).join().unwrap(); - /// ``` - #[inline] - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn read(&self) -> RwLockReadGuard<'_, T> { - unsafe { - self.inner.read(); - RwLockReadGuard::new(self) - } - } - - /// Attempts to acquire this `RwLock` with shared read access. - /// - /// If the access could not be granted at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the shared access - /// when it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return the [`WouldBlock`] error if the `RwLock` could - /// not be acquired because it was already locked exclusively. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// match lock.try_read() { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; - /// ``` - #[inline] - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn try_read(&self) -> TryLockResult> { - unsafe { - if self.inner.try_read() { Ok(RwLockReadGuard::new(self)) } else { Err(WouldBlock) } - } - } - - /// Locks this `RwLock` with exclusive write access, blocking the current - /// thread until it can be acquired. - /// - /// This function will not return while other writers or other readers - /// currently have access to the lock. - /// - /// Returns an RAII guard which will drop the write access of this `RwLock` - /// when dropped. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let mut n = lock.write(); - /// *n = 2; - /// - /// assert!(lock.try_read().is_err()); - /// ``` - #[inline] - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn write(&self) -> RwLockWriteGuard<'_, T> { - unsafe { - self.inner.write(); - RwLockWriteGuard::new(self) - } - } - - /// Attempts to lock this `RwLock` with exclusive write access. - /// - /// If the lock could not be acquired at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the lock when - /// it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return the [`WouldBlock`] error if the `RwLock` could - /// not be acquired because it was already locked. - /// - /// [`WouldBlock`]: WouldBlock - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read(); - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_write().is_err()); - /// ``` - #[inline] - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn try_write(&self) -> TryLockResult> { - unsafe { - if self.inner.try_write() { Ok(RwLockWriteGuard::new(self)) } else { Err(WouldBlock) } - } - } - - /// Consumes this `RwLock`, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let lock = RwLock::new(String::new()); - /// { - /// let mut s = lock.write(); - /// *s = "modified".to_owned(); - /// } - /// assert_eq!(lock.into_inner(), "modified"); - /// ``` - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn into_inner(self) -> T - where - T: Sized, - { - self.data.into_inner() - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `RwLock` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no new locks can be acquired - /// while this reference exists. Note that this method does not clear any previously abandoned - /// locks (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). - /// - /// # Examples - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let mut lock = RwLock::new(0); - /// *lock.get_mut() = 10; - /// assert_eq!(*lock.read(), 10); - /// ``` - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn get_mut(&mut self) -> &mut T { - self.data.get_mut() - } - - /// Returns a raw pointer to the underlying data. - /// - /// The returned pointer is always non-null and properly aligned, but it is - /// the user's responsibility to ensure that any reads and writes through it - /// are properly synchronized to avoid data races, and that it is not read - /// or written through after the lock is dropped. - #[unstable(feature = "rwlock_data_ptr", issue = "140368")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub const fn data_ptr(&self) -> *mut T { - self.data.get() - } - - /// Locks this `RwLock` with shared read access to the underlying data by passing - /// a reference to the given closure. - /// - /// This method acquires the lock, calls the provided closure with a reference - /// to the data, and returns the result of the closure. The lock is released after - /// the closure completes, even if it panics. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors, nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let rwlock = RwLock::new(2); - /// let result = rwlock.with(|data| *data + 3); - /// - /// assert_eq!(result, 5); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn with(&self, f: F) -> R - where - F: FnOnce(&T) -> R, - { - f(&self.read()) - } - - /// Locks this `RwLock` with exclusive write access to the underlying data by passing - /// a mutable reference to the given closure. - /// - /// This method acquires the lock, calls the provided closure with a mutable reference - /// to the data, and returns the result of the closure. The lock is released after - /// the closure completes, even if it panics. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors, nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::RwLock; - /// - /// let rwlock = RwLock::new(2); - /// - /// let result = rwlock.with_mut(|data| { - /// *data += 3; - /// - /// *data + 5 - /// }); - /// - /// assert_eq!(*rwlock.read(), 5); - /// assert_eq!(result, 10); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn with_mut(&self, f: F) -> R - where - F: FnOnce(&mut T) -> R, - { - f(&mut self.write()) - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("RwLock"); - match self.try_read() { - Ok(guard) => { - d.field("data", &&*guard); - } - Err(WouldBlock) => { - d.field("data", &format_args!("")); - } - } - d.finish_non_exhaustive() - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Default for RwLock { - /// Creates a new `RwLock`, with the `Default` value for T. - fn default() -> RwLock { - RwLock::new(Default::default()) - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl From for RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// This is equivalent to [`RwLock::new`]. - fn from(t: T) -> Self { - RwLock::new(t) - } -} - -impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { - /// Creates a new instance of `RwLockReadGuard` from a `RwLock`. - /// - /// # Safety - /// - /// This function is safe if and only if the same thread has successfully and safely called - /// `lock.inner.read()`, `lock.inner.try_read()`, or `lock.inner.downgrade()` before - /// instantiating this object. - unsafe fn new(lock: &'rwlock RwLock) -> RwLockReadGuard<'rwlock, T> { - RwLockReadGuard { - data: unsafe { NonNull::new_unchecked(lock.data.get()) }, - inner_lock: &lock.inner, - } - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockReadGuard::map(...)`. A method would interfere with methods of - /// the same name on the contents of the `RwLockReadGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> - where - F: FnOnce(&T) -> &U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_ref() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockReadGuard::filter_map(...)`. A method would interfere with methods - /// of the same name on the contents of the `RwLockReadGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&T) -> Option<&U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_ref() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - /// Creates a new instance of `RwLockWriteGuard` from a `RwLock`. - /// - /// # Safety - /// - /// This function is safe if and only if the same thread has successfully and safely called - /// `lock.inner.write()`, `lock.inner.try_write()`, or `lock.inner.try_upgrade` before - /// instantiating this object. - unsafe fn new(lock: &'rwlock RwLock) -> RwLockWriteGuard<'rwlock, T> { - RwLockWriteGuard { lock } - } - - /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. - /// - /// Since we have the `RwLockWriteGuard`, the [`RwLock`] must already be locked for writing, so - /// this method cannot fail. - /// - /// After downgrading, other readers will be allowed to read the protected data. - /// - /// # Examples - /// - /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; - /// - /// let rw = RwLock::new(0); - /// - /// let mut write_guard = rw.write(); - /// *write_guard = 42; - /// - /// let read_guard = RwLockWriteGuard::downgrade(write_guard); - /// assert_eq!(42, *read_guard); - /// ``` - /// - /// `downgrade` will _atomically_ change the state of the [`RwLock`] from exclusive mode into - /// shared mode. This means that it is impossible for another writing thread to get in between a - /// thread calling `downgrade` and any reads it performs after downgrading. - /// - /// ``` - /// #![feature(nonpoison_rwlock)] - /// - /// use std::sync::Arc; - /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; - /// - /// let rw = Arc::new(RwLock::new(1)); - /// - /// // Put the lock in write mode. - /// let mut main_write_guard = rw.write(); - /// - /// let rw_clone = rw.clone(); - /// let evil_handle = std::thread::spawn(move || { - /// // This will not return until the main thread drops the `main_read_guard`. - /// let mut evil_guard = rw_clone.write(); - /// - /// assert_eq!(*evil_guard, 2); - /// *evil_guard = 3; - /// }); - /// - /// *main_write_guard = 2; - /// - /// // Atomically downgrade the write guard into a read guard. - /// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); - /// - /// // Since `downgrade` is atomic, the writer thread cannot have changed the protected data. - /// assert_eq!(*main_read_guard, 2, "`downgrade` was not atomic"); - /// # - /// # drop(main_read_guard); - /// # evil_handle.join().unwrap(); - /// # - /// # let final_check = rw.read(); - /// # assert_eq!(*final_check, 3); - /// ``` - #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { - let lock = s.lock; - - // We don't want to call the destructor since that calls `write_unlock`. - forget(s); - - // SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write - // mode, satisfying the `downgrade` contract. - unsafe { lock.inner.downgrade() }; - - // SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract. - unsafe { RwLockReadGuard::new(lock) } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::map(...)`. A method would interfere with methods of - /// the same name on the contents of the `RwLockWriteGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn map(orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockWriteGuard { data, inner_lock: &orig.lock.inner, _variance: PhantomData } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::filter_map(...)`. A method would interfere with methods - /// of the same name on the contents of the `RwLockWriteGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { &mut *orig.lock.data.get() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockWriteGuard { - data, - inner_lock: &orig.lock.inner, - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, - /// e.g. an enum variant. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> - where - F: FnOnce(&T) -> &U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_ref() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. - /// The original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&T) -> Option<&U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_ref() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> MappedRwLockWriteGuard<'rwlock, T> { - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, - /// e.g. an enum variant. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockWriteGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn map(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_mut() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockWriteGuard { data, inner_lock: orig.inner_lock, _variance: PhantomData } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. - /// The original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::filter_map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockWriteGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked). - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] - pub fn filter_map( - mut orig: Self, - f: F, - ) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_mut() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockWriteGuard { - data, - inner_lock: orig.inner_lock, - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { - self.lock.inner.write_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Drop for MappedRwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Drop for MappedRwLockWriteGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.write_unlock(); - } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &*self.lock.data.get() } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &mut *self.lock.data.get() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Deref for MappedRwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl Deref for MappedRwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl DerefMut for MappedRwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_mut() } - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Debug for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Display for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Debug for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] -impl fmt::Display for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} diff --git a/crates/std/src/sync/once.rs b/crates/std/src/sync/once.rs deleted file mode 100644 index 2556d18..0000000 --- a/crates/std/src/sync/once.rs +++ /dev/null @@ -1,402 +0,0 @@ -//! A "once initialization" primitive -//! -//! This primitive is meant to be used to run one-time initialization. An -//! example use case would be for initializing an FFI library. - -use crate::fmt; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sys::sync as sys; - -/// A low-level synchronization primitive for one-time global execution. -/// -/// Previously this was the only "execute once" synchronization in `std`. -/// Other libraries implemented novel synchronizing types with `Once`, like -/// [`OnceLock`] or [`LazyLock`], before those were added to `std`. -/// `OnceLock` in particular supersedes `Once` in functionality and should -/// be preferred for the common case where the `Once` is associated with data. -/// -/// This type can only be constructed with [`Once::new()`]. -/// -/// # Examples -/// -/// ``` -/// use std::sync::Once; -/// -/// static START: Once = Once::new(); -/// -/// START.call_once(|| { -/// // run initialization here -/// }); -/// ``` -/// -/// [`OnceLock`]: crate::sync::OnceLock -/// [`LazyLock`]: crate::sync::LazyLock -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Once { - inner: sys::Once, -} - -#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")] -impl UnwindSafe for Once {} - -#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")] -impl RefUnwindSafe for Once {} - -/// State yielded to [`Once::call_once_force()`]’s closure parameter. The state -/// can be used to query the poison status of the [`Once`]. -#[stable(feature = "once_poison", since = "1.51.0")] -pub struct OnceState { - pub(crate) inner: sys::OnceState, -} - -/// Used for the internal implementation of `sys::sync::once` on different platforms and the -/// [`LazyLock`](crate::sync::LazyLock) implementation. -pub(crate) enum OnceExclusiveState { - Incomplete, - Poisoned, - Complete, -} - -/// Initialization value for static [`Once`] values. -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Once, ONCE_INIT}; -/// -/// static START: Once = ONCE_INIT; -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated( - since = "1.38.0", - note = "the `Once::new()` function is now preferred", - suggestion = "Once::new()" -)] -pub const ONCE_INIT: Once = Once::new(); - -impl Once { - /// Creates a new `Once` value. - #[inline] - #[stable(feature = "once_new", since = "1.2.0")] - #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")] - #[must_use] - pub const fn new() -> Once { - Once { inner: sys::Once::new() } - } - - /// Creates a new `Once` value that starts already completed. - #[inline] - #[must_use] - pub(crate) const fn new_complete() -> Once { - Once { inner: sys::Once::new_complete() } - } - - /// Performs an initialization routine once and only once. The given closure - /// will be executed if this is the first time `call_once` has been called, - /// and otherwise the routine will *not* be invoked. - /// - /// This method will block the calling thread if another initialization - /// routine is currently running. - /// - /// When this function returns, it is guaranteed that some initialization - /// has run and completed (it might not be the closure specified). It is also - /// guaranteed that any memory writes performed by the executed closure can - /// be reliably observed by other threads at this point (there is a - /// happens-before relation between the closure and code executing after the - /// return). - /// - /// If the given closure recursively invokes `call_once` on the same [`Once`] - /// instance, the exact behavior is not specified: allowed outcomes are - /// a panic or a deadlock. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Once; - /// - /// static mut VAL: usize = 0; - /// static INIT: Once = Once::new(); - /// - /// // Accessing a `static mut` is unsafe much of the time, but if we do so - /// // in a synchronized fashion (e.g., write once or read all) then we're - /// // good to go! - /// // - /// // This function will only call `expensive_computation` once, and will - /// // otherwise always return the value returned from the first invocation. - /// fn get_cached_val() -> usize { - /// unsafe { - /// INIT.call_once(|| { - /// VAL = expensive_computation(); - /// }); - /// VAL - /// } - /// } - /// - /// fn expensive_computation() -> usize { - /// // ... - /// # 2 - /// } - /// ``` - /// - /// # Panics - /// - /// The closure `f` will only be executed once even if this is called - /// concurrently amongst many threads. If that closure panics, however, then - /// it will *poison* this [`Once`] instance, causing all future invocations of - /// `call_once` to also panic. - /// - /// This is similar to [poisoning with mutexes][poison], but this mechanism - /// is guaranteed to never skip panics within `f`. - /// - /// [poison]: struct.Mutex.html#poisoning - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[track_caller] - #[rustc_should_not_be_called_on_const_items] - pub fn call_once(&self, f: F) - where - F: FnOnce(), - { - // Fast path check - if self.inner.is_completed() { - return; - } - - let mut f = Some(f); - self.inner.call(false, &mut |_| f.take().unwrap()()); - } - - /// Performs the same function as [`call_once()`] except ignores poisoning. - /// - /// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous - /// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling - /// [`call_once_force()`] will still invoke the closure `f` and will _not_ - /// result in an immediate panic. If `f` panics, the [`Once`] will remain - /// in a poison state. If `f` does _not_ panic, the [`Once`] will no - /// longer be in a poison state and all future calls to [`call_once()`] or - /// [`call_once_force()`] will be no-ops. - /// - /// The closure `f` is yielded a [`OnceState`] structure which can be used - /// to query the poison status of the [`Once`]. - /// - /// [`call_once()`]: Once::call_once - /// [`call_once_force()`]: Once::call_once_force - /// - /// # Examples - /// - /// ``` - /// use std::sync::Once; - /// use std::thread; - /// - /// static INIT: Once = Once::new(); - /// - /// // poison the once - /// let handle = thread::spawn(|| { - /// INIT.call_once(|| panic!()); - /// }); - /// assert!(handle.join().is_err()); - /// - /// // poisoning propagates - /// let handle = thread::spawn(|| { - /// INIT.call_once(|| {}); - /// }); - /// assert!(handle.join().is_err()); - /// - /// // call_once_force will still run and reset the poisoned state - /// INIT.call_once_force(|state| { - /// assert!(state.is_poisoned()); - /// }); - /// - /// // once any success happens, we stop propagating the poison - /// INIT.call_once(|| {}); - /// ``` - #[inline] - #[stable(feature = "once_poison", since = "1.51.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn call_once_force(&self, f: F) - where - F: FnOnce(&OnceState), - { - // Fast path check - if self.inner.is_completed() { - return; - } - - let mut f = Some(f); - self.inner.call(true, &mut |p| f.take().unwrap()(p)); - } - - /// Returns `true` if some [`call_once()`] call has completed - /// successfully. Specifically, `is_completed` will return false in - /// the following situations: - /// * [`call_once()`] was not called at all, - /// * [`call_once()`] was called, but has not yet completed, - /// * the [`Once`] instance is poisoned - /// - /// This function returning `false` does not mean that [`Once`] has not been - /// executed. For example, it may have been executed in the time between - /// when `is_completed` starts executing and when it returns, in which case - /// the `false` return value would be stale (but still permissible). - /// - /// [`call_once()`]: Once::call_once - /// - /// # Examples - /// - /// ``` - /// use std::sync::Once; - /// - /// static INIT: Once = Once::new(); - /// - /// assert_eq!(INIT.is_completed(), false); - /// INIT.call_once(|| { - /// assert_eq!(INIT.is_completed(), false); - /// }); - /// assert_eq!(INIT.is_completed(), true); - /// ``` - /// - /// ``` - /// use std::sync::Once; - /// use std::thread; - /// - /// static INIT: Once = Once::new(); - /// - /// assert_eq!(INIT.is_completed(), false); - /// let handle = thread::spawn(|| { - /// INIT.call_once(|| panic!()); - /// }); - /// assert!(handle.join().is_err()); - /// assert_eq!(INIT.is_completed(), false); - /// ``` - #[stable(feature = "once_is_completed", since = "1.43.0")] - #[inline] - pub fn is_completed(&self) -> bool { - self.inner.is_completed() - } - - /// Blocks the current thread until initialization has completed. - /// - /// # Example - /// - /// ```rust - /// use std::sync::Once; - /// use std::thread; - /// - /// static READY: Once = Once::new(); - /// - /// let thread = thread::spawn(|| { - /// READY.wait(); - /// println!("everything is ready"); - /// }); - /// - /// READY.call_once(|| println!("performing setup")); - /// ``` - /// - /// # Panics - /// - /// If this [`Once`] has been poisoned because an initialization closure has - /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) - /// if this behavior is not desired. - #[stable(feature = "once_wait", since = "1.86.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait(&self) { - if !self.inner.is_completed() { - self.inner.wait(false); - } - } - - /// Blocks the current thread until initialization has completed, ignoring - /// poisoning. - /// - /// If this [`Once`] has been poisoned, this function blocks until it - /// becomes completed, unlike [`Once::wait()`], which panics in this case. - #[stable(feature = "once_wait", since = "1.86.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait_force(&self) { - if !self.inner.is_completed() { - self.inner.wait(true); - } - } - - /// Returns the current state of the `Once` instance. - /// - /// Since this takes a mutable reference, no initialization can currently - /// be running, so the state must be either "incomplete", "poisoned" or - /// "complete". - #[inline] - pub(crate) fn state(&mut self) -> OnceExclusiveState { - self.inner.state() - } - - /// Sets current state of the `Once` instance. - /// - /// Since this takes a mutable reference, no initialization can currently - /// be running, so the state must be either "incomplete", "poisoned" or - /// "complete". - #[inline] - pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) { - self.inner.set_state(new_state); - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Once { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Once").finish_non_exhaustive() - } -} - -impl OnceState { - /// Returns `true` if the associated [`Once`] was poisoned prior to the - /// invocation of the closure passed to [`Once::call_once_force()`]. - /// - /// # Examples - /// - /// A poisoned [`Once`]: - /// - /// ``` - /// use std::sync::Once; - /// use std::thread; - /// - /// static INIT: Once = Once::new(); - /// - /// // poison the once - /// let handle = thread::spawn(|| { - /// INIT.call_once(|| panic!()); - /// }); - /// assert!(handle.join().is_err()); - /// - /// INIT.call_once_force(|state| { - /// assert!(state.is_poisoned()); - /// }); - /// ``` - /// - /// An unpoisoned [`Once`]: - /// - /// ``` - /// use std::sync::Once; - /// - /// static INIT: Once = Once::new(); - /// - /// INIT.call_once_force(|state| { - /// assert!(!state.is_poisoned()); - /// }); - #[stable(feature = "once_poison", since = "1.51.0")] - #[inline] - pub fn is_poisoned(&self) -> bool { - self.inner.is_poisoned() - } - - /// Poison the associated [`Once`] without explicitly panicking. - // NOTE: This is currently only exposed for `OnceLock`. - #[inline] - pub(crate) fn poison(&self) { - self.inner.poison(); - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for OnceState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OnceState").field("poisoned", &self.is_poisoned()).finish() - } -} diff --git a/crates/std/src/sync/once_lock.rs b/crates/std/src/sync/once_lock.rs deleted file mode 100644 index f6ea3c1..0000000 --- a/crates/std/src/sync/once_lock.rs +++ /dev/null @@ -1,709 +0,0 @@ -use super::once::OnceExclusiveState; -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::marker::PhantomData; -use crate::mem::MaybeUninit; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::Once; - -/// A synchronization primitive which can nominally be written to only once. -/// -/// This type is a thread-safe [`OnceCell`], and can be used in statics. -/// In many simple cases, you can use [`LazyLock`] instead to get the benefits of this type -/// with less effort: `LazyLock` "looks like" `&T` because it initializes with `F` on deref! -/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock -/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. -/// -/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes -/// initialized once written. -/// -/// Unlike [`Mutex`](crate::sync::Mutex), `OnceLock` is never poisoned on panic. -/// -/// [`OnceCell`]: crate::cell::OnceCell -/// [`LazyLock`]: crate::sync::LazyLock -/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new -/// -/// # Examples -/// -/// Writing to a `OnceLock` from a separate thread: -/// -/// ``` -/// use std::sync::OnceLock; -/// -/// static CELL: OnceLock = OnceLock::new(); -/// -/// // `OnceLock` has not been written to yet. -/// assert!(CELL.get().is_none()); -/// -/// // Spawn a thread and write to `OnceLock`. -/// std::thread::spawn(|| { -/// let value = CELL.get_or_init(|| 12345); -/// assert_eq!(value, &12345); -/// }) -/// .join() -/// .unwrap(); -/// -/// // `OnceLock` now contains the value. -/// assert_eq!( -/// CELL.get(), -/// Some(&12345), -/// ); -/// ``` -/// -/// You can use `OnceLock` to implement a type that requires "append-only" logic: -/// -/// ``` -/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}}; -/// use std::thread; -/// -/// struct OnceList { -/// data: OnceLock, -/// next: OnceLock>>, -/// } -/// impl OnceList { -/// const fn new() -> OnceList { -/// OnceList { data: OnceLock::new(), next: OnceLock::new() } -/// } -/// fn push(&self, value: T) { -/// // FIXME: this impl is concise, but is also slow for long lists or many threads. -/// // as an exercise, consider how you might improve on it while preserving the behavior -/// if let Err(value) = self.data.set(value) { -/// let next = self.next.get_or_init(|| Box::new(OnceList::new())); -/// next.push(value) -/// }; -/// } -/// fn contains(&self, example: &T) -> bool -/// where -/// T: PartialEq, -/// { -/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| { -/// self.next.get().map(|next| next.contains(example)).unwrap_or(false) -/// }) -/// } -/// } -/// -/// // Let's exercise this new Sync append-only list by doing a little counting -/// static LIST: OnceList = OnceList::new(); -/// static COUNTER: AtomicU32 = AtomicU32::new(0); -/// -/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 }; -/// # /* -/// const LEN: u32 = 1000; -/// # */ -/// thread::scope(|s| { -/// for _ in 0..thread::available_parallelism().unwrap().get() { -/// s.spawn(|| { -/// while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) { -/// LIST.push(i); -/// } -/// }); -/// } -/// }); -/// -/// for i in 0..LEN { -/// assert!(LIST.contains(&i)); -/// } -/// -/// ``` -#[stable(feature = "once_cell", since = "1.70.0")] -pub struct OnceLock { - // FIXME(nonpoison_once): switch to nonpoison version once it is available - once: Once, - // Whether or not the value is initialized is tracked by `once.is_completed()`. - value: UnsafeCell>, - /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl. - /// - /// ```compile_fail,E0597 - /// use std::sync::OnceLock; - /// - /// struct A<'a>(&'a str); - /// - /// impl<'a> Drop for A<'a> { - /// fn drop(&mut self) {} - /// } - /// - /// let cell = OnceLock::new(); - /// { - /// let s = String::new(); - /// let _ = cell.set(A(&s)); - /// } - /// ``` - _marker: PhantomData, -} - -impl OnceLock { - /// Creates a new uninitialized cell. - #[inline] - #[must_use] - #[stable(feature = "once_cell", since = "1.70.0")] - #[rustc_const_stable(feature = "once_cell", since = "1.70.0")] - pub const fn new() -> OnceLock { - OnceLock { - once: Once::new(), - value: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData, - } - } - - /// Gets the reference to the underlying value. - /// - /// Returns `None` if the cell is uninitialized, or being initialized. - /// This method never blocks. - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn get(&self) -> Option<&T> { - if self.initialized() { - // Safe b/c checked initialized - Some(unsafe { self.get_unchecked() }) - } else { - None - } - } - - /// Gets the mutable reference to the underlying value. - /// - /// Returns `None` if the cell is uninitialized. - /// - /// This method never blocks. Since it borrows the `OnceLock` mutably, - /// it is statically guaranteed that no active borrows to the `OnceLock` - /// exist, including from other threads. - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - pub fn get_mut(&mut self) -> Option<&mut T> { - if self.initialized_mut() { - // Safe b/c checked initialized and we have a unique access - Some(unsafe { self.get_unchecked_mut() }) - } else { - None - } - } - - /// Blocks the current thread until the cell is initialized. - /// - /// # Example - /// - /// Waiting for a computation on another thread to finish: - /// ```rust - /// use std::thread; - /// use std::sync::OnceLock; - /// - /// let value = OnceLock::new(); - /// - /// thread::scope(|s| { - /// s.spawn(|| value.set(1 + 1)); - /// - /// let result = value.wait(); - /// assert_eq!(result, &2); - /// }) - /// ``` - #[inline] - #[stable(feature = "once_wait", since = "1.86.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait(&self) -> &T { - self.once.wait_force(); - - unsafe { self.get_unchecked() } - } - - /// Initializes the contents of the cell to `value`. - /// - /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. - /// - /// Returns `Ok(())` if the cell was uninitialized and - /// `Err(value)` if the cell was already initialized. - /// - /// # Examples - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// static CELL: OnceLock = OnceLock::new(); - /// - /// fn main() { - /// assert!(CELL.get().is_none()); - /// - /// std::thread::spawn(|| { - /// assert_eq!(CELL.set(92), Ok(())); - /// }).join().unwrap(); - /// - /// assert_eq!(CELL.set(62), Err(62)); - /// assert_eq!(CELL.get(), Some(&92)); - /// } - /// ``` - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn set(&self, value: T) -> Result<(), T> { - match self.try_insert(value) { - Ok(_) => Ok(()), - Err((_, value)) => Err(value), - } - } - - /// Initializes the contents of the cell to `value` if the cell was uninitialized, - /// then returns a reference to it. - /// - /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when `try_insert` returns, though not necessarily the - /// one provided. - /// - /// Returns `Ok(&value)` if the cell was uninitialized and - /// `Err((¤t_value, value))` if it was already initialized. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell_try_insert)] - /// - /// use std::sync::OnceLock; - /// - /// static CELL: OnceLock = OnceLock::new(); - /// - /// fn main() { - /// assert!(CELL.get().is_none()); - /// - /// std::thread::spawn(|| { - /// assert_eq!(CELL.try_insert(92), Ok(&92)); - /// }).join().unwrap(); - /// - /// assert_eq!(CELL.try_insert(62), Err((&92, 62))); - /// assert_eq!(CELL.get(), Some(&92)); - /// } - /// ``` - #[inline] - #[unstable(feature = "once_cell_try_insert", issue = "116693")] - #[rustc_should_not_be_called_on_const_items] - pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> { - let mut value = Some(value); - let res = self.get_or_init(|| value.take().unwrap()); - match value { - None => Ok(res), - Some(value) => Err((res, value)), - } - } - - /// Gets the contents of the cell, initializing it to `f()` if the cell - /// was uninitialized. - /// - /// Many threads may call `get_or_init` concurrently with different - /// initializing functions, but it is guaranteed that only one function - /// will be executed if the function doesn't panic. - /// - /// # Panics - /// - /// If `f()` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. The - /// exact outcome is unspecified. Current implementation deadlocks, but - /// this may be changed to a panic in the future. - /// - /// # Examples - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// let cell = OnceLock::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn get_or_init(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(f())) { - Ok(val) => val, - } - } - - /// Gets the mutable reference of the contents of the cell, initializing - /// it to `f()` if the cell was uninitialized. - /// - /// This method never blocks. Since it borrows the `OnceLock` mutably, - /// it is statically guaranteed that no active borrows to the `OnceLock` - /// exist, including from other threads. - /// - /// # Panics - /// - /// If `f()` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell_get_mut)] - /// - /// use std::sync::OnceLock; - /// - /// let mut cell = OnceLock::new(); - /// let value = cell.get_mut_or_init(|| 92); - /// assert_eq!(*value, 92); - /// - /// *value += 2; - /// assert_eq!(*value, 94); - /// - /// let value = cell.get_mut_or_init(|| unreachable!()); - /// assert_eq!(*value, 94); - /// ``` - #[inline] - #[unstable(feature = "once_cell_get_mut", issue = "121641")] - pub fn get_mut_or_init(&mut self, f: F) -> &mut T - where - F: FnOnce() -> T, - { - match self.get_mut_or_try_init(|| Ok::(f())) { - Ok(val) => val, - } - } - - /// Gets the contents of the cell, initializing it to `f()` if - /// the cell was uninitialized. If the cell was uninitialized - /// and `f()` failed, an error is returned. - /// - /// # Panics - /// - /// If `f()` panics, the panic is propagated to the caller, and - /// the cell remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. - /// The exact outcome is unspecified. Current implementation - /// deadlocks, but this may be changed to a panic in the future. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell_try)] - /// - /// use std::sync::OnceLock; - /// - /// let cell = OnceLock::new(); - /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - /// assert!(cell.get().is_none()); - /// let value = cell.get_or_try_init(|| -> Result { - /// Ok(92) - /// }); - /// assert_eq!(value, Ok(&92)); - /// assert_eq!(cell.get(), Some(&92)) - /// ``` - #[inline] - #[unstable(feature = "once_cell_try", issue = "109737")] - #[rustc_should_not_be_called_on_const_items] - pub fn get_or_try_init(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - // Fast path check - // NOTE: We need to perform an acquire on the state in this method - // in order to correctly synchronize `LazyLock::force`. This is - // currently done by calling `self.get()`, which in turn calls - // `self.initialized()`, which in turn performs the acquire. - if let Some(value) = self.get() { - return Ok(value); - } - self.initialize(f)?; - - // SAFETY: The inner value has been initialized - Ok(unsafe { self.get_unchecked() }) - } - - /// Gets the mutable reference of the contents of the cell, initializing - /// it to `f()` if the cell was uninitialized. If the cell was uninitialized - /// and `f()` failed, an error is returned. - /// - /// This method never blocks. Since it borrows the `OnceLock` mutably, - /// it is statically guaranteed that no active borrows to the `OnceLock` - /// exist, including from other threads. - /// - /// # Panics - /// - /// If `f()` panics, the panic is propagated to the caller, and - /// the cell remains uninitialized. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell_get_mut)] - /// - /// use std::sync::OnceLock; - /// - /// let mut cell: OnceLock = OnceLock::new(); - /// - /// // Failed attempts to initialize the cell do not change its contents - /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); - /// assert!(cell.get().is_none()); - /// - /// let value = cell.get_mut_or_try_init(|| "1234".parse()); - /// assert_eq!(value, Ok(&mut 1234)); - /// *value.unwrap() += 2; - /// assert_eq!(cell.get(), Some(&1236)) - /// ``` - #[inline] - #[unstable(feature = "once_cell_get_mut", issue = "121641")] - pub fn get_mut_or_try_init(&mut self, f: F) -> Result<&mut T, E> - where - F: FnOnce() -> Result, - { - if self.get_mut().is_none() { - self.initialize(f)?; - } - - // SAFETY: The inner value has been initialized - Ok(unsafe { self.get_unchecked_mut() }) - } - - /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was uninitialized. - /// - /// # Examples - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// let cell: OnceLock = OnceLock::new(); - /// assert_eq!(cell.into_inner(), None); - /// - /// let cell = OnceLock::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); - /// ``` - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - pub fn into_inner(mut self) -> Option { - self.take() - } - - /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. - /// - /// Has no effect and returns `None` if the `OnceLock` was uninitialized. - /// - /// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that - /// no active borrows to the `OnceLock` exist, including from other threads. - /// - /// # Examples - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// let mut cell: OnceLock = OnceLock::new(); - /// assert_eq!(cell.take(), None); - /// - /// let mut cell = OnceLock::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.take(), Some("hello".to_string())); - /// assert_eq!(cell.get(), None); - /// ``` - #[inline] - #[stable(feature = "once_cell", since = "1.70.0")] - pub fn take(&mut self) -> Option { - if self.initialized_mut() { - self.once = Once::new(); - // SAFETY: `self.value` is initialized and contains a valid `T`. - // `self.once` is reset, so `initialized()` will be false again - // which prevents the value from being read twice. - unsafe { Some(self.value.get_mut().assume_init_read()) } - } else { - None - } - } - - #[inline] - fn initialized(&self) -> bool { - self.once.is_completed() - } - - #[inline] - fn initialized_mut(&mut self) -> bool { - // `state()` does not perform an atomic load, so prefer it over `is_complete()`. - let state = self.once.state(); - match state { - OnceExclusiveState::Complete => true, - _ => false, - } - } - - #[cold] - #[optimize(size)] - fn initialize(&self, f: F) -> Result<(), E> - where - F: FnOnce() -> Result, - { - let mut res: Result<(), E> = Ok(()); - let slot = &self.value; - - // Ignore poisoning from other threads - // If another thread panics, then we'll be able to run our closure - self.once.call_once_force(|p| { - match f() { - Ok(value) => { - unsafe { (&mut *slot.get()).write(value) }; - } - Err(e) => { - res = Err(e); - - // Treat the underlying `Once` as poisoned since we - // failed to initialize our value. - p.poison(); - } - } - }); - res - } - - /// # Safety - /// - /// The cell must be initialized - #[inline] - unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.initialized()); - unsafe { (&*self.value.get()).assume_init_ref() } - } - - /// # Safety - /// - /// The cell must be initialized - #[inline] - unsafe fn get_unchecked_mut(&mut self) -> &mut T { - debug_assert!(self.initialized_mut()); - unsafe { self.value.get_mut().assume_init_mut() } - } -} - -// Why do we need `T: Send`? -// Thread A creates a `OnceLock` and shares it with -// scoped thread B, which fills the cell, which is -// then destroyed by A. That is, destructor observes -// a sent value. -#[stable(feature = "once_cell", since = "1.70.0")] -unsafe impl Sync for OnceLock {} -#[stable(feature = "once_cell", since = "1.70.0")] -unsafe impl Send for OnceLock {} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl RefUnwindSafe for OnceLock {} -#[stable(feature = "once_cell", since = "1.70.0")] -impl UnwindSafe for OnceLock {} - -#[stable(feature = "once_cell", since = "1.70.0")] -#[rustc_const_unstable(feature = "const_default", issue = "143894")] -impl const Default for OnceLock { - /// Creates a new uninitialized cell. - /// - /// # Example - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// fn main() { - /// assert_eq!(OnceLock::<()>::new(), OnceLock::default()); - /// } - /// ``` - #[inline] - fn default() -> OnceLock { - OnceLock::new() - } -} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl fmt::Debug for OnceLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_tuple("OnceLock"); - match self.get() { - Some(v) => d.field(v), - None => d.field(&format_args!("")), - }; - d.finish() - } -} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl Clone for OnceLock { - #[inline] - fn clone(&self) -> OnceLock { - let cell = Self::new(); - if let Some(value) = self.get() { - match cell.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } - } - cell - } -} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl From for OnceLock { - /// Creates a new cell with its contents set to `value`. - /// - /// # Example - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// # fn main() -> Result<(), i32> { - /// let a = OnceLock::from(3); - /// let b = OnceLock::new(); - /// b.set(3)?; - /// assert_eq!(a, b); - /// Ok(()) - /// # } - /// ``` - #[inline] - fn from(value: T) -> Self { - let cell = Self::new(); - match cell.set(value) { - Ok(()) => cell, - Err(_) => unreachable!(), - } - } -} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl PartialEq for OnceLock { - /// Equality for two `OnceLock`s. - /// - /// Two `OnceLock`s are equal if they either both contain values and their - /// values are equal, or if neither contains a value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::OnceLock; - /// - /// let five = OnceLock::new(); - /// five.set(5).unwrap(); - /// - /// let also_five = OnceLock::new(); - /// also_five.set(5).unwrap(); - /// - /// assert!(five == also_five); - /// - /// assert!(OnceLock::::new() == OnceLock::::new()); - /// ``` - #[inline] - fn eq(&self, other: &OnceLock) -> bool { - self.get() == other.get() - } -} - -#[stable(feature = "once_cell", since = "1.70.0")] -impl Eq for OnceLock {} - -#[stable(feature = "once_cell", since = "1.70.0")] -unsafe impl<#[may_dangle] T> Drop for OnceLock { - #[inline] - fn drop(&mut self) { - if self.initialized_mut() { - // SAFETY: The cell is initialized and being dropped, so it can't - // be accessed again. We also don't touch the `T` other than - // dropping it, which validates our usage of #[may_dangle]. - unsafe { self.value.get_mut().assume_init_drop() }; - } - } -} diff --git a/crates/std/src/sync/oneshot.rs b/crates/std/src/sync/oneshot.rs deleted file mode 100644 index b2c9ba3..0000000 --- a/crates/std/src/sync/oneshot.rs +++ /dev/null @@ -1,466 +0,0 @@ -//! A single-producer, single-consumer (oneshot) channel. -//! -//! This is an experimental module, so the API will likely change. - -use crate::sync::mpmc; -use crate::sync::mpsc::{RecvError, SendError}; -use crate::time::{Duration, Instant}; -use crate::{error, fmt}; - -/// Creates a new oneshot channel, returning the sender/receiver halves. -/// -/// # Examples -/// -/// ``` -/// #![feature(oneshot_channel)] -/// use std::sync::oneshot; -/// use std::thread; -/// -/// let (sender, receiver) = oneshot::channel(); -/// -/// // Spawn off an expensive computation. -/// thread::spawn(move || { -/// # fn expensive_computation() -> i32 { 42 } -/// sender.send(expensive_computation()).unwrap(); -/// // `sender` is consumed by `send`, so we cannot use it anymore. -/// }); -/// -/// # fn do_other_work() -> i32 { 42 } -/// do_other_work(); -/// -/// // Let's see what that answer was... -/// println!("{:?}", receiver.recv().unwrap()); -/// // `receiver` is consumed by `recv`, so we cannot use it anymore. -/// ``` -#[must_use] -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub fn channel() -> (Sender, Receiver) { - // Using a `sync_channel` with capacity 1 means that the internal implementation will use the - // `Array`-flavored channel implementation. - let (sender, receiver) = mpmc::sync_channel(1); - (Sender { inner: sender }, Receiver { inner: receiver }) -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Sender -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// The sending half of a oneshot channel. -/// -/// # Examples -/// -/// ``` -/// #![feature(oneshot_channel)] -/// use std::sync::oneshot; -/// use std::thread; -/// -/// let (sender, receiver) = oneshot::channel(); -/// -/// thread::spawn(move || { -/// sender.send("Hello from thread!").unwrap(); -/// }); -/// -/// assert_eq!(receiver.recv().unwrap(), "Hello from thread!"); -/// ``` -/// -/// `Sender` cannot be sent between threads if it is sending non-`Send` types. -/// -/// ```compile_fail -/// #![feature(oneshot_channel)] -/// use std::sync::oneshot; -/// use std::thread; -/// use std::ptr; -/// -/// let (sender, receiver) = oneshot::channel(); -/// -/// struct NotSend(*mut ()); -/// thread::spawn(move || { -/// sender.send(NotSend(ptr::null_mut())); -/// }); -/// -/// let reply = receiver.try_recv().unwrap(); -/// ``` -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub struct Sender { - /// The `oneshot` channel is simply a wrapper around a `mpmc` channel. - inner: mpmc::Sender, -} - -// SAFETY: Since the only methods in which synchronization must occur take full ownership of the -// [`Sender`], it is perfectly safe to share a `&Sender` between threads (as it is effectively -// useless without ownership). -#[unstable(feature = "oneshot_channel", issue = "143674")] -unsafe impl Sync for Sender {} - -impl Sender { - /// Attempts to send a value through this channel. This can only fail if the corresponding - /// [`Receiver`] has been dropped. - /// - /// This method is non-blocking (wait-free). - /// - /// # Examples - /// - /// ``` - /// #![feature(oneshot_channel)] - /// use std::sync::oneshot; - /// use std::thread; - /// - /// let (tx, rx) = oneshot::channel(); - /// - /// thread::spawn(move || { - /// // Perform some computation. - /// let result = 2 + 2; - /// tx.send(result).unwrap(); - /// }); - /// - /// assert_eq!(rx.recv().unwrap(), 4); - /// ``` - #[unstable(feature = "oneshot_channel", issue = "143674")] - pub fn send(self, t: T) -> Result<(), SendError> { - self.inner.send(t) - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Receiver -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// The receiving half of a oneshot channel. -/// -/// # Examples -/// -/// ``` -/// #![feature(oneshot_channel)] -/// use std::sync::oneshot; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = oneshot::channel(); -/// -/// thread::spawn(move || { -/// thread::sleep(Duration::from_millis(100)); -/// sender.send("Hello after delay!").unwrap(); -/// }); -/// -/// println!("Waiting for message..."); -/// println!("{}", receiver.recv().unwrap()); -/// ``` -/// -/// `Receiver` cannot be sent between threads if it is receiving non-`Send` types. -/// -/// ```compile_fail -/// # #![feature(oneshot_channel)] -/// # use std::sync::oneshot; -/// # use std::thread; -/// # use std::ptr; -/// # -/// let (sender, receiver) = oneshot::channel(); -/// -/// struct NotSend(*mut ()); -/// sender.send(NotSend(ptr::null_mut())); -/// -/// thread::spawn(move || { -/// let reply = receiver.try_recv().unwrap(); -/// }); -/// ``` -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub struct Receiver { - /// The `oneshot` channel is simply a wrapper around a `mpmc` channel. - inner: mpmc::Receiver, -} - -// SAFETY: Since the only methods in which synchronization must occur take full ownership of the -// [`Receiver`], it is perfectly safe to share a `&Receiver` between threads (as it is unable to -// receive any values without ownership). -#[unstable(feature = "oneshot_channel", issue = "143674")] -unsafe impl Sync for Receiver {} - -impl Receiver { - /// Receives the value from the sending end, blocking the calling thread until it gets it. - /// - /// Can only fail if the corresponding [`Sender`] has been dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(oneshot_channel)] - /// use std::sync::oneshot; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (tx, rx) = oneshot::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(500)); - /// tx.send("Done!").unwrap(); - /// }); - /// - /// // This will block until the message arrives. - /// println!("{}", rx.recv().unwrap()); - /// ``` - #[unstable(feature = "oneshot_channel", issue = "143674")] - pub fn recv(self) -> Result { - self.inner.recv() - } - - // Fallible methods. - - /// Attempts to return a pending value on this receiver without blocking. - /// - /// # Examples - /// - /// ``` - /// #![feature(oneshot_channel)] - /// use std::sync::oneshot; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, mut receiver) = oneshot::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(100)); - /// sender.send(42).unwrap(); - /// }); - /// - /// // Keep trying until we get the message, doing other work in the process. - /// loop { - /// match receiver.try_recv() { - /// Ok(value) => { - /// assert_eq!(value, 42); - /// break; - /// } - /// Err(oneshot::TryRecvError::Empty(rx)) => { - /// // Retake ownership of the receiver. - /// receiver = rx; - /// # fn do_other_work() { thread::sleep(Duration::from_millis(25)); } - /// do_other_work(); - /// } - /// Err(oneshot::TryRecvError::Disconnected) => panic!("Sender disconnected"), - /// } - /// } - /// ``` - #[unstable(feature = "oneshot_channel", issue = "143674")] - pub fn try_recv(self) -> Result> { - self.inner.try_recv().map_err(|err| match err { - mpmc::TryRecvError::Empty => TryRecvError::Empty(self), - mpmc::TryRecvError::Disconnected => TryRecvError::Disconnected, - }) - } - - /// Attempts to wait for a value on this receiver, returning an error if the corresponding - /// [`Sender`] half of this channel has been dropped, or if it waits more than `timeout`. - /// - /// # Examples - /// - /// ``` - /// #![feature(oneshot_channel)] - /// use std::sync::oneshot; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, receiver) = oneshot::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(500)); - /// sender.send("Success!").unwrap(); - /// }); - /// - /// // Wait up to 1 second for the message - /// match receiver.recv_timeout(Duration::from_secs(1)) { - /// Ok(msg) => println!("Received: {}", msg), - /// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Timed out!"), - /// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"), - /// } - /// ``` - #[unstable(feature = "oneshot_channel", issue = "143674")] - pub fn recv_timeout(self, timeout: Duration) -> Result> { - self.inner.recv_timeout(timeout).map_err(|err| match err { - mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self), - mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected, - }) - } - - /// Attempts to wait for a value on this receiver, returning an error if the corresponding - /// [`Sender`] half of this channel has been dropped, or if `deadline` is reached. - /// - /// # Examples - /// - /// ``` - /// #![feature(oneshot_channel)] - /// use std::sync::oneshot; - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// - /// let (sender, receiver) = oneshot::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(100)); - /// sender.send("Just in time!").unwrap(); - /// }); - /// - /// let deadline = Instant::now() + Duration::from_millis(500); - /// match receiver.recv_deadline(deadline) { - /// Ok(msg) => println!("Received: {}", msg), - /// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Missed deadline!"), - /// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"), - /// } - /// ``` - #[unstable(feature = "oneshot_channel", issue = "143674")] - pub fn recv_deadline(self, deadline: Instant) -> Result> { - self.inner.recv_deadline(deadline).map_err(|err| match err { - mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self), - mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected, - }) - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Receiver Errors -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// An error returned from the [`try_recv`](Receiver::try_recv) method. -/// -/// See the documentation for [`try_recv`] for more information on how to use this error. -/// -/// [`try_recv`]: Receiver::try_recv -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub enum TryRecvError { - /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet - /// disconnected). This variant contains the [`Receiver`] that [`try_recv`](Receiver::try_recv) - /// took ownership over. - Empty(Receiver), - /// The corresponding [`Sender`] half of this channel has become disconnected, and there will - /// never be any more data sent over the channel. - Disconnected, -} - -/// An error returned from the [`recv_timeout`](Receiver::recv_timeout) or -/// [`recv_deadline`](Receiver::recv_deadline) methods. -/// -/// # Examples -/// -/// Usage of this error is similar to [`TryRecvError`]. -/// -/// ``` -/// #![feature(oneshot_channel)] -/// use std::sync::oneshot::{self, RecvTimeoutError}; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = oneshot::channel(); -/// -/// let send_failure = thread::spawn(move || { -/// // Simulate a long computation that takes longer than our timeout. -/// thread::sleep(Duration::from_millis(250)); -/// -/// // This will likely fail to send because we drop the receiver in the main thread. -/// sender.send("Goodbye!".to_string()).unwrap(); -/// }); -/// -/// // Try to receive the message with a short timeout. -/// match receiver.recv_timeout(Duration::from_millis(10)) { -/// Ok(msg) => println!("Received: {}", msg), -/// Err(RecvTimeoutError::Timeout(rx)) => { -/// println!("Timed out waiting for message!"); -/// -/// // Note that you can reuse the receiver without dropping it. -/// drop(rx); -/// }, -/// Err(RecvTimeoutError::Disconnected) => println!("Sender dropped!"), -/// } -/// -/// send_failure.join().unwrap_err(); -/// ``` -#[unstable(feature = "oneshot_channel", issue = "143674")] -pub enum RecvTimeoutError { - /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet - /// disconnected). This variant contains the [`Receiver`] that either - /// [`recv_timeout`](Receiver::recv_timeout) or [`recv_deadline`](Receiver::recv_deadline) took - /// ownership over. - Timeout(Receiver), - /// The corresponding [`Sender`] half of this channel has become disconnected, and there will - /// never be any more data sent over the channel. - Disconnected, -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Debug for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TryRecvError").finish_non_exhaustive() - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryRecvError::Empty(..) => "receiving on an empty oneshot channel".fmt(f), - TryRecvError::Disconnected => "receiving on a closed oneshot channel".fmt(f), - } - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl error::Error for TryRecvError {} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl From for TryRecvError { - /// Converts a `RecvError` into a `TryRecvError`. - /// - /// This conversion always returns `TryRecvError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> TryRecvError { - match err { - RecvError => TryRecvError::Disconnected, - } - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Debug for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("RecvTimeoutError").finish_non_exhaustive() - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl fmt::Display for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RecvTimeoutError::Timeout(..) => "timed out waiting on oneshot channel".fmt(f), - RecvTimeoutError::Disconnected => "receiving on a closed oneshot channel".fmt(f), - } - } -} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl error::Error for RecvTimeoutError {} - -#[unstable(feature = "oneshot_channel", issue = "143674")] -impl From for RecvTimeoutError { - /// Converts a `RecvError` into a `RecvTimeoutError`. - /// - /// This conversion always returns `RecvTimeoutError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> RecvTimeoutError { - match err { - RecvError => RecvTimeoutError::Disconnected, - } - } -} diff --git a/crates/std/src/sync/poison.rs b/crates/std/src/sync/poison.rs deleted file mode 100644 index 9f40c01..0000000 --- a/crates/std/src/sync/poison.rs +++ /dev/null @@ -1,389 +0,0 @@ -//! Synchronization objects that employ poisoning. -//! -//! # Poisoning -//! -//! All synchronization objects in this module implement a strategy called -//! "poisoning" where a primitive becomes poisoned if it recognizes that some -//! thread has panicked while holding the exclusive access granted by the -//! primitive. This information is then propagated to all other threads -//! to signify that the data protected by this primitive is likely tainted -//! (some invariant is not being upheld). -//! -//! The specifics of how this "poisoned" state affects other threads and whether -//! the panics are recognized reliably or on a best-effort basis depend on the -//! primitive. See [Overview](#overview) below. -//! -//! The synchronization objects in this module have alternative implementations that do not employ -//! poisoning in the [`std::sync::nonpoison`] module. -//! -//! [`std::sync::nonpoison`]: crate::sync::nonpoison -//! -//! # Overview -//! -//! Below is a list of synchronization objects provided by this module -//! with a high-level overview for each object and a description -//! of how it employs "poisoning". -//! -//! - [`Condvar`]: Condition Variable, providing the ability to block -//! a thread while waiting for an event to occur. -//! -//! Condition variables are typically associated with -//! a boolean predicate (a condition) and a mutex. -//! This implementation is associated with [`poison::Mutex`](Mutex), -//! which employs poisoning. -//! For this reason, [`Condvar::wait()`] will return a [`LockResult`], -//! just like [`poison::Mutex::lock()`](Mutex::lock) does. -//! -//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at -//! most one thread at a time is able to access some data. -//! -//! Panicking while holding the lock typically poisons the mutex, but it is -//! not guaranteed to detect this condition in all circumstances. -//! [`Mutex::lock()`] returns a [`LockResult`], providing a way to deal with -//! the poisoned state. See [`Mutex`'s documentation](Mutex#poisoning) for more. -//! -//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows -//! multiple readers at the same time, while allowing only one -//! writer at a time. In some cases, this can be more efficient than -//! a mutex. -//! -//! This implementation, like [`Mutex`], usually becomes poisoned on a panic. -//! Note, however, that an `RwLock` may only be poisoned if a panic occurs -//! while it is locked exclusively (write mode). If a panic occurs in any reader, -//! then the lock will not be poisoned. -//! -//! Note that the [`Once`] type also employs poisoning, but since it has non-poisoning `force` -//! methods available on it, there is no separate `nonpoison` and `poison` version. -//! -//! [`Once`]: crate::sync::Once - -// If we are not unwinding, `PoisonError` is uninhabited. -#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::Condvar; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::mutex::MappedMutexGuard; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::{Mutex, MutexGuard}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use crate::error::Error; -use crate::fmt; -#[cfg(panic = "unwind")] -use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; -#[cfg(panic = "unwind")] -use crate::thread; - -mod condvar; -#[stable(feature = "rust1", since = "1.0.0")] -mod mutex; -mod rwlock; - -pub(crate) struct Flag { - #[cfg(panic = "unwind")] - failed: Atomic, -} - -// Note that the Ordering uses to access the `failed` field of `Flag` below is -// always `Relaxed`, and that's because this isn't actually protecting any data, -// it's just a flag whether we've panicked or not. -// -// The actual location that this matters is when a mutex is **locked** which is -// where we have external synchronization ensuring that we see memory -// reads/writes to this flag. -// -// As a result, if it matters, we should see the correct value for `failed` in -// all cases. - -impl Flag { - #[inline] - pub const fn new() -> Flag { - Flag { - #[cfg(panic = "unwind")] - failed: AtomicBool::new(false), - } - } - - /// Checks the flag for an unguarded borrow, where we only care about existing poison. - #[inline] - pub fn borrow(&self) -> LockResult<()> { - if self.get() { Err(PoisonError::new(())) } else { Ok(()) } - } - - /// Checks the flag for a guarded borrow, where we may also set poison when `done`. - #[inline] - pub fn guard(&self) -> LockResult { - let ret = Guard { - #[cfg(panic = "unwind")] - panicking: thread::panicking(), - }; - if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) } - } - - #[inline] - #[cfg(panic = "unwind")] - pub fn done(&self, guard: &Guard) { - if !guard.panicking && thread::panicking() { - self.failed.store(true, Ordering::Relaxed); - } - } - - #[inline] - #[cfg(not(panic = "unwind"))] - pub fn done(&self, _guard: &Guard) {} - - #[inline] - #[cfg(panic = "unwind")] - pub fn get(&self) -> bool { - self.failed.load(Ordering::Relaxed) - } - - #[inline(always)] - #[cfg(not(panic = "unwind"))] - pub fn get(&self) -> bool { - false - } - - #[inline] - pub fn clear(&self) { - #[cfg(panic = "unwind")] - self.failed.store(false, Ordering::Relaxed) - } -} - -#[derive(Clone)] -pub(crate) struct Guard { - #[cfg(panic = "unwind")] - panicking: bool, -} - -/// A type of error which can be returned whenever a lock is acquired. -/// -/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock -/// is held. The precise semantics for when a lock is poisoned is documented on -/// each lock. For a lock in the poisoned state, unless the state is cleared manually, -/// all future acquisitions will return this error. -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// -/// let mutex = Arc::new(Mutex::new(1)); -/// -/// // poison the mutex -/// let c_mutex = Arc::clone(&mutex); -/// let _ = thread::spawn(move || { -/// let mut data = c_mutex.lock().unwrap(); -/// *data = 2; -/// panic!(); -/// }).join(); -/// -/// match mutex.lock() { -/// Ok(_) => unreachable!(), -/// Err(p_err) => { -/// let data = p_err.get_ref(); -/// println!("recovered: {data}"); -/// } -/// }; -/// ``` -/// [`Mutex`]: crate::sync::Mutex -/// [`RwLock`]: crate::sync::RwLock -#[stable(feature = "rust1", since = "1.0.0")] -pub struct PoisonError { - data: T, - #[cfg(not(panic = "unwind"))] - _never: !, -} - -/// An enumeration of possible errors associated with a [`TryLockResult`] which -/// can occur while trying to acquire a lock, from the [`try_lock`] method on a -/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`]. -/// -/// [`try_lock`]: crate::sync::Mutex::try_lock -/// [`try_read`]: crate::sync::RwLock::try_read -/// [`try_write`]: crate::sync::RwLock::try_write -/// [`Mutex`]: crate::sync::Mutex -/// [`RwLock`]: crate::sync::RwLock -#[stable(feature = "rust1", since = "1.0.0")] -pub enum TryLockError { - /// The lock could not be acquired because another thread failed while holding - /// the lock. - #[stable(feature = "rust1", since = "1.0.0")] - Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError), - /// The lock could not be acquired at this time because the operation would - /// otherwise block. - #[stable(feature = "rust1", since = "1.0.0")] - WouldBlock, -} - -/// A type alias for the result of a lock method which can be poisoned. -/// -/// The [`Ok`] variant of this result indicates that the primitive was not -/// poisoned, and the operation result is contained within. The [`Err`] variant indicates -/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries -/// an associated value assigned by the lock method, and it can be acquired through the -/// [`into_inner`] method. The semantics of the associated value depends on the corresponding -/// lock method. -/// -/// [`into_inner`]: PoisonError::into_inner -#[stable(feature = "rust1", since = "1.0.0")] -pub type LockResult = Result>; - -/// A type alias for the result of a nonblocking locking method. -/// -/// For more information, see [`LockResult`]. A `TryLockResult` doesn't -/// necessarily hold the associated guard in the [`Err`] type as the lock might not -/// have been acquired for other reasons. -#[stable(feature = "rust1", since = "1.0.0")] -pub type TryLockResult = Result>; - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for PoisonError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PoisonError").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for PoisonError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "poisoned lock: another task failed inside".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for PoisonError {} - -impl PoisonError { - /// Creates a `PoisonError`. - /// - /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) - /// or [`RwLock::read`](crate::sync::RwLock::read). - /// - /// This method may panic if std was built with `panic="abort"`. - #[cfg(panic = "unwind")] - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn new(data: T) -> PoisonError { - PoisonError { data } - } - - /// Creates a `PoisonError`. - /// - /// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock) - /// or [`RwLock::read`](crate::sync::RwLock::read). - /// - /// This method may panic if std was built with `panic="abort"`. - #[cfg(not(panic = "unwind"))] - #[stable(feature = "sync_poison", since = "1.2.0")] - #[track_caller] - pub fn new(_data: T) -> PoisonError { - panic!("PoisonError created in a libstd built with panic=\"abort\"") - } - - /// Consumes this error indicating that a lock is poisoned, returning the - /// associated data. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(HashSet::new())); - /// - /// // poison the mutex - /// let c_mutex = Arc::clone(&mutex); - /// let _ = thread::spawn(move || { - /// let mut data = c_mutex.lock().unwrap(); - /// data.insert(10); - /// panic!(); - /// }).join(); - /// - /// let p_err = mutex.lock().unwrap_err(); - /// let data = p_err.into_inner(); - /// println!("recovered {} items", data.len()); - /// ``` - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn into_inner(self) -> T { - self.data - } - - /// Reaches into this error indicating that a lock is poisoned, returning a - /// reference to the associated data. - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn get_ref(&self) -> &T { - &self.data - } - - /// Reaches into this error indicating that a lock is poisoned, returning a - /// mutable reference to the associated data. - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn get_mut(&mut self) -> &mut T { - &mut self.data - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From> for TryLockError { - fn from(err: PoisonError) -> TryLockError { - TryLockError::Poisoned(err) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TryLockError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - #[cfg(panic = "unwind")] - TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), - #[cfg(not(panic = "unwind"))] - TryLockError::Poisoned(ref p) => match p._never {}, - TryLockError::WouldBlock => "WouldBlock".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TryLockError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - #[cfg(panic = "unwind")] - TryLockError::Poisoned(..) => "poisoned lock: another task failed inside", - #[cfg(not(panic = "unwind"))] - TryLockError::Poisoned(ref p) => match p._never {}, - TryLockError::WouldBlock => "try_lock failed because the operation would block", - } - .fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for TryLockError { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn Error> { - match *self { - #[cfg(panic = "unwind")] - TryLockError::Poisoned(ref p) => Some(p), - #[cfg(not(panic = "unwind"))] - TryLockError::Poisoned(ref p) => match p._never {}, - _ => None, - } - } -} - -pub(crate) fn map_result(result: LockResult, f: F) -> LockResult -where - F: FnOnce(T) -> U, -{ - match result { - Ok(t) => Ok(f(t)), - #[cfg(panic = "unwind")] - Err(PoisonError { data }) => Err(PoisonError::new(f(data))), - } -} diff --git a/crates/std/src/sync/poison/condvar.rs b/crates/std/src/sync/poison/condvar.rs deleted file mode 100644 index fa9e1ca..0000000 --- a/crates/std/src/sync/poison/condvar.rs +++ /dev/null @@ -1,510 +0,0 @@ -use crate::fmt; -use crate::sync::WaitTimeoutResult; -use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; -use crate::sys::sync as sys; -use crate::time::{Duration, Instant}; - -/// A Condition Variable -/// -/// Condition variables represent the ability to block a thread such that it -/// consumes no CPU time while waiting for an event to occur. Condition -/// variables are typically associated with a boolean predicate (a condition) -/// and a mutex. The predicate is always verified inside of the mutex before -/// determining that a thread must block. -/// -/// Functions in this module will block the current **thread** of execution. -/// Note that any attempt to use multiple mutexes on the same condition -/// variable may result in a runtime panic. -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, Mutex, Condvar}; -/// use std::thread; -/// -/// let pair = Arc::new((Mutex::new(false), Condvar::new())); -/// let pair2 = Arc::clone(&pair); -/// -/// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move || { -/// let (lock, cvar) = &*pair2; -/// let mut started = lock.lock().unwrap(); -/// *started = true; -/// // We notify the condvar that the value has changed. -/// cvar.notify_one(); -/// }); -/// -/// // Wait for the thread to start up. -/// let (lock, cvar) = &*pair; -/// let mut started = lock.lock().unwrap(); -/// while !*started { -/// started = cvar.wait(started).unwrap(); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Condvar { - inner: sys::Condvar, -} - -impl Condvar { - /// Creates a new condition variable which is ready to be waited on and - /// notified. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Condvar; - /// - /// let condvar = Condvar::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - #[must_use] - #[inline] - pub const fn new() -> Condvar { - Condvar { inner: sys::Condvar::new() } - } - - /// Blocks the current thread until this condition variable receives a - /// notification. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// Note that this function is susceptible to spurious wakeups. Condition - /// variables normally have a boolean predicate associated with them, and - /// the predicate must always be checked each time this function returns to - /// protect against spurious wakeups. - /// - /// # Errors - /// - /// This function will return an error if the mutex being waited on is - /// poisoned when this thread re-acquires the lock. For more information, - /// see information about [poisoning] on the [`Mutex`] type. - /// - /// # Panics - /// - /// This function may [`panic!`] if it is used with more than one mutex - /// over time. - /// - /// [`notify_one`]: Self::notify_one - /// [`notify_all`]: Self::notify_all - /// [poisoning]: super::Mutex#poisoning - /// [`Mutex`]: super::Mutex - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { - let poisoned = unsafe { - let lock = mutex::guard_lock(&guard); - self.inner.wait(lock); - mutex::guard_poison(&guard).get() - }; - if poisoned { Err(PoisonError::new(guard)) } else { Ok(guard) } - } - - /// Blocks the current thread until the provided condition becomes false. - /// - /// `condition` is checked immediately; if not met (returns `true`), this - /// will [`wait`] for the next notification then check again. This repeats - /// until `condition` returns `false`, in which case this function returns. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `guard`) and block the current thread. This means that any calls - /// to [`notify_one`] or [`notify_all`] which happen logically after the - /// mutex is unlocked are candidates to wake this thread up. When this - /// function call returns, the lock specified will have been re-acquired. - /// - /// # Errors - /// - /// This function will return an error if the mutex being waited on is - /// poisoned when this thread re-acquires the lock. For more information, - /// see information about [poisoning] on the [`Mutex`] type. - /// - /// [`wait`]: Self::wait - /// [`notify_one`]: Self::notify_one - /// [`notify_all`]: Self::notify_all - /// [poisoning]: super::Mutex#poisoning - /// [`Mutex`]: super::Mutex - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock().unwrap(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// // As long as the value inside the `Mutex` is `true`, we wait. - /// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap(); - /// ``` - #[stable(feature = "wait_until", since = "1.42.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - mut condition: F, - ) -> LockResult> - where - F: FnMut(&mut T) -> bool, - { - while condition(&mut *guard) { - guard = self.wait(guard)?; - } - Ok(guard) - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait`] - /// except that the thread will be blocked for roughly no longer - /// than `ms` milliseconds. This method should not be used for - /// precise timing due to anomalies such as preemption or platform - /// differences that might not cause the maximum amount of time - /// waited to be precisely `ms`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned boolean is `false` only if the timeout is known - /// to have elapsed. - /// - /// Like [`wait`], the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait`]: Self::wait - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// loop { - /// let result = cvar.wait_timeout_ms(started, 10).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - #[deprecated(since = "1.6.0", note = "replaced by `std::sync::Condvar::wait_timeout`")] - pub fn wait_timeout_ms<'a, T>( - &self, - guard: MutexGuard<'a, T>, - ms: u32, - ) -> LockResult<(MutexGuard<'a, T>, bool)> { - let res = self.wait_timeout(guard, Duration::from_millis(ms as u64)); - poison::map_result(res, |(a, b)| (a, !b.timed_out())) - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait`] except that - /// the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that might not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. This function is susceptible to spurious wakeups. - /// Condition variables normally have a boolean predicate associated with - /// them, and the predicate must always be checked each time this function - /// returns to protect against spurious wakeups. Furthermore, since the timeout - /// is given relative to the moment this function is called, it needs to be adjusted - /// when this function is called in a loop. The [`wait_timeout_while`] method - /// lets you wait with a timeout while a predicate is true, taking care of all these concerns. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed. - /// - /// Like [`wait`], the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait`]: Self::wait - /// [`wait_timeout_while`]: Self::wait_timeout_while - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // as long as the value inside the `Mutex` is `false`, we wait - /// loop { - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. - /// break - /// } - /// } - /// ``` - #[stable(feature = "wait_timeout", since = "1.5.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait_timeout<'a, T>( - &self, - guard: MutexGuard<'a, T>, - dur: Duration, - ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { - let (poisoned, result) = unsafe { - let lock = mutex::guard_lock(&guard); - let success = self.inner.wait_timeout(lock, dur); - (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) - }; - if poisoned { Err(PoisonError::new((guard, result))) } else { Ok((guard, result)) } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to [`wait_while`] except - /// that the thread will be blocked for roughly no longer than `dur`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that might not cause the maximum - /// amount of time waited to be precisely `dur`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned [`WaitTimeoutResult`] value indicates if the timeout is - /// known to have elapsed without the condition being met. - /// - /// Like [`wait_while`], the lock specified will be re-acquired when this - /// function returns, regardless of whether the timeout elapsed or not. - /// - /// [`wait_while`]: Self::wait_while - /// [`wait_timeout`]: Self::wait_timeout - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(true), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut pending = lock.lock().unwrap(); - /// *pending = false; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // wait for the thread to start up - /// let (lock, cvar) = &*pair; - /// let result = cvar.wait_timeout_while( - /// lock.lock().unwrap(), - /// Duration::from_millis(100), - /// |&mut pending| pending, - /// ).unwrap(); - /// if result.1.timed_out() { - /// // timed-out without the condition ever evaluating to false. - /// } - /// // access the locked mutex via result.0 - /// ``` - #[stable(feature = "wait_timeout_until", since = "1.42.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn wait_timeout_while<'a, T, F>( - &self, - mut guard: MutexGuard<'a, T>, - dur: Duration, - mut condition: F, - ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> - where - F: FnMut(&mut T) -> bool, - { - let start = Instant::now(); - loop { - if !condition(&mut *guard) { - return Ok((guard, WaitTimeoutResult(false))); - } - let timeout = match dur.checked_sub(start.elapsed()) { - Some(timeout) => timeout, - None => return Ok((guard, WaitTimeoutResult(true))), - }; - guard = self.wait_timeout(guard, timeout)?.0; - } - } - - /// Wakes up one blocked thread on this condvar. - /// - /// If there is a blocked thread on this condition variable, then it will - /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to - /// `notify_one` are not buffered in any way. - /// - /// To wake up all threads, see [`notify_all`]. - /// - /// [`wait`]: Self::wait - /// [`wait_timeout`]: Self::wait_timeout - /// [`notify_all`]: Self::notify_all - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn notify_one(&self) { - self.inner.notify_one() - } - - /// Wakes up all blocked threads on this condvar. - /// - /// This method will ensure that any current waiters on the condition - /// variable are awoken. Calls to `notify_all()` are not buffered in any - /// way. - /// - /// To wake up only one thread, see [`notify_one`]. - /// - /// [`notify_one`]: Self::notify_one - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex, Condvar}; - /// use std::thread; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// let mut started = lock.lock().unwrap(); - /// *started = true; - /// // We notify the condvar that the value has changed. - /// cvar.notify_all(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is `false`, we wait. - /// while !*started { - /// started = cvar.wait(started).unwrap(); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn notify_all(&self) { - self.inner.notify_all() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Condvar").finish_non_exhaustive() - } -} - -#[stable(feature = "condvar_default", since = "1.10.0")] -impl Default for Condvar { - /// Creates a `Condvar` which is ready to be waited on and notified. - fn default() -> Condvar { - Condvar::new() - } -} diff --git a/crates/std/src/sync/poison/mutex.rs b/crates/std/src/sync/poison/mutex.rs deleted file mode 100644 index 6eccd8a..0000000 --- a/crates/std/src/sync/poison/mutex.rs +++ /dev/null @@ -1,946 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop}; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::NonNull; -use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; -use crate::sys::sync as sys; - -/// A mutual exclusion primitive useful for protecting shared data -/// -/// This mutex will block threads waiting for the lock to become available. The -/// mutex can be created via a [`new`] constructor. Each mutex has a type parameter -/// which represents the data that it is protecting. The data can only be accessed -/// through the RAII guards returned from [`lock`] and [`try_lock`], which -/// guarantees that the data is only ever accessed when the mutex is locked. -/// -/// # Poisoning -/// -/// The mutexes in this module implement a strategy called "poisoning" where a -/// mutex becomes poisoned if it recognizes that the thread holding it has -/// panicked. -/// -/// Once a mutex is poisoned, all other threads are unable to access the data by -/// default as it is likely tainted (some invariant is not being upheld). For a -/// mutex, this means that the [`lock`] and [`try_lock`] methods return a -/// [`Result`] which indicates whether a mutex has been poisoned or not. Most -/// usage of a mutex will simply [`unwrap()`] these results, propagating panics -/// among threads to ensure that a possibly invalid invariant is not witnessed. -/// -/// Poisoning is only advisory: the [`PoisonError`] type has an [`into_inner`] -/// method which will return the guard that would have otherwise been returned -/// on a successful lock. This allows access to the data, despite the lock being -/// poisoned. -/// -/// In addition, the panic detection is not ideal, so even unpoisoned mutexes -/// need to be handled with care, since certain panics may have been skipped. -/// Here is a non-exhaustive list of situations where this might occur: -/// -/// - If a mutex is locked while a panic is underway, e.g. within a [`Drop`] -/// implementation or a [panic hook], panicking for the second time while the -/// lock is held will leave the mutex unpoisoned. Note that while double panic -/// usually aborts the program, [`catch_unwind`] can prevent this. -/// -/// - Locking and unlocking the mutex across different panic contexts, e.g. by -/// storing the guard to a [`Cell`] within [`Drop::drop`] and accessing it -/// outside, or vice versa, can affect poisoning status in an unexpected way. -/// -/// - Foreign exceptions do not currently trigger poisoning even in absence of -/// other panics. -/// -/// While this rarely happens in realistic code, `unsafe` code cannot rely on -/// poisoning for soundness, since the behavior of poisoning can depend on -/// outside context. Here's an example of **incorrect** use of poisoning: -/// -/// ```rust -/// use std::sync::Mutex; -/// -/// struct MutexBox { -/// data: Mutex<*mut T>, -/// } -/// -/// impl MutexBox { -/// pub fn new(value: T) -> Self { -/// Self { -/// data: Mutex::new(Box::into_raw(Box::new(value))), -/// } -/// } -/// -/// pub fn replace_with(&self, f: impl FnOnce(T) -> T) { -/// let ptr = self.data.lock().expect("poisoned"); -/// // While `f` is running, the data is moved out of `*ptr`. If `f` -/// // panics, `*ptr` keeps pointing at a dropped value. The intention -/// // is that this will poison the mutex, so the following calls to -/// // `replace_with` will panic without reading `*ptr`. But since -/// // poisoning is not guaranteed to occur if this is run from a panic -/// // hook, this can lead to use-after-free. -/// unsafe { -/// (*ptr).write(f((*ptr).read())); -/// } -/// } -/// } -/// ``` -/// -/// [`new`]: Self::new -/// [`lock`]: Self::lock -/// [`try_lock`]: Self::try_lock -/// [`unwrap()`]: Result::unwrap -/// [`PoisonError`]: super::PoisonError -/// [`into_inner`]: super::PoisonError::into_inner -/// [panic hook]: crate::panic::set_hook -/// [`catch_unwind`]: crate::panic::catch_unwind -/// [`Cell`]: crate::cell::Cell -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// const N: usize = 10; -/// -/// // Spawn a few threads to increment a shared variable (non-atomically), and -/// // let the main thread know once all increments are done. -/// // -/// // Here we're using an Arc to share memory among threads, and the data inside -/// // the Arc is protected with a mutex. -/// let data = Arc::new(Mutex::new(0)); -/// -/// let (tx, rx) = channel(); -/// for _ in 0..N { -/// let (data, tx) = (Arc::clone(&data), tx.clone()); -/// thread::spawn(move || { -/// // The shared state can only be accessed once the lock is held. -/// // Our non-atomic increment is safe because we're the only thread -/// // which can access the shared state when the lock is held. -/// // -/// // We unwrap() the return value to assert that we are not expecting -/// // threads to ever fail while holding the lock. -/// let mut data = data.lock().unwrap(); -/// *data += 1; -/// if *data == N { -/// tx.send(()).unwrap(); -/// } -/// // the lock is unlocked here when `data` goes out of scope. -/// }); -/// } -/// -/// rx.recv().unwrap(); -/// ``` -/// -/// To recover from a poisoned mutex: -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// -/// let lock = Arc::new(Mutex::new(0_u32)); -/// let lock2 = Arc::clone(&lock); -/// -/// let _ = thread::spawn(move || -> () { -/// // This thread will acquire the mutex first, unwrapping the result of -/// // `lock` because the lock has not been poisoned. -/// let _guard = lock2.lock().unwrap(); -/// -/// // This panic while holding the lock (`_guard` is in scope) will poison -/// // the mutex. -/// panic!(); -/// }).join(); -/// -/// // The lock is poisoned by this point, but the returned result can be -/// // pattern matched on to return the underlying guard on both branches. -/// let mut guard = match lock.lock() { -/// Ok(guard) => guard, -/// Err(poisoned) => poisoned.into_inner(), -/// }; -/// -/// *guard += 1; -/// ``` -/// -/// To unlock a mutex guard sooner than the end of the enclosing scope, -/// either create an inner scope or drop the guard manually. -/// -/// ``` -/// use std::sync::{Arc, Mutex}; -/// use std::thread; -/// -/// const N: usize = 3; -/// -/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); -/// let res_mutex = Arc::new(Mutex::new(0)); -/// -/// let mut threads = Vec::with_capacity(N); -/// (0..N).for_each(|_| { -/// let data_mutex_clone = Arc::clone(&data_mutex); -/// let res_mutex_clone = Arc::clone(&res_mutex); -/// -/// threads.push(thread::spawn(move || { -/// // Here we use a block to limit the lifetime of the lock guard. -/// let result = { -/// let mut data = data_mutex_clone.lock().unwrap(); -/// // This is the result of some important and long-ish work. -/// let result = data.iter().fold(0, |acc, x| acc + x * 2); -/// data.push(result); -/// result -/// // The mutex guard gets dropped here, together with any other values -/// // created in the critical section. -/// }; -/// // The guard created here is a temporary dropped at the end of the statement, i.e. -/// // the lock would not remain being held even if the thread did some additional work. -/// *res_mutex_clone.lock().unwrap() += result; -/// })); -/// }); -/// -/// let mut data = data_mutex.lock().unwrap(); -/// // This is the result of some important and long-ish work. -/// let result = data.iter().fold(0, |acc, x| acc + x * 2); -/// data.push(result); -/// // We drop the `data` explicitly because it's not necessary anymore and the -/// // thread still has work to do. This allows other threads to start working on -/// // the data immediately, without waiting for the rest of the unrelated work -/// // to be done here. -/// // -/// // It's even more important here than in the threads because we `.join` the -/// // threads after that. If we had not dropped the mutex guard, a thread could -/// // be waiting forever for it, causing a deadlock. -/// // As in the threads, a block could have been used instead of calling the -/// // `drop` function. -/// drop(data); -/// // Here the mutex guard is not assigned to a variable and so, even if the -/// // scope does not end after this line, the mutex is still released: there is -/// // no deadlock. -/// *res_mutex.lock().unwrap() += result; -/// -/// threads.into_iter().for_each(|thread| { -/// thread -/// .join() -/// .expect("The thread creating or execution failed !") -/// }); -/// -/// assert_eq!(*res_mutex.lock().unwrap(), 800); -/// ``` -/// -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")] -pub struct Mutex { - inner: sys::Mutex, - poison: poison::Flag, - data: UnsafeCell, -} - -/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire -/// the owned `T` from the `Mutex` via [`into_inner`]. -/// -/// [`into_inner`]: Mutex::into_inner -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Mutex {} - -/// `T` must be `Send` for [`Mutex`] to be `Sync`. -/// This ensures that the protected data can be accessed safely from multiple threads -/// without causing data races or other unsafe behavior. -/// -/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential -/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in -/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, -/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap -/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would -/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable -/// to potential data races. -/// -/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available -/// to one thread at a time if `T` is not `Sync`. -/// -/// [`Rc`]: crate::rc::Rc -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Mutex {} - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] and [`DerefMut`] implementations. -/// -/// This structure is created by the [`lock`] and [`try_lock`] methods on -/// [`Mutex`]. -/// -/// [`lock`]: Mutex::lock -/// [`try_lock`]: Mutex::try_lock -#[must_use = "if unused the Mutex will immediately unlock"] -#[must_not_suspend = "holding a MutexGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[stable(feature = "rust1", since = "1.0.0")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "MutexGuard")] -pub struct MutexGuard<'a, T: ?Sized + 'a> { - lock: &'a Mutex, - poison: poison::Guard, -} - -/// A [`MutexGuard`] is not `Send` to maximize platform portability. -/// -/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to -/// release mutex locks on the same thread they were acquired. -/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from -/// another thread. -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for MutexGuard<'_, T> {} - -/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` -/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). -#[stable(feature = "mutexguard", since = "1.19.0")] -unsafe impl Sync for MutexGuard<'_, T> {} - -/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a -/// subfield of the protected data. When this structure is dropped (falls out -/// of scope), the lock will be unlocked. -/// -/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the -/// former cannot be used with [`Condvar`], since that -/// could introduce soundness issues if the locked object is modified by another -/// thread while the `Mutex` is unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] and [`DerefMut`] implementations. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods on -/// [`MutexGuard`]. -/// -/// [`map`]: MutexGuard::map -/// [`filter_map`]: MutexGuard::filter_map -/// [`Condvar`]: crate::sync::Condvar -#[must_use = "if unused the Mutex will immediately unlock"] -#[must_not_suspend = "holding a MappedMutexGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -#[clippy::has_significant_drop] -pub struct MappedMutexGuard<'a, T: ?Sized + 'a> { - // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a - // `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. - // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field - // below for the correct variance over `T` (invariance). - data: NonNull, - inner: &'a sys::Mutex, - poison_flag: &'a poison::Flag, - poison: poison::Guard, - _variance: PhantomData<&'a mut T>, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl !Send for MappedMutexGuard<'_, T> {} -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -unsafe impl Sync for MappedMutexGuard<'_, T> {} - -impl Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - #[inline] - pub const fn new(t: T) -> Mutex { - Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } - } - - /// Returns the contained value by cloning it. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned().unwrap(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn get_cloned(&self) -> Result> - where - T: Clone, - { - match self.lock() { - Ok(guard) => Ok((*guard).clone()), - Err(_) => Err(PoisonError::new(())), - } - } - - /// Sets the contained value. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the provided `value` instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned().unwrap(), 7); - /// mutex.set(11).unwrap(); - /// assert_eq!(mutex.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - #[rustc_should_not_be_called_on_const_items] - pub fn set(&self, value: T) -> Result<(), PoisonError> { - if mem::needs_drop::() { - // If the contained value has non-trivial destructor, we - // call that destructor after the lock being released. - self.replace(value).map(drop) - } else { - match self.lock() { - Ok(mut guard) => { - *guard = value; - - Ok(()) - } - Err(_) => Err(PoisonError::new(value)), - } - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the provided `value` instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.replace(11).unwrap(), 7); - /// assert_eq!(mutex.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - #[rustc_should_not_be_called_on_const_items] - pub fn replace(&self, value: T) -> LockResult { - match self.lock() { - Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), - Err(_) => Err(PoisonError::new(value)), - } - } -} - -impl Mutex { - /// Acquires a mutex, blocking the current thread until it is able to do so. - /// - /// This function will block the local thread until it is available to acquire - /// the mutex. Upon returning, the thread is the only thread with the lock - /// held. An RAII guard is returned to allow scoped unlock of the lock. When - /// the guard goes out of scope, the mutex will be unlocked. - /// - /// The exact behavior on locking a mutex in the thread which already holds - /// the lock is left unspecified. However, this function will not return on - /// the second call (it might panic or deadlock, for example). - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error once the mutex is acquired. The acquired - /// mutex guard will be contained in the returned error. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by - /// the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// thread::spawn(move || { - /// *c_mutex.lock().unwrap() = 10; - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn lock(&self) -> LockResult> { - unsafe { - self.inner.lock(); - MutexGuard::new(self) - } - } - - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then [`Err`] is returned. - /// Otherwise, an RAII guard is returned. The lock will be unlocked when the - /// guard is dropped. - /// - /// This function does not block. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return the [`Poisoned`] error if the mutex would - /// otherwise be acquired. An acquired lock guard will be contained - /// in the returned error. - /// - /// If the mutex could not be acquired because it is already locked, then - /// this call will return the [`WouldBlock`] error. - /// - /// [`Poisoned`]: TryLockError::Poisoned - /// [`WouldBlock`]: TryLockError::WouldBlock - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// thread::spawn(move || { - /// let mut lock = c_mutex.try_lock(); - /// if let Ok(ref mut mutex) = lock { - /// **mutex = 10; - /// } else { - /// println!("try_lock failed"); - /// } - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn try_lock(&self) -> TryLockResult> { - unsafe { - if self.inner.try_lock() { - Ok(MutexGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Determines whether the mutex is poisoned. - /// - /// If another thread is active, the mutex can still become poisoned at any - /// time. You should not trust a `false` value for program correctness - /// without additional synchronization. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_mutex.lock().unwrap(); - /// panic!(); // the mutex gets poisoned - /// }).join(); - /// assert_eq!(mutex.is_poisoned(), true); - /// ``` - #[inline] - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn is_poisoned(&self) -> bool { - self.poison.get() - } - - /// Clear the poisoned state from a mutex. - /// - /// If the mutex is poisoned, it will remain poisoned until this function is called. This - /// allows recovering from a poisoned state and marking that it has recovered. For example, if - /// the value is overwritten by a known-good value, then the mutex can be marked as - /// un-poisoned. Or possibly, the value could be inspected to determine if it is in a - /// consistent state, and if so the poison is removed. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Mutex}; - /// use std::thread; - /// - /// let mutex = Arc::new(Mutex::new(0)); - /// let c_mutex = Arc::clone(&mutex); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_mutex.lock().unwrap(); - /// panic!(); // the mutex gets poisoned - /// }).join(); - /// - /// assert_eq!(mutex.is_poisoned(), true); - /// let x = mutex.lock().unwrap_or_else(|mut e| { - /// **e.get_mut() = 1; - /// mutex.clear_poison(); - /// e.into_inner() - /// }); - /// assert_eq!(mutex.is_poisoned(), false); - /// assert_eq!(*x, 1); - /// ``` - #[inline] - #[stable(feature = "mutex_unpoison", since = "1.77.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn clear_poison(&self) { - self.poison.clear(); - } - - /// Consumes this mutex, returning the underlying data. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the underlying data - /// instead. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mutex = Mutex::new(0); - /// assert_eq!(mutex.into_inner().unwrap(), 0); - /// ``` - #[stable(feature = "mutex_into_inner", since = "1.6.0")] - pub fn into_inner(self) -> LockResult - where - T: Sized, - { - let data = self.data.into_inner(); - poison::map_result(self.poison.borrow(), |()| data) - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `Mutex` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no new locks can be acquired - /// while this reference exists. Note that this method does not clear any previous abandoned locks - /// (e.g., via [`forget()`] on a [`MutexGuard`]). - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing a mutable reference to the - /// underlying data instead. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(0); - /// *mutex.get_mut().unwrap() = 10; - /// assert_eq!(*mutex.lock().unwrap(), 10); - /// ``` - /// - /// [`forget()`]: mem::forget - #[stable(feature = "mutex_get_mut", since = "1.6.0")] - pub fn get_mut(&mut self) -> LockResult<&mut T> { - let data = self.data.get_mut(); - poison::map_result(self.poison.borrow(), |()| data) - } - - /// Returns a raw pointer to the underlying data. - /// - /// The returned pointer is always non-null and properly aligned, but it is - /// the user's responsibility to ensure that any reads and writes through it - /// are properly synchronized to avoid data races, and that it is not read - /// or written through after the mutex is dropped. - #[unstable(feature = "mutex_data_ptr", issue = "140368")] - pub const fn data_ptr(&self) -> *mut T { - self.data.get() - } -} - -#[stable(feature = "mutex_from", since = "1.24.0")] -impl From for Mutex { - /// Creates a new mutex in an unlocked state ready for use. - /// This is equivalent to [`Mutex::new`]. - fn from(t: T) -> Self { - Mutex::new(t) - } -} - -#[stable(feature = "mutex_default", since = "1.10.0")] -impl Default for Mutex { - /// Creates a `Mutex`, with the `Default` value for T. - fn default() -> Mutex { - Mutex::new(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Mutex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("Mutex"); - match self.try_lock() { - Ok(guard) => { - d.field("data", &&*guard); - } - Err(TryLockError::Poisoned(err)) => { - d.field("data", &&**err.get_ref()); - } - Err(TryLockError::WouldBlock) => { - d.field("data", &""); - } - } - d.field("poisoned", &self.poison.get()); - d.finish_non_exhaustive() - } -} - -impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { - unsafe fn new(lock: &'mutex Mutex) -> LockResult> { - poison::map_result(lock.poison.guard(), |guard| MutexGuard { lock, poison: guard }) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for MutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for MutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for MutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.lock.poison.done(&self.poison); - self.lock.inner.unlock(); - } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -/// For use in [`nonpoison::condvar`](super::condvar). -pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { - &guard.lock.inner -} - -/// For use in [`nonpoison::condvar`](super::condvar). -pub(super) fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { - &guard.lock.poison -} - -impl<'a, T: ?Sized> MutexGuard<'a, T> { - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MutexGuard::map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); - let orig = ManuallyDrop::new(orig); - MappedMutexGuard { - data, - inner: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), - _variance: PhantomData, - } - } - - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { &mut *orig.lock.data.get() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedMutexGuard { - data, - inner: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Deref for MappedMutexGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl DerefMut for MappedMutexGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { self.data.as_mut() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Drop for MappedMutexGuard<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.poison_flag.done(&self.poison); - self.inner.unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Debug for MappedMutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Display for MappedMutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedMutexGuard::map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_mut() })); - let orig = ManuallyDrop::new(orig); - MappedMutexGuard { - data, - inner: orig.inner, - poison_flag: orig.poison_flag, - poison: orig.poison.clone(), - _variance: PhantomData, - } - } - - /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `Mutex` is already locked, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the - /// same name on the contents of the `MutexGuard` used through `Deref`. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map(mut orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_mut() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedMutexGuard { - data, - inner: orig.inner, - poison_flag: orig.poison_flag, - poison: orig.poison.clone(), - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} diff --git a/crates/std/src/sync/poison/rwlock.rs b/crates/std/src/sync/poison/rwlock.rs deleted file mode 100644 index c01ee17..0000000 --- a/crates/std/src/sync/poison/rwlock.rs +++ /dev/null @@ -1,1274 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop, forget}; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::NonNull; -use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; -use crate::sys::sync as sys; - -/// A reader-writer lock -/// -/// This type of lock allows a number of readers or at most one writer at any -/// point in time. The write portion of this lock typically allows modification -/// of the underlying data (exclusive access) and the read portion of this lock -/// typically allows for read-only access (shared access). -/// -/// In comparison, a [`Mutex`] does not distinguish between readers or writers -/// that acquire the lock, therefore blocking any threads waiting for the lock to -/// become available. An `RwLock` will allow any number of readers to acquire the -/// lock as long as a writer is not holding the lock. -/// -/// The priority policy of the lock is dependent on the underlying operating -/// system's implementation, and this type does not guarantee that any -/// particular policy will be used. In particular, a writer which is waiting to -/// acquire the lock in `write` might or might not block concurrent calls to -/// `read`, e.g.: -/// -///
Potential deadlock example -/// -/// ```text -/// // Thread 1 | // Thread 2 -/// let _rg1 = lock.read(); | -/// | // will block -/// | let _wg = lock.write(); -/// // may deadlock | -/// let _rg2 = lock.read(); | -/// ``` -/// -///
-/// -/// The type parameter `T` represents the data that this lock protects. It is -/// required that `T` satisfies [`Send`] to be shared across threads and -/// [`Sync`] to allow concurrent access through readers. The RAII guards -/// returned from the locking methods implement [`Deref`] (and [`DerefMut`] -/// for the `write` methods) to allow access to the content of the lock. -/// -/// # Poisoning -/// -/// An `RwLock`, like [`Mutex`], will [usually] become poisoned on a panic. Note, -/// however, that an `RwLock` may only be poisoned if a panic occurs while it is -/// locked exclusively (write mode). If a panic occurs in any reader, then the -/// lock will not be poisoned. -/// -/// [usually]: super::Mutex#poisoning -/// -/// # Examples -/// -/// ``` -/// use std::sync::{Arc, RwLock}; -/// use std::thread; -/// use std::time::Duration; -/// -/// let data = Arc::new(RwLock::new(5)); -/// -/// // Multiple readers can access in parallel. -/// for i in 0..3 { -/// let lock_clone = Arc::clone(&data); -/// -/// thread::spawn(move || { -/// let value = lock_clone.read().unwrap(); -/// -/// println!("Reader {}: Read value {}, now holding lock...", i, *value); -/// -/// // Simulating a long read operation -/// thread::sleep(Duration::from_secs(1)); -/// -/// println!("Reader {}: Dropping lock.", i); -/// // Read lock unlocked when going out of scope. -/// }); -/// } -/// -/// thread::sleep(Duration::from_millis(100)); // Wait for readers to start -/// -/// // While all readers can proceed, a call to .write() has to wait for -// // current active reader locks. -/// let mut writable_data = data.write().unwrap(); -/// println!("Writer proceeds..."); -/// *writable_data += 1; -/// ``` -/// -/// [`Mutex`]: super::Mutex -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "RwLock")] -pub struct RwLock { - /// The inner [`sys::RwLock`] that synchronizes thread access to the protected data. - inner: sys::RwLock, - /// A flag denoting if this `RwLock` has been poisoned. - poison: poison::Flag, - /// The lock-protected data. - data: UnsafeCell, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for RwLock {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for RwLock {} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Guards -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// RAII structure used to release the shared read access of a lock when -/// dropped. -/// -/// This structure is created by the [`read`] and [`try_read`] methods on -/// [`RwLock`]. -/// -/// [`read`]: RwLock::read -/// [`try_read`]: RwLock::try_read -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a RwLockReadGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[stable(feature = "rust1", since = "1.0.0")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockReadGuard")] -pub struct RwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `RwLockReadGuard` instance only holds - /// immutability until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also - /// covariant over `T`, just like we would have with `&T`. - data: NonNull, - /// A reference to the internal [`sys::RwLock`] that we have read-locked. - inner_lock: &'rwlock sys::RwLock, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for RwLockReadGuard<'_, T> {} - -#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] -unsafe impl Sync for RwLockReadGuard<'_, T> {} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped. -/// -/// This structure is created by the [`write`] and [`try_write`] methods -/// on [`RwLock`]. -/// -/// [`write`]: RwLock::write -/// [`try_write`]: RwLock::try_write -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a RwLockWriteGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Future's to not implement `Send`"] -#[stable(feature = "rust1", since = "1.0.0")] -#[clippy::has_significant_drop] -#[cfg_attr(not(test), rustc_diagnostic_item = "RwLockWriteGuard")] -pub struct RwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A reference to the [`RwLock`] that we have write-locked. - lock: &'rwlock RwLock, - /// The poison guard. See the [`poison`] module for more information. - poison: poison::Guard, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Send for RwLockWriteGuard<'_, T> {} - -#[stable(feature = "rwlock_guard_sync", since = "1.23.0")] -unsafe impl Sync for RwLockWriteGuard<'_, T> {} - -/// RAII structure used to release the shared read access of a lock when -/// dropped, which can point to a subfield of the protected data. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods -/// on [`RwLockReadGuard`]. -/// -/// [`map`]: RwLockReadGuard::map -/// [`filter_map`]: RwLockReadGuard::filter_map -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a MappedRwLockReadGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Futures to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -#[clippy::has_significant_drop] -pub struct MappedRwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockReadGuard` instance only - /// holds immutability until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also - /// covariant over `T`, just like we would have with `&T`. - data: NonNull, - /// A reference to the internal [`sys::RwLock`] that we have read-locked. - inner_lock: &'rwlock sys::RwLock, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl !Send for MappedRwLockReadGuard<'_, T> {} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -unsafe impl Sync for MappedRwLockReadGuard<'_, T> {} - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped, which can point to a subfield of the protected data. -/// -/// This structure is created by the [`map`] and [`filter_map`] methods -/// on [`RwLockWriteGuard`]. -/// -/// [`map`]: RwLockWriteGuard::map -/// [`filter_map`]: RwLockWriteGuard::filter_map -#[must_use = "if unused the RwLock will immediately unlock"] -#[must_not_suspend = "holding a MappedRwLockWriteGuard across suspend \ - points can cause deadlocks, delays, \ - and cause Future's to not implement `Send`"] -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -#[clippy::has_significant_drop] -pub struct MappedRwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { - /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of - /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockWriteGuard` instance only - /// holds uniquneness until it drops, not for its whole scope. - /// `NonNull` is preferable over `*const T` to allow for niche optimizations. - data: NonNull, - /// `NonNull` is covariant over `T`, so we add a `PhantomData<&'rwlock mut T>` field here to - /// enforce the correct invariance over `T`. - _variance: PhantomData<&'rwlock mut T>, - /// A reference to the internal [`sys::RwLock`] that we have write-locked. - inner_lock: &'rwlock sys::RwLock, - /// A reference to the original `RwLock`'s poison state. - poison_flag: &'rwlock poison::Flag, - /// The poison guard. See the [`poison`] module for more information. - poison_guard: poison::Guard, -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl !Send for MappedRwLockWriteGuard<'_, T> {} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -unsafe impl Sync for MappedRwLockWriteGuard<'_, T> {} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Implementations -//////////////////////////////////////////////////////////////////////////////////////////////////// - -impl RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(5); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - #[inline] - pub const fn new(t: T) -> RwLock { - RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } - } - - /// Returns the contained value by cloning it. - /// - /// # Errors - /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned().unwrap(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn get_cloned(&self) -> Result> - where - T: Clone, - { - match self.read() { - Ok(guard) => Ok((*guard).clone()), - Err(_) => Err(PoisonError::new(())), - } - } - - /// Sets the contained value. - /// - /// # Errors - /// - /// This function will return an error containing the provided `value` if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned().unwrap(), 7); - /// lock.set(11).unwrap(); - /// assert_eq!(lock.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - #[rustc_should_not_be_called_on_const_items] - pub fn set(&self, value: T) -> Result<(), PoisonError> { - if mem::needs_drop::() { - // If the contained value has non-trivial destructor, we - // call that destructor after the lock being released. - self.replace(value).map(drop) - } else { - match self.write() { - Ok(mut guard) => { - *guard = value; - - Ok(()) - } - Err(_) => Err(PoisonError::new(value)), - } - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Errors - /// - /// This function will return an error containing the provided `value` if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.replace(11).unwrap(), 7); - /// assert_eq!(lock.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - #[rustc_should_not_be_called_on_const_items] - pub fn replace(&self, value: T) -> LockResult { - match self.write() { - Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), - Err(_) => Err(PoisonError::new(value)), - } - } -} - -impl RwLock { - /// Locks this `RwLock` with shared read access, blocking the current thread - /// until it can be acquired. - /// - /// The calling thread will be blocked until there are no more writers which - /// hold the lock. There may be other readers currently inside the lock when - /// this method returns. This method does not provide any guarantees with - /// respect to the ordering of whether contentious readers or writers will - /// acquire the lock first. - /// - /// Returns an RAII guard which will release this thread's shared access - /// once it is dropped. - /// - /// # Errors - /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. The failure will occur immediately after the lock has been - /// acquired. The acquired lock guard will be contained in the returned - /// error. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread - /// in read or write mode. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, RwLock}; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(1)); - /// let c_lock = Arc::clone(&lock); - /// - /// let n = lock.read().unwrap(); - /// assert_eq!(*n, 1); - /// - /// thread::spawn(move || { - /// let r = c_lock.read(); - /// assert!(r.is_ok()); - /// }).join().unwrap(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn read(&self) -> LockResult> { - unsafe { - self.inner.read(); - RwLockReadGuard::new(self) - } - } - - /// Attempts to acquire this `RwLock` with shared read access. - /// - /// If the access could not be granted at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the shared access - /// when it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return the [`Poisoned`] error if the `RwLock` is - /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding - /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. An acquired lock guard will be contained - /// in the returned error. - /// - /// This function will return the [`WouldBlock`] error if the `RwLock` could - /// not be acquired because it was already locked exclusively. - /// - /// [`Poisoned`]: TryLockError::Poisoned - /// [`WouldBlock`]: TryLockError::WouldBlock - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// match lock.try_read() { - /// Ok(n) => assert_eq!(*n, 1), - /// Err(_) => unreachable!(), - /// }; - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn try_read(&self) -> TryLockResult> { - unsafe { - if self.inner.try_read() { - Ok(RwLockReadGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Locks this `RwLock` with exclusive write access, blocking the current - /// thread until it can be acquired. - /// - /// This function will not return while other writers or other readers - /// currently have access to the lock. - /// - /// Returns an RAII guard which will drop the write access of this `RwLock` - /// when dropped. - /// - /// # Errors - /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will be returned when the lock is acquired. The acquired - /// lock guard will be contained in the returned error. - /// - /// # Panics - /// - /// This function might panic when called if the lock is already held by the current thread - /// in read or write mode. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let mut n = lock.write().unwrap(); - /// *n = 2; - /// - /// assert!(lock.try_read().is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn write(&self) -> LockResult> { - unsafe { - self.inner.write(); - RwLockWriteGuard::new(self) - } - } - - /// Attempts to lock this `RwLock` with exclusive write access. - /// - /// If the lock could not be acquired at this time, then `Err` is returned. - /// Otherwise, an RAII guard is returned which will release the lock when - /// it is dropped. - /// - /// This function does not block. - /// - /// This function does not provide any guarantees with respect to the ordering - /// of whether contentious readers or writers will acquire the lock first. - /// - /// # Errors - /// - /// This function will return the [`Poisoned`] error if the `RwLock` is - /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding - /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. An acquired lock guard will be contained - /// in the returned error. - /// - /// This function will return the [`WouldBlock`] error if the `RwLock` could - /// not be acquired because it was already locked. - /// - /// [`Poisoned`]: TryLockError::Poisoned - /// [`WouldBlock`]: TryLockError::WouldBlock - /// - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(1); - /// - /// let n = lock.read().unwrap(); - /// assert_eq!(*n, 1); - /// - /// assert!(lock.try_write().is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_should_not_be_called_on_const_items] - pub fn try_write(&self) -> TryLockResult> { - unsafe { - if self.inner.try_write() { - Ok(RwLockWriteGuard::new(self)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Determines whether the lock is poisoned. - /// - /// If another thread is active, the lock can still become poisoned at any - /// time. You should not trust a `false` value for program correctness - /// without additional synchronization. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, RwLock}; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(0)); - /// let c_lock = Arc::clone(&lock); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_lock.write().unwrap(); - /// panic!(); // the lock gets poisoned - /// }).join(); - /// assert_eq!(lock.is_poisoned(), true); - /// ``` - #[inline] - #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn is_poisoned(&self) -> bool { - self.poison.get() - } - - /// Clear the poisoned state from a lock. - /// - /// If the lock is poisoned, it will remain poisoned until this function is called. This allows - /// recovering from a poisoned state and marking that it has recovered. For example, if the - /// value is overwritten by a known-good value, then the lock can be marked as un-poisoned. Or - /// possibly, the value could be inspected to determine if it is in a consistent state, and if - /// so the poison is removed. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, RwLock}; - /// use std::thread; - /// - /// let lock = Arc::new(RwLock::new(0)); - /// let c_lock = Arc::clone(&lock); - /// - /// let _ = thread::spawn(move || { - /// let _lock = c_lock.write().unwrap(); - /// panic!(); // the lock gets poisoned - /// }).join(); - /// - /// assert_eq!(lock.is_poisoned(), true); - /// let guard = lock.write().unwrap_or_else(|mut e| { - /// **e.get_mut() = 1; - /// lock.clear_poison(); - /// e.into_inner() - /// }); - /// assert_eq!(lock.is_poisoned(), false); - /// assert_eq!(*guard, 1); - /// ``` - #[inline] - #[stable(feature = "mutex_unpoison", since = "1.77.0")] - pub fn clear_poison(&self) { - self.poison.clear(); - } - - /// Consumes this `RwLock`, returning the underlying data. - /// - /// # Errors - /// - /// This function will return an error containing the underlying data if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. An error will only be returned - /// if the lock would have otherwise been acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let lock = RwLock::new(String::new()); - /// { - /// let mut s = lock.write().unwrap(); - /// *s = "modified".to_owned(); - /// } - /// assert_eq!(lock.into_inner().unwrap(), "modified"); - /// ``` - #[stable(feature = "rwlock_into_inner", since = "1.6.0")] - pub fn into_inner(self) -> LockResult - where - T: Sized, - { - let data = self.data.into_inner(); - poison::map_result(self.poison.borrow(), |()| data) - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `RwLock` mutably, no actual locking needs to - /// take place -- the mutable borrow statically guarantees no new locks can be acquired - /// while this reference exists. Note that this method does not clear any previously abandoned - /// locks (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). - /// - /// # Errors - /// - /// This function will return an error containing a mutable reference to - /// the underlying data if the `RwLock` is poisoned. An `RwLock` is - /// poisoned whenever a writer panics while holding an exclusive lock. - /// An error will only be returned if the lock would have otherwise been - /// acquired. - /// - /// # Examples - /// - /// ``` - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(0); - /// *lock.get_mut().unwrap() = 10; - /// assert_eq!(*lock.read().unwrap(), 10); - /// ``` - #[stable(feature = "rwlock_get_mut", since = "1.6.0")] - pub fn get_mut(&mut self) -> LockResult<&mut T> { - let data = self.data.get_mut(); - poison::map_result(self.poison.borrow(), |()| data) - } - - /// Returns a raw pointer to the underlying data. - /// - /// The returned pointer is always non-null and properly aligned, but it is - /// the user's responsibility to ensure that any reads and writes through it - /// are properly synchronized to avoid data races, and that it is not read - /// or written through after the lock is dropped. - #[unstable(feature = "rwlock_data_ptr", issue = "140368")] - pub const fn data_ptr(&self) -> *mut T { - self.data.get() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for RwLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("RwLock"); - match self.try_read() { - Ok(guard) => { - d.field("data", &&*guard); - } - Err(TryLockError::Poisoned(err)) => { - d.field("data", &&**err.get_ref()); - } - Err(TryLockError::WouldBlock) => { - d.field("data", &format_args!("")); - } - } - d.field("poisoned", &self.poison.get()); - d.finish_non_exhaustive() - } -} - -#[stable(feature = "rw_lock_default", since = "1.10.0")] -impl Default for RwLock { - /// Creates a new `RwLock`, with the `Default` value for T. - fn default() -> RwLock { - RwLock::new(Default::default()) - } -} - -#[stable(feature = "rw_lock_from", since = "1.24.0")] -impl From for RwLock { - /// Creates a new instance of an `RwLock` which is unlocked. - /// This is equivalent to [`RwLock::new`]. - fn from(t: T) -> Self { - RwLock::new(t) - } -} - -impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { - /// Creates a new instance of `RwLockReadGuard` from a `RwLock`. - /// - /// # Safety - /// - /// This function is safe if and only if the same thread has successfully and safely called - /// `lock.inner.read()`, `lock.inner.try_read()`, or `lock.inner.downgrade()` before - /// instantiating this object. - unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard { - data: unsafe { NonNull::new_unchecked(lock.data.get()) }, - inner_lock: &lock.inner, - }) - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockReadGuard::map(...)`. A method would interfere with methods of - /// the same name on the contents of the `RwLockReadGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be - /// poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> - where - F: FnOnce(&T) -> &U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_ref() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockReadGuard::filter_map(...)`. A method would interfere with methods - /// of the same name on the contents of the `RwLockReadGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be - /// poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&T) -> Option<&U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_ref() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - /// Creates a new instance of `RwLockWriteGuard` from a `RwLock`. - /// - /// # Safety - /// - /// This function is safe if and only if the same thread has successfully and safely called - /// `lock.inner.write()`, `lock.inner.try_write()`, or `lock.inner.try_upgrade` before - /// instantiating this object. - unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { - poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard { lock, poison: guard }) - } - - /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. - /// - /// Since we have the `RwLockWriteGuard`, the [`RwLock`] must already be locked for writing, so - /// this method cannot fail. - /// - /// After downgrading, other readers will be allowed to read the protected data. - /// - /// # Examples - /// - /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. - /// - /// ``` - /// use std::sync::{RwLock, RwLockWriteGuard}; - /// - /// let rw = RwLock::new(0); - /// - /// let mut write_guard = rw.write().unwrap(); - /// *write_guard = 42; - /// - /// let read_guard = RwLockWriteGuard::downgrade(write_guard); - /// assert_eq!(42, *read_guard); - /// ``` - /// - /// `downgrade` will _atomically_ change the state of the [`RwLock`] from exclusive mode into - /// shared mode. This means that it is impossible for another writing thread to get in between a - /// thread calling `downgrade` and any reads it performs after downgrading. - /// - /// ``` - /// use std::sync::{Arc, RwLock, RwLockWriteGuard}; - /// - /// let rw = Arc::new(RwLock::new(1)); - /// - /// // Put the lock in write mode. - /// let mut main_write_guard = rw.write().unwrap(); - /// - /// let rw_clone = rw.clone(); - /// let evil_handle = std::thread::spawn(move || { - /// // This will not return until the main thread drops the `main_read_guard`. - /// let mut evil_guard = rw_clone.write().unwrap(); - /// - /// assert_eq!(*evil_guard, 2); - /// *evil_guard = 3; - /// }); - /// - /// *main_write_guard = 2; - /// - /// // Atomically downgrade the write guard into a read guard. - /// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); - /// - /// // Since `downgrade` is atomic, the writer thread cannot have changed the protected data. - /// assert_eq!(*main_read_guard, 2, "`downgrade` was not atomic"); - /// # - /// # drop(main_read_guard); - /// # evil_handle.join().unwrap(); - /// # - /// # let final_check = rw.read().unwrap(); - /// # assert_eq!(*final_check, 3); - /// ``` - #[stable(feature = "rwlock_downgrade", since = "1.92.0")] - pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { - let lock = s.lock; - - // We don't want to call the destructor since that calls `write_unlock`. - forget(s); - - // SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write - // mode, satisfying the `downgrade` contract. - unsafe { lock.inner.downgrade() }; - - // SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract. - unsafe { RwLockReadGuard::new(lock).unwrap_or_else(PoisonError::into_inner) } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, e.g. - /// an enum variant. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::map(...)`. A method would interfere with methods of - /// the same name on the contents of the `RwLockWriteGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockWriteGuard { - data, - inner_lock: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison_guard: orig.poison.clone(), - _variance: PhantomData, - } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. The - /// original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `RwLockWriteGuard::filter_map(...)`. A method would interfere with methods - /// of the same name on the contents of the `RwLockWriteGuard` used through - /// `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { &mut *orig.lock.data.get() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockWriteGuard { - data, - inner_lock: &orig.lock.inner, - poison_flag: &orig.lock.poison, - poison_guard: orig.poison.clone(), - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, - /// e.g. an enum variant. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be - /// poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> - where - F: FnOnce(&T) -> &U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_ref() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } - } - - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. - /// The original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be - /// poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map(orig: Self, f: F) -> Result, Self> - where - F: FnOnce(&T) -> Option<&U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_ref() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) - } - None => Err(orig), - } - } -} - -impl<'rwlock, T: ?Sized> MappedRwLockWriteGuard<'rwlock, T> { - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, - /// e.g. an enum variant. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockWriteGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> - where - F: FnOnce(&mut T) -> &mut U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_mut() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockWriteGuard { - data, - inner_lock: orig.inner_lock, - poison_flag: orig.poison_flag, - poison_guard: orig.poison_guard.clone(), - _variance: PhantomData, - } - } - - /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. - /// The original guard is returned as an `Err(...)` if the closure returns - /// `None`. - /// - /// The `RwLock` is already locked for writing, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockWriteGuard::filter_map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockWriteGuard` - /// used through `Deref`. - /// - /// # Panics - /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map( - mut orig: Self, - f: F, - ) -> Result, Self> - where - F: FnOnce(&mut T) -> Option<&mut U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the - // reference passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_mut() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockWriteGuard { - data, - inner_lock: orig.inner_lock, - poison_flag: orig.poison_flag, - poison_guard: orig.poison_guard.clone(), - _variance: PhantomData, - }) - } - None => Err(orig), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.lock.poison.done(&self.poison); - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { - self.lock.inner.write_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Drop for MappedRwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Drop for MappedRwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.poison_flag.done(&self.poison_guard); - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.write_unlock(); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { self.data.as_ref() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &mut *self.lock.data.get() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Deref for MappedRwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl Deref for MappedRwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl DerefMut for MappedRwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_mut() } - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Debug for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Display for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Debug for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl fmt::Display for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} diff --git a/crates/std/src/sync/reentrant_lock.rs b/crates/std/src/sync/reentrant_lock.rs deleted file mode 100644 index f560b61..0000000 --- a/crates/std/src/sync/reentrant_lock.rs +++ /dev/null @@ -1,432 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::fmt; -use crate::ops::Deref; -use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sys::sync as sys; -use crate::thread::{ThreadId, current_id}; - -/// A re-entrant mutual exclusion lock -/// -/// This lock will block *other* threads waiting for the lock to become -/// available. The thread which has already locked the mutex can lock it -/// multiple times without blocking, preventing a common source of deadlocks. -/// -/// # Examples -/// -/// Allow recursively calling a function needing synchronization from within -/// a callback (this is how [`StdoutLock`](crate::io::StdoutLock) is currently -/// implemented): -/// -/// ``` -/// #![feature(reentrant_lock)] -/// -/// use std::cell::RefCell; -/// use std::sync::ReentrantLock; -/// -/// pub struct Log { -/// data: RefCell, -/// } -/// -/// impl Log { -/// pub fn append(&self, msg: &str) { -/// self.data.borrow_mut().push_str(msg); -/// } -/// } -/// -/// static LOG: ReentrantLock = ReentrantLock::new(Log { data: RefCell::new(String::new()) }); -/// -/// pub fn with_log(f: impl FnOnce(&Log) -> R) -> R { -/// let log = LOG.lock(); -/// f(&*log) -/// } -/// -/// with_log(|log| { -/// log.append("Hello"); -/// with_log(|log| log.append(" there!")); -/// }); -/// ``` -/// -// # Implementation details -// -// The 'owner' field tracks which thread has locked the mutex. -// -// We use thread::current_id() as the thread identifier, which is just the -// current thread's ThreadId, so it's unique across the process lifetime. -// -// If `owner` is set to the identifier of the current thread, -// we assume the mutex is already locked and instead of locking it again, -// we increment `lock_count`. -// -// When unlocking, we decrement `lock_count`, and only unlock the mutex when -// it reaches zero. -// -// `lock_count` is protected by the mutex and only accessed by the thread that has -// locked the mutex, so needs no synchronization. -// -// `owner` can be checked by other threads that want to see if they already -// hold the lock, so needs to be atomic. If it compares equal, we're on the -// same thread that holds the mutex and memory access can use relaxed ordering -// since we're not dealing with multiple threads. If it's not equal, -// synchronization is left to the mutex, making relaxed memory ordering for -// the `owner` field fine in all cases. -// -// On systems without 64 bit atomics we also store the address of a TLS variable -// along the 64-bit TID. We then first check that address against the address -// of that variable on the current thread, and only if they compare equal do we -// compare the actual TIDs. Because we only ever read the TID on the same thread -// that it was written on (or a thread sharing the TLS block with that writer thread), -// we don't need to further synchronize the TID accesses, so they can be regular 64-bit -// non-atomic accesses. -#[unstable(feature = "reentrant_lock", issue = "121440")] -pub struct ReentrantLock { - mutex: sys::Mutex, - owner: Tid, - lock_count: UnsafeCell, - data: T, -} - -cfg_select!( - target_has_atomic = "64" => { - use crate::sync::atomic::{Atomic, AtomicU64, Ordering::Relaxed}; - - struct Tid(Atomic); - - impl Tid { - const fn new() -> Self { - Self(AtomicU64::new(0)) - } - - #[inline] - fn contains(&self, owner: ThreadId) -> bool { - owner.as_u64().get() == self.0.load(Relaxed) - } - - #[inline] - // This is just unsafe to match the API of the Tid type below. - unsafe fn set(&self, tid: Option) { - let value = tid.map_or(0, |tid| tid.as_u64().get()); - self.0.store(value, Relaxed); - } - } - } - _ => { - /// Returns the address of a TLS variable. This is guaranteed to - /// be unique across all currently alive threads. - fn tls_addr() -> usize { - thread_local! { static X: u8 = const { 0u8 } }; - - X.with(|p| <*const u8>::addr(p)) - } - - use crate::sync::atomic::{ - Atomic, - AtomicUsize, - Ordering, - }; - - struct Tid { - // When a thread calls `set()`, this value gets updated to - // the address of a thread local on that thread. This is - // used as a first check in `contains()`; if the `tls_addr` - // doesn't match the TLS address of the current thread, then - // the ThreadId also can't match. Only if the TLS addresses do - // match do we read out the actual TID. - // Note also that we can use relaxed atomic operations here, because - // we only ever read from the tid if `tls_addr` matches the current - // TLS address. In that case, either the tid has been set by - // the current thread, or by a thread that has terminated before - // the current thread's `tls_addr` was allocated. In either case, no further - // synchronization is needed (as per ) - tls_addr: Atomic, - tid: UnsafeCell, - } - - unsafe impl Send for Tid {} - unsafe impl Sync for Tid {} - - impl Tid { - const fn new() -> Self { - Self { tls_addr: AtomicUsize::new(0), tid: UnsafeCell::new(0) } - } - - #[inline] - // NOTE: This assumes that `owner` is the ID of the current - // thread, and may spuriously return `false` if that's not the case. - fn contains(&self, owner: ThreadId) -> bool { - // We must call `tls_addr()` *before* doing the load to ensure that if we reuse an - // earlier thread's address, the `tls_addr.load()` below happens-after everything - // that thread did. - let tls_addr = tls_addr(); - // SAFETY: See the comments in the struct definition. - self.tls_addr.load(Ordering::Relaxed) == tls_addr - && unsafe { *self.tid.get() } == owner.as_u64().get() - } - - #[inline] - // This may only be called by one thread at a time, and can lead to - // race conditions otherwise. - unsafe fn set(&self, tid: Option) { - // It's important that we set `self.tls_addr` to 0 if the tid is - // cleared. Otherwise, there might be race conditions between - // `set()` and `get()`. - let tls_addr = if tid.is_some() { tls_addr() } else { 0 }; - let value = tid.map_or(0, |tid| tid.as_u64().get()); - self.tls_addr.store(tls_addr, Ordering::Relaxed); - unsafe { *self.tid.get() = value }; - } - } - } -); - -#[unstable(feature = "reentrant_lock", issue = "121440")] -unsafe impl Send for ReentrantLock {} -#[unstable(feature = "reentrant_lock", issue = "121440")] -unsafe impl Sync for ReentrantLock {} - -// Because of the `UnsafeCell`, these traits are not implemented automatically -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl UnwindSafe for ReentrantLock {} -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl RefUnwindSafe for ReentrantLock {} - -/// An RAII implementation of a "scoped lock" of a re-entrant lock. When this -/// structure is dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// [`Deref`] implementation. -/// -/// This structure is created by the [`lock`](ReentrantLock::lock) method on -/// [`ReentrantLock`]. -/// -/// # Mutability -/// -/// Unlike [`MutexGuard`](super::MutexGuard), `ReentrantLockGuard` does not -/// implement [`DerefMut`](crate::ops::DerefMut), because implementation of -/// the trait would violate Rust’s reference aliasing rules. Use interior -/// mutability (usually [`RefCell`](crate::cell::RefCell)) in order to mutate -/// the guarded data. -#[must_use = "if unused the ReentrantLock will immediately unlock"] -#[unstable(feature = "reentrant_lock", issue = "121440")] -pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> { - lock: &'a ReentrantLock, -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl !Send for ReentrantLockGuard<'_, T> {} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -unsafe impl Sync for ReentrantLockGuard<'_, T> {} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl ReentrantLock { - /// Creates a new re-entrant lock in an unlocked state ready for use. - /// - /// # Examples - /// - /// ``` - /// #![feature(reentrant_lock)] - /// use std::sync::ReentrantLock; - /// - /// let lock = ReentrantLock::new(0); - /// ``` - pub const fn new(t: T) -> ReentrantLock { - ReentrantLock { - mutex: sys::Mutex::new(), - owner: Tid::new(), - lock_count: UnsafeCell::new(0), - data: t, - } - } - - /// Consumes this lock, returning the underlying data. - /// - /// # Examples - /// - /// ``` - /// #![feature(reentrant_lock)] - /// - /// use std::sync::ReentrantLock; - /// - /// let lock = ReentrantLock::new(0); - /// assert_eq!(lock.into_inner(), 0); - /// ``` - pub fn into_inner(self) -> T { - self.data - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl ReentrantLock { - /// Acquires the lock, blocking the current thread until it is able to do - /// so. - /// - /// This function will block the caller until it is available to acquire - /// the lock. Upon returning, the thread is the only thread with the lock - /// held. When the thread calling this method already holds the lock, the - /// call succeeds without blocking. - /// - /// # Examples - /// - /// ``` - /// #![feature(reentrant_lock)] - /// use std::cell::Cell; - /// use std::sync::{Arc, ReentrantLock}; - /// use std::thread; - /// - /// let lock = Arc::new(ReentrantLock::new(Cell::new(0))); - /// let c_lock = Arc::clone(&lock); - /// - /// thread::spawn(move || { - /// c_lock.lock().set(10); - /// }).join().expect("thread::spawn failed"); - /// assert_eq!(lock.lock().get(), 10); - /// ``` - pub fn lock(&self) -> ReentrantLockGuard<'_, T> { - let this_thread = current_id(); - // Safety: We only touch lock_count when we own the inner mutex. - // Additionally, we only call `self.owner.set()` while holding - // the inner mutex, so no two threads can call it concurrently. - unsafe { - if self.owner.contains(this_thread) { - self.increment_lock_count().expect("lock count overflow in reentrant mutex"); - } else { - self.mutex.lock(); - self.owner.set(Some(this_thread)); - debug_assert_eq!(*self.lock_count.get(), 0); - *self.lock_count.get() = 1; - } - } - ReentrantLockGuard { lock: self } - } - - /// Returns a mutable reference to the underlying data. - /// - /// Since this call borrows the `ReentrantLock` mutably, no actual locking - /// needs to take place -- the mutable borrow statically guarantees no locks - /// exist. - /// - /// # Examples - /// - /// ``` - /// #![feature(reentrant_lock)] - /// use std::sync::ReentrantLock; - /// - /// let mut lock = ReentrantLock::new(0); - /// *lock.get_mut() = 10; - /// assert_eq!(*lock.lock(), 10); - /// ``` - pub fn get_mut(&mut self) -> &mut T { - &mut self.data - } - - /// Attempts to acquire this lock. - /// - /// If the lock could not be acquired at this time, then `None` is returned. - /// Otherwise, an RAII guard is returned. - /// - /// This function does not block. - // FIXME maybe make it a public part of the API? - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - pub fn try_lock(&self) -> Option> { - let this_thread = current_id(); - // Safety: We only touch lock_count when we own the inner mutex. - // Additionally, we only call `self.owner.set()` while holding - // the inner mutex, so no two threads can call it concurrently. - unsafe { - if self.owner.contains(this_thread) { - self.increment_lock_count()?; - Some(ReentrantLockGuard { lock: self }) - } else if self.mutex.try_lock() { - self.owner.set(Some(this_thread)); - debug_assert_eq!(*self.lock_count.get(), 0); - *self.lock_count.get() = 1; - Some(ReentrantLockGuard { lock: self }) - } else { - None - } - } - } - - /// Returns a raw pointer to the underlying data. - /// - /// The returned pointer is always non-null and properly aligned, but it is - /// the user's responsibility to ensure that any reads through it are - /// properly synchronized to avoid data races, and that it is not read - /// through after the lock is dropped. - #[unstable(feature = "reentrant_lock_data_ptr", issue = "140368")] - pub const fn data_ptr(&self) -> *const T { - &raw const self.data - } - - unsafe fn increment_lock_count(&self) -> Option<()> { - unsafe { - *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; - } - Some(()) - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl fmt::Debug for ReentrantLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_struct("ReentrantLock"); - match self.try_lock() { - Some(v) => d.field("data", &&*v), - None => d.field("data", &format_args!("")), - }; - d.finish_non_exhaustive() - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl Default for ReentrantLock { - fn default() -> Self { - Self::new(T::default()) - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl From for ReentrantLock { - fn from(t: T) -> Self { - Self::new(t) - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl Deref for ReentrantLockGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - &self.lock.data - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl fmt::Debug for ReentrantLockGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl fmt::Display for ReentrantLockGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "reentrant_lock", issue = "121440")] -impl Drop for ReentrantLockGuard<'_, T> { - #[inline] - fn drop(&mut self) { - // Safety: We own the lock. - unsafe { - *self.lock.lock_count.get() -= 1; - if *self.lock.lock_count.get() == 0 { - self.lock.owner.set(None); - self.lock.mutex.unlock(); - } - } - } -} diff --git a/crates/std/src/sys/alloc/mod.rs b/crates/std/src/sys/alloc/mod.rs deleted file mode 100644 index 445dde8..0000000 --- a/crates/std/src/sys/alloc/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; - -// The minimum alignment guaranteed by the architecture. This value is used to -// add fast paths for low alignment values. -#[allow(dead_code)] -const MIN_ALIGN: usize = if cfg!(any( - all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), - all(target_arch = "xtensa", target_os = "espidf"), -)) { - // The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment. - 4 -} else if cfg!(any( - target_arch = "x86", - target_arch = "arm", - target_arch = "m68k", - target_arch = "csky", - target_arch = "loongarch32", - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "sparc", - target_arch = "wasm32", - target_arch = "hexagon", - target_arch = "riscv32", - target_arch = "xtensa", -)) { - 8 -} else if cfg!(any( - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "loongarch64", - target_arch = "mips64", - target_arch = "mips64r6", - target_arch = "s390x", - target_arch = "sparc64", - target_arch = "riscv64", - target_arch = "wasm64", -)) { - 16 -} else { - panic!("add a value for MIN_ALIGN") -}; - -#[allow(dead_code)] -unsafe fn realloc_fallback( - alloc: &System, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, -) -> *mut u8 { - // SAFETY: Docs for GlobalAlloc::realloc require this to be valid - unsafe { - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); - if !new_ptr.is_null() { - let size = usize::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); - } - - new_ptr - } -} - -cfg_select! { - any( - target_family = "unix", - target_os = "wasi", - target_os = "teeos", - target_os = "trusty", - ) => { - mod unix; - } - target_os = "windows" => { - mod windows; - } - target_os = "hermit" => { - mod hermit; - } - target_os = "motor" => { - mod motor; - } - all(target_vendor = "fortanix", target_env = "sgx") => { - mod sgx; - } - target_os = "solid_asp3" => { - mod solid; - } - target_os = "uefi" => { - mod uefi; - } - target_os = "vexos" => { - mod vexos; - } - target_family = "wasm" => { - mod wasm; - } - target_os = "xous" => { - mod xous; - } - target_os = "zkvm" => { - mod zkvm; - } - target_os = "survos" => { - mod survos; - } -} diff --git a/crates/std/src/sys/alloc/survos.rs b/crates/std/src/sys/alloc/survos.rs deleted file mode 100644 index b953842..0000000 --- a/crates/std/src/sys/alloc/survos.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - unsafe { crate::syscall::alloc(layout) } - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - unsafe { crate::syscall::dealloc(ptr, layout) } - } -} diff --git a/crates/std/src/thread/builder.rs b/crates/std/src/thread/builder.rs deleted file mode 100644 index baec8c9..0000000 --- a/crates/std/src/thread/builder.rs +++ /dev/null @@ -1,263 +0,0 @@ -use super::join_handle::JoinHandle; -use super::lifecycle::spawn_unchecked; -use crate::io; - -/// Thread factory, which can be used in order to configure the properties of -/// a new thread. -/// -/// Methods can be chained on it in order to configure it. -/// -/// The two configurations available are: -/// -/// - [`name`]: specifies an [associated name for the thread][naming-threads] -/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] -/// -/// The [`spawn`] method will take ownership of the builder and create an -/// [`io::Result`] to the thread handle with the given configuration. -/// -/// The [`thread::spawn`] free function uses a `Builder` with default -/// configuration and [`unwrap`]s its return value. -/// -/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want -/// to recover from a failure to launch a thread, indeed the free function will -/// panic where the `Builder` method will return a [`io::Result`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let handler = builder.spawn(|| { -/// // thread code -/// }).unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// [`stack_size`]: Builder::stack_size -/// [`name`]: Builder::name -/// [`spawn`]: Builder::spawn -/// [`thread::spawn`]: super::spawn -/// [`unwrap`]: crate::result::Result::unwrap -/// [naming-threads]: ./index.html#naming-threads -/// [stack-size]: ./index.html#stack-size -#[must_use = "must eventually spawn the thread"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Builder { - /// A name for the thread-to-be, for identification in panic messages - pub(super) name: Option, - /// The size of the stack for the spawned thread in bytes - pub(super) stack_size: Option, - /// Skip running and inheriting the thread spawn hooks - pub(super) no_hooks: bool, -} - -impl Builder { - /// Generates the base configuration for spawning a thread, from which - /// configuration methods can be chained. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()) - /// .stack_size(32 * 1024); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Builder { - Builder { name: None, stack_size: None, no_hooks: false } - } - - /// Names the thread-to-be. Currently the name is used for identification - /// only in panic messages. - /// - /// The name must not contain null bytes (`\0`). - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Sets the size of the stack (in bytes) for the new thread. - /// - /// The actual stack size may be greater than this value if - /// the platform specifies a minimal stack size. - /// - /// For more information about the stack size for threads, see - /// [this module-level documentation][stack-size]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new().stack_size(32 * 1024); - /// ``` - /// - /// [stack-size]: ./index.html#stack-size - #[stable(feature = "rust1", since = "1.0.0")] - pub fn stack_size(mut self, size: usize) -> Builder { - self.stack_size = Some(size); - self - } - - /// Disables running and inheriting [spawn hooks]. - /// - /// Use this if the parent thread is in no way relevant for the child thread. - /// For example, when lazily spawning threads for a thread pool. - /// - /// [spawn hooks]: super::add_spawn_hook - #[unstable(feature = "thread_spawn_hook", issue = "132951")] - pub fn no_hooks(mut self) -> Builder { - self.no_hooks = true; - self - } - - /// Spawns a new thread by taking ownership of the `Builder`, and returns an - /// [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// For a more complete documentation see [`thread::spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [`thread::spawn`]: super::spawn - /// [`spawn`]: super::spawn - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub fn spawn(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - unsafe { self.spawn_unchecked(f) } - } - - /// Spawns a new thread without any lifetime restrictions by taking ownership - /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], - /// except for the relaxed lifetime bounds, which render it unsafe. - /// For a more complete documentation see [`thread::spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Safety - /// - /// The caller has to ensure that the spawned thread does not outlive any - /// references in the supplied thread closure and its return type. - /// This can be guaranteed in two ways: - /// - /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced - /// data is dropped - /// - use only types with `'static` lifetime bounds, i.e., those with no or only - /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] - /// and [`thread::spawn`] enforce this property statically) - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let x = 1; - /// let thread_x = &x; - /// - /// let handler = unsafe { - /// builder.spawn_unchecked(move || { - /// println!("x = {}", *thread_x); - /// }).unwrap() - /// }; - /// - /// // caller has to ensure `join()` is called, otherwise - /// // it is possible to access freed memory if `x` gets - /// // dropped before the thread closure is executed! - /// handler.join().unwrap(); - /// ``` - /// - /// [`thread::spawn`]: super::spawn - /// [`spawn`]: super::spawn - #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send, - T: Send, - { - let Builder { name, stack_size, no_hooks } = self; - Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?)) - } -} diff --git a/crates/std/src/thread/current.rs b/crates/std/src/thread/current.rs deleted file mode 100644 index 508e35c..0000000 --- a/crates/std/src/thread/current.rs +++ /dev/null @@ -1,332 +0,0 @@ -use super::id::ThreadId; -use super::main_thread; -use super::thread::Thread; -use crate::mem::ManuallyDrop; -use crate::ptr; -use crate::sys::thread as imp; -use crate::sys::thread_local::local_pointer; - -const NONE: *mut () = ptr::null_mut(); -const BUSY: *mut () = ptr::without_provenance_mut(1); -const DESTROYED: *mut () = ptr::without_provenance_mut(2); - -local_pointer! { - static CURRENT; -} - -/// Persistent storage for the thread ID. -/// -/// We store the thread ID so that it never gets destroyed during the lifetime -/// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. -pub(super) mod id { - use super::*; - - cfg_select! { - target_thread_local => { - use crate::cell::Cell; - - #[thread_local] - static ID: Cell> = Cell::new(None); - - pub(super) const CHEAP: bool = true; - - pub(crate) fn get() -> Option { - ID.get() - } - - pub(super) fn set(id: ThreadId) { - ID.set(Some(id)) - } - } - target_pointer_width = "16" => { - local_pointer! { - static ID0; - static ID16; - static ID32; - static ID48; - } - - pub(super) const CHEAP: bool = false; - - pub(crate) fn get() -> Option { - let id0 = ID0.get().addr() as u64; - let id16 = ID16.get().addr() as u64; - let id32 = ID32.get().addr() as u64; - let id48 = ID48.get().addr() as u64; - ThreadId::from_u64((id48 << 48) + (id32 << 32) + (id16 << 16) + id0) - } - - pub(super) fn set(id: ThreadId) { - let val = id.as_u64().get(); - ID0.set(ptr::without_provenance_mut(val as usize)); - ID16.set(ptr::without_provenance_mut((val >> 16) as usize)); - ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); - ID48.set(ptr::without_provenance_mut((val >> 48) as usize)); - } - } - target_pointer_width = "32" => { - local_pointer! { - static ID0; - static ID32; - } - - pub(super) const CHEAP: bool = false; - - pub(crate) fn get() -> Option { - let id0 = ID0.get().addr() as u64; - let id32 = ID32.get().addr() as u64; - ThreadId::from_u64((id32 << 32) + id0) - } - - pub(super) fn set(id: ThreadId) { - let val = id.as_u64().get(); - ID0.set(ptr::without_provenance_mut(val as usize)); - ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); - } - } - _ => { - local_pointer! { - static ID; - } - - pub(super) const CHEAP: bool = true; - - pub(crate) fn get() -> Option { - let id = ID.get().addr() as u64; - ThreadId::from_u64(id) - } - - pub(super) fn set(id: ThreadId) { - let val = id.as_u64().get(); - ID.set(ptr::without_provenance_mut(val as usize)); - } - } - } - - #[inline] - pub(super) fn get_or_init() -> ThreadId { - get().unwrap_or_else( - #[cold] - || { - let id = ThreadId::new(); - id::set(id); - id - }, - ) - } -} - -/// Tries to set the thread handle for the current thread. Fails if a handle was -/// already set or if the thread ID of `thread` would change an already-set ID. -pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { - if CURRENT.get() != NONE { - return Err(thread); - } - - match id::get() { - Some(id) if id == thread.id() => {} - None => id::set(thread.id()), - _ => return Err(thread), - } - - // Make sure that `crate::rt::thread_cleanup` will be run, which will - // call `drop_current`. - crate::sys::thread_local::guard::enable(); - CURRENT.set(thread.into_raw().cast_mut()); - Ok(()) -} - -/// Gets the unique identifier of the thread which invokes it. -/// -/// Calling this function may be more efficient than accessing the current -/// thread id through the current thread handle. i.e. `thread::current().id()`. -/// -/// This function will always succeed, will always return the same value for -/// one thread and is guaranteed not to call the global allocator. -/// -/// # Examples -/// -/// ``` -/// #![feature(current_thread_id)] -/// -/// use std::thread; -/// -/// let other_thread = thread::spawn(|| { -/// thread::current_id() -/// }); -/// -/// let other_thread_id = other_thread.join().unwrap(); -/// assert_ne!(thread::current_id(), other_thread_id); -/// ``` -#[inline] -#[must_use] -#[unstable(feature = "current_thread_id", issue = "147194")] -pub fn current_id() -> ThreadId { - // If accessing the persistent thread ID takes multiple TLS accesses, try - // to retrieve it from the current thread handle, which will only take one - // TLS access. - if !id::CHEAP { - if let Some(id) = try_with_current(|t| t.map(|t| t.id())) { - return id; - } - } - - id::get_or_init() -} - -/// Gets the OS thread ID of the thread that invokes it, if available. If not, return the Rust -/// thread ID. -/// -/// We use a `u64` to all possible platform IDs without excess `cfg`; most use `int`, some use a -/// pointer, and Apple uses `uint64_t`. This is a "best effort" approach for diagnostics and is -/// allowed to fall back to a non-OS ID (such as the Rust thread ID) or a non-unique ID (such as a -/// PID) if the thread ID cannot be retrieved. -pub(crate) fn current_os_id() -> u64 { - imp::current_os_id().unwrap_or_else(|| current_id().as_u64().get()) -} - -/// Gets a reference to the handle of the thread that invokes it, if the handle -/// has been initialized. -fn try_with_current(f: F) -> R -where - F: FnOnce(Option<&Thread>) -> R, -{ - let current = CURRENT.get(); - if current > DESTROYED { - // SAFETY: `Arc` does not contain interior mutability, so it does not - // matter that the address of the handle might be different depending - // on where this is called. - unsafe { - let current = ManuallyDrop::new(Thread::from_raw(current)); - f(Some(¤t)) - } - } else { - f(None) - } -} - -/// Run a function with the current thread's name. -/// -/// Modulo thread local accesses, this function is safe to call from signal -/// handlers and in similar circumstances where allocations are not possible. -pub(crate) fn with_current_name(f: F) -> R -where - F: FnOnce(Option<&str>) -> R, -{ - try_with_current(|thread| { - let name = if let Some(thread) = thread { - // If there is a current thread handle, try to use the name stored - // there. - thread.name() - } else if let Some(main) = main_thread::get() - && let Some(id) = id::get() - && id == main - { - // The main thread doesn't always have a thread handle, we must - // identify it through its ID instead. The checks are ordered so - // that the current ID is only loaded if it is actually needed, - // since loading it from TLS might need multiple expensive accesses. - Some("main") - } else { - None - }; - - f(name) - }) -} - -/// Gets a handle to the thread that invokes it. If the handle stored in thread- -/// local storage was already destroyed, this creates a new unnamed temporary -/// handle to allow thread parking in nearly all situations. -pub(crate) fn current_or_unnamed() -> Thread { - let current = CURRENT.get(); - if current > DESTROYED { - unsafe { - let current = ManuallyDrop::new(Thread::from_raw(current)); - (*current).clone() - } - } else if current == DESTROYED { - Thread::new(id::get_or_init(), None) - } else { - init_current(current) - } -} - -/// Gets a handle to the thread that invokes it. -/// -/// # Examples -/// -/// Getting a handle to the current thread with `thread::current()`: -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::Builder::new() -/// .name("named thread".into()) -/// .spawn(|| { -/// let handle = thread::current(); -/// assert_eq!(handle.name(), Some("named thread")); -/// }) -/// .unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn current() -> Thread { - let current = CURRENT.get(); - if current > DESTROYED { - unsafe { - let current = ManuallyDrop::new(Thread::from_raw(current)); - (*current).clone() - } - } else { - init_current(current) - } -} - -#[cold] -fn init_current(current: *mut ()) -> Thread { - if current == NONE { - CURRENT.set(BUSY); - // If the thread ID was initialized already, use it. - let id = id::get_or_init(); - let thread = Thread::new(id, None); - - // Make sure that `crate::rt::thread_cleanup` will be run, which will - // call `drop_current`. - crate::sys::thread_local::guard::enable(); - CURRENT.set(thread.clone().into_raw().cast_mut()); - thread - } else if current == BUSY { - // BUSY exists solely for this check, but as it is in the slow path, the - // extra TLS write above shouldn't matter. The alternative is nearly always - // a stack overflow. - // - // If we reach this point it means our initialization routine ended up - // calling current() either directly, or indirectly through the global - // allocator, which is a bug either way as we may not call the global - // allocator in current(). - rtabort!( - "init_current() was re-entrant, which indicates a bug in the Rust threading implementation" - ) - } else { - debug_assert_eq!(current, DESTROYED); - panic!( - "use of std::thread::current() is not possible after the thread's \ - local data has been destroyed" - ) - } -} - -/// This should be run in [`crate::rt::thread_cleanup`] to reset the thread -/// handle. -pub(crate) fn drop_current() { - let current = CURRENT.get(); - if current > DESTROYED { - unsafe { - CURRENT.set(DESTROYED); - drop(Thread::from_raw(current)); - } - } -} diff --git a/crates/std/src/thread/functions.rs b/crates/std/src/thread/functions.rs deleted file mode 100644 index 7064210..0000000 --- a/crates/std/src/thread/functions.rs +++ /dev/null @@ -1,696 +0,0 @@ -//! Free functions. - -use super::builder::Builder; -use super::current::current; -use super::join_handle::JoinHandle; -use crate::mem::forget; -use crate::num::NonZero; -use crate::sys::thread as imp; -use crate::time::{Duration, Instant}; -use crate::{io, panicking}; - -/// Spawns a new thread, returning a [`JoinHandle`] for it. -/// -/// The join handle provides a [`join`] method that can be used to join the spawned -/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing -/// the argument given to [`panic!`]. -/// -/// If the join handle is dropped, the spawned thread will implicitly be *detached*. -/// In this case, the spawned thread may no longer be joined. -/// (It is the responsibility of the program to either eventually join threads it -/// creates or detach them; otherwise, a resource leak will result.) -/// -/// This function creates a thread with the default parameters of [`Builder`]. -/// To specify the new thread's stack size or the name, use [`Builder::spawn`]. -/// -/// As you can see in the signature of `spawn` there are two constraints on -/// both the closure given to `spawn` and its return value, let's explain them: -/// -/// - The `'static` constraint means that the closure and its return value -/// must have a lifetime of the whole program execution. The reason for this -/// is that threads can outlive the lifetime they have been created in. -/// -/// Indeed if the thread, and by extension its return value, can outlive their -/// caller, we need to make sure that they will be valid afterwards, and since -/// we *can't* know when it will return we need to have them valid as long as -/// possible, that is until the end of the program, hence the `'static` -/// lifetime. -/// - The [`Send`] constraint is because the closure will need to be passed -/// *by value* from the thread where it is spawned to the new thread. Its -/// return value will need to be passed from the new thread to the thread -/// where it is `join`ed. -/// As a reminder, the [`Send`] marker trait expresses that it is safe to be -/// passed from thread to thread. [`Sync`] expresses that it is safe to have a -/// reference be passed from thread to thread. -/// -/// # Panics -/// -/// Panics if the OS fails to create a thread; use [`Builder::spawn`] -/// to recover from such errors. -/// -/// # Examples -/// -/// Creating a thread. -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::spawn(|| { -/// // thread code -/// }); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// As mentioned in the module documentation, threads are usually made to -/// communicate using [`channels`], here is how it usually looks. -/// -/// This example also shows how to use `move`, in order to give ownership -/// of values to a thread. -/// -/// ``` -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// let (tx, rx) = channel(); -/// -/// let sender = thread::spawn(move || { -/// tx.send("Hello, thread".to_owned()) -/// .expect("Unable to send on channel"); -/// }); -/// -/// let receiver = thread::spawn(move || { -/// let value = rx.recv().expect("Unable to receive from channel"); -/// println!("{value}"); -/// }); -/// -/// sender.join().expect("The sender thread has panicked"); -/// receiver.join().expect("The receiver thread has panicked"); -/// ``` -/// -/// A thread can also return a value through its [`JoinHandle`], you can use -/// this to make asynchronous computations (futures might be more appropriate -/// though). -/// -/// ``` -/// use std::thread; -/// -/// let computation = thread::spawn(|| { -/// // Some expensive computation. -/// 42 -/// }); -/// -/// let result = computation.join().unwrap(); -/// println!("{result}"); -/// ``` -/// -/// # Notes -/// -/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. -/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a -/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` -/// unwinds all the way to the root with such an exception, one of two behaviors are possible, -/// and it is unspecified which will occur: -/// -/// * The process aborts. -/// * The process does not abort, and [`join`] will return a `Result::Err` -/// containing an opaque type. -/// -/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html -/// [`channels`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Err`]: crate::result::Result::Err -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -pub fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(f).expect("failed to spawn thread") -} - -/// Cooperatively gives up a timeslice to the OS scheduler. -/// -/// This calls the underlying OS scheduler's yield primitive, signaling -/// that the calling thread is willing to give up its remaining timeslice -/// so that the OS may schedule other threads on the CPU. -/// -/// A drawback of yielding in a loop is that if the OS does not have any -/// other ready threads to run on the current CPU, the thread will effectively -/// busy-wait, which wastes CPU time and energy. -/// -/// Therefore, when waiting for events of interest, a programmer's first -/// choice should be to use synchronization devices such as [`channel`]s, -/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are -/// implemented in a blocking manner, giving up the CPU until the event -/// of interest has occurred which avoids repeated yielding. -/// -/// `yield_now` should thus be used only rarely, mostly in situations where -/// repeated polling is required because there is no other suitable way to -/// learn when an event of interest has occurred. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// thread::yield_now(); -/// ``` -/// -/// [`channel`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Condvar`]: crate::sync::Condvar -/// [`Mutex`]: crate::sync::Mutex -#[stable(feature = "rust1", since = "1.0.0")] -pub fn yield_now() { - imp::yield_now() -} - -/// Determines whether the current thread is panicking. -/// -/// This returns `true` both when the thread is unwinding due to a panic, -/// or executing a panic hook. Note that the latter case will still happen -/// when `panic=abort` is set. -/// -/// A common use of this feature is to poison shared resources when writing -/// unsafe code, by checking `panicking` when the `drop` is called. -/// -/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] -/// already poison themselves when a thread panics while holding the lock. -/// -/// This can also be used in multithreaded applications, in order to send a -/// message to other threads warning that a thread has panicked (e.g., for -/// monitoring purposes). -/// -/// # Examples -/// -/// ```should_panic -/// use std::thread; -/// -/// struct SomeStruct; -/// -/// impl Drop for SomeStruct { -/// fn drop(&mut self) { -/// if thread::panicking() { -/// println!("dropped while unwinding"); -/// } else { -/// println!("dropped while not unwinding"); -/// } -/// } -/// } -/// -/// { -/// print!("a: "); -/// let a = SomeStruct; -/// } -/// -/// { -/// print!("b: "); -/// let b = SomeStruct; -/// panic!() -/// } -/// ``` -/// -/// [Mutex]: crate::sync::Mutex -#[inline] -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn panicking() -> bool { - panicking::panicking() -} - -/// Uses [`sleep`]. -/// -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// -/// // Let's sleep for 2 seconds: -/// thread::sleep_ms(2000); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] -pub fn sleep_ms(ms: u32) { - sleep(Duration::from_millis(ms as u64)) -} - -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// Platforms which do not support nanosecond precision for sleeping will -/// have `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// Currently, specifying a zero duration on Unix platforms returns immediately -/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows -/// platforms the underlying [`Sleep`] syscall is always invoked. -/// If the intention is to yield the current time-slice you may want to use -/// [`yield_now`] instead. -/// -/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep -/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep -/// -/// # Examples -/// -/// ```no_run -/// use std::{thread, time}; -/// -/// let ten_millis = time::Duration::from_millis(10); -/// let now = time::Instant::now(); -/// -/// thread::sleep(ten_millis); -/// -/// assert!(now.elapsed() >= ten_millis); -/// ``` -#[stable(feature = "thread_sleep", since = "1.4.0")] -pub fn sleep(dur: Duration) { - imp::sleep(dur) -} - -/// Puts the current thread to sleep until the specified deadline has passed. -/// -/// The thread may still be asleep after the deadline specified due to -/// scheduling specifics or platform-dependent functionality. It will never -/// wake before. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// In most cases this function will call an OS specific function. Where that -/// is not supported [`sleep`] is used. Those platforms are referred to as other -/// in the table below. -/// -/// # Underlying System calls -/// -/// The following system calls are [currently] being used: -/// -/// | Platform | System call | -/// |-----------|----------------------------------------------------------------------| -/// | Linux | [clock_nanosleep] (Monotonic Clock) | -/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock) | -/// | Android | [clock_nanosleep] (Monotonic Clock) | -/// | Solaris | [clock_nanosleep] (Monotonic Clock) | -/// | Illumos | [clock_nanosleep] (Monotonic Clock) | -/// | Dragonfly | [clock_nanosleep] (Monotonic Clock) | -/// | Hurd | [clock_nanosleep] (Monotonic Clock) | -/// | Vxworks | [clock_nanosleep] (Monotonic Clock) | -/// | Apple | `mach_wait_until` | -/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | -/// -/// [currently]: crate::io#platform-specific-behavior -/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep -/// -/// **Disclaimer:** These system calls might change over time. -/// -/// # Examples -/// -/// A simple game loop that limits the game to 60 frames per second. -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # fn update() {} -/// # fn render() {} -/// # -/// let max_fps = 60.0; -/// let frame_time = Duration::from_secs_f32(1.0/max_fps); -/// let mut next_frame = Instant::now(); -/// loop { -/// thread::sleep_until(next_frame); -/// next_frame += frame_time; -/// update(); -/// render(); -/// } -/// ``` -/// -/// A slow API we must not call too fast and which takes a few -/// tries before succeeding. By using `sleep_until` the time the -/// API call takes does not influence when we retry or when we give up -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # enum Status { -/// # Ready(usize), -/// # Waiting, -/// # } -/// # fn slow_web_api_call() -> Status { Status::Ready(42) } -/// # -/// # const MAX_DURATION: Duration = Duration::from_secs(10); -/// # -/// # fn try_api_call() -> Result { -/// let deadline = Instant::now() + MAX_DURATION; -/// let delay = Duration::from_millis(250); -/// let mut next_attempt = Instant::now(); -/// loop { -/// if Instant::now() > deadline { -/// break Err(()); -/// } -/// if let Status::Ready(data) = slow_web_api_call() { -/// break Ok(data); -/// } -/// -/// next_attempt = deadline.min(next_attempt + delay); -/// thread::sleep_until(next_attempt); -/// } -/// # } -/// # let _data = try_api_call(); -/// ``` -#[unstable(feature = "thread_sleep_until", issue = "113752")] -pub fn sleep_until(deadline: Instant) { - imp::sleep_until(deadline) -} - -/// Used to ensure that `park` and `park_timeout` do not unwind, as that can -/// cause undefined behavior if not handled correctly (see #102398 for context). -struct PanicGuard; - -impl Drop for PanicGuard { - fn drop(&mut self) { - rtabort!("an irrecoverable error occurred while synchronizing threads") - } -} - -/// Blocks unless or until the current thread's token is made available. -/// -/// A call to `park` does not guarantee that the thread will remain parked -/// forever, and callers should be prepared for this possibility. However, -/// it is guaranteed that this function will not panic (it may abort the -/// process if the implementation encounters some rare errors). -/// -/// # `park` and `unpark` -/// -/// Every thread is equipped with some basic low-level blocking support, via the -/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] -/// method. [`park`] blocks the current thread, which can then be resumed from -/// another thread by calling the [`unpark`] method on the blocked thread's -/// handle. -/// -/// Conceptually, each [`Thread`] handle has an associated token, which is -/// initially not present: -/// -/// * The [`thread::park`][`park`] function blocks the current thread unless or -/// until the token is available for its thread handle, at which point it -/// atomically consumes the token. It may also return *spuriously*, without -/// consuming the token. [`thread::park_timeout`] does the same, but allows -/// specifying a maximum time to block the thread for. -/// -/// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token can be held by a thread even if it is currently not -/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. -/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens -/// after all `park` that may be done by other data structures! -/// -/// The API is typically used by acquiring a handle to the current thread, placing that handle in a -/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some -/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point -/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it -/// will be woken up properly. -/// -/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread -/// without first establishing that it is about to be `park`ing within your code, that `unpark` may -/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means -/// you must not call unknown code between setting up for parking and calling `park`; for instance, -/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a -/// deadlock. -/// -/// The motivation for this design is twofold: -/// -/// * It avoids the need to allocate mutexes and condvars when building new -/// synchronization primitives; the threads already provide basic -/// blocking/signaling. -/// -/// * It can be implemented very efficiently on many platforms. -/// -/// # Memory Ordering -/// -/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory -/// operations performed before a call to `unpark` are made visible to the thread that -/// consumes the token and returns from `park`. Note that all `park` and `unpark` -/// operations for a given thread form a total order and _all_ prior `unpark` operations -/// synchronize-with `park`. -/// -/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` -/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same -/// thread form a [release sequence]. -/// -/// Note that being unblocked does not imply a call was made to `unpark`, because -/// wakeups can also be spurious. For example, a valid, but inefficient, -/// implementation could have `park` and `unpark` return immediately without doing anything, -/// making *all* wakeups spurious. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// use std::sync::atomic::{Ordering, AtomicBool}; -/// use std::time::Duration; -/// -/// static QUEUED: AtomicBool = AtomicBool::new(false); -/// static FLAG: AtomicBool = AtomicBool::new(false); -/// -/// let parked_thread = thread::spawn(move || { -/// println!("Thread spawned"); -/// // Signal that we are going to `park`. Between this store and our `park`, there may -/// // be no other `park`, or else that `park` could consume our `unpark` token! -/// QUEUED.store(true, Ordering::Release); -/// // We want to wait until the flag is set. We *could* just spin, but using -/// // park/unpark is more efficient. -/// while !FLAG.load(Ordering::Acquire) { -/// // We can *not* use `println!` here since that could use thread parking internally. -/// thread::park(); -/// // We *could* get here spuriously, i.e., way before the 10ms below are over! -/// // But that is no problem, we are in a loop until the flag is set anyway. -/// } -/// println!("Flag received"); -/// }); -/// -/// // Let some time pass for the thread to be spawned. -/// thread::sleep(Duration::from_millis(10)); -/// -/// // Ensure the thread is about to park. -/// // This is crucial! It guarantees that the `unpark` below is not consumed -/// // by some other code in the parked thread (e.g. inside `println!`). -/// while !QUEUED.load(Ordering::Acquire) { -/// // Spinning is of course inefficient; in practice, this would more likely be -/// // a dequeue where we have no work to do if there's nobody queued. -/// std::hint::spin_loop(); -/// } -/// -/// // Set the flag, and let the thread wake up. -/// // There is no race condition here: if `unpark` -/// // happens first, `park` will return immediately. -/// // There is also no other `park` that could consume this token, -/// // since we waited until the other thread got queued. -/// // Hence there is no risk of a deadlock. -/// FLAG.store(true, Ordering::Release); -/// println!("Unpark the thread"); -/// parked_thread.thread().unpark(); -/// -/// parked_thread.join().unwrap(); -/// ``` -/// -/// [`Thread`]: super::Thread -/// [`unpark`]: super::Thread::unpark -/// [`thread::park_timeout`]: park_timeout -/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence -#[stable(feature = "rust1", since = "1.0.0")] -pub fn park() { - let guard = PanicGuard; - // SAFETY: park_timeout is called on the parker owned by this thread. - unsafe { - current().park(); - } - // No panic occurred, do not abort. - forget(guard); -} - -/// Uses [`park_timeout`]. -/// -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that might not cause the maximum -/// amount of time waited to be precisely `ms` long. -/// -/// See the [park documentation][`park`] for more detail. -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] -pub fn park_timeout_ms(ms: u32) { - park_timeout(Duration::from_millis(ms as u64)) -} - -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`][park] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that might not cause the maximum -/// amount of time waited to be precisely `dur` long. -/// -/// See the [park documentation][park] for more details. -/// -/// # Platform-specific behavior -/// -/// Platforms which do not support nanosecond precision for sleeping will have -/// `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// # Examples -/// -/// Waiting for the complete expiration of the timeout: -/// -/// ```rust,no_run -/// use std::thread::park_timeout; -/// use std::time::{Instant, Duration}; -/// -/// let timeout = Duration::from_secs(2); -/// let beginning_park = Instant::now(); -/// -/// let mut timeout_remaining = timeout; -/// loop { -/// park_timeout(timeout_remaining); -/// let elapsed = beginning_park.elapsed(); -/// if elapsed >= timeout { -/// break; -/// } -/// println!("restarting park_timeout after {elapsed:?}"); -/// timeout_remaining = timeout - elapsed; -/// } -/// ``` -#[stable(feature = "park_timeout", since = "1.4.0")] -pub fn park_timeout(dur: Duration) { - let guard = PanicGuard; - // SAFETY: park_timeout is called on a handle owned by this thread. - unsafe { - current().park_timeout(dur); - } - // No panic occurred, do not abort. - forget(guard); -} - -/// Returns an estimate of the default amount of parallelism a program should use. -/// -/// Parallelism is a resource. A given machine provides a certain capacity for -/// parallelism, i.e., a bound on the number of computations it can perform -/// simultaneously. This number often corresponds to the amount of CPUs a -/// computer has, but it may diverge in various cases. -/// -/// Host environments such as VMs or container orchestrators may want to -/// restrict the amount of parallelism made available to programs in them. This -/// is often done to limit the potential impact of (unintentionally) -/// resource-intensive programs on other programs running on the same machine. -/// -/// # Limitations -/// -/// The purpose of this API is to provide an easy and portable way to query -/// the default amount of parallelism the program should use. Among other things it -/// does not expose information on NUMA regions, does not account for -/// differences in (co)processor capabilities or current system load, -/// and will not modify the program's global state in order to more accurately -/// query the amount of available parallelism. -/// -/// Where both fixed steady-state and burst limits are available the steady-state -/// capacity will be used to ensure more predictable latencies. -/// -/// Resource limits can be changed during the runtime of a program, therefore the value is -/// not cached and instead recomputed every time this function is called. It should not be -/// called from hot code. -/// -/// The value returned by this function should be considered a simplified -/// approximation of the actual amount of parallelism available at any given -/// time. To get a more detailed or precise overview of the amount of -/// parallelism available to the program, you may wish to use -/// platform-specific APIs as well. The following platform limitations currently -/// apply to `available_parallelism`: -/// -/// On Windows: -/// - It may undercount the amount of parallelism available on systems with more -/// than 64 logical CPUs. However, programs typically need specific support to -/// take advantage of more than 64 logical CPUs, and in the absence of such -/// support, the number returned by this function accurately reflects the -/// number of logical CPUs the program can use by default. -/// - It may overcount the amount of parallelism available on systems limited by -/// process-wide affinity masks, or job object limitations. -/// -/// On Linux: -/// - It may overcount the amount of parallelism available when limited by a -/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be -/// queried, e.g. due to sandboxing. -/// - It may undercount the amount of parallelism if the current thread's affinity mask -/// does not reflect the process' cpuset, e.g. due to pinned threads. -/// - If the process is in a cgroup v1 cpu controller, this may need to -/// scan mountpoints to find the corresponding cgroup v1 controller, -/// which may take time on systems with large numbers of mountpoints. -/// (This does not apply to cgroup v2, or to processes not in a -/// cgroup.) -/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of -/// threads, `available_parallelism` cannot know how much of that limit a Rust program should -/// take, or know in a reliable and race-free way how much of that limit is already taken. -/// -/// On all targets: -/// - It may overcount the amount of parallelism available when running in a VM -/// with CPU usage limits (e.g. an overcommitted host). -/// -/// # Errors -/// -/// This function will, but is not limited to, return errors in the following -/// cases: -/// -/// - If the amount of parallelism is not known for the target platform. -/// - If the program lacks permission to query the amount of parallelism made -/// available to it. -/// -/// # Examples -/// -/// ``` -/// # #![allow(dead_code)] -/// use std::{io, thread}; -/// -/// fn main() -> io::Result<()> { -/// let count = thread::available_parallelism()?.get(); -/// assert!(count >= 1_usize); -/// Ok(()) -/// } -/// ``` -#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. -#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. -#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. -#[stable(feature = "available_parallelism", since = "1.59.0")] -pub fn available_parallelism() -> io::Result> { - imp::available_parallelism() -} diff --git a/crates/std/src/thread/id.rs b/crates/std/src/thread/id.rs deleted file mode 100644 index 3da0825..0000000 --- a/crates/std/src/thread/id.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::num::NonZero; -use crate::sync::atomic::{Atomic, Ordering}; - -/// A unique identifier for a running thread. -/// -/// A `ThreadId` is an opaque object that uniquely identifies each thread -/// created during the lifetime of a process. `ThreadId`s are guaranteed not to -/// be reused, even when a thread terminates. `ThreadId`s are under the control -/// of Rust's standard library and there may not be any relationship between -/// `ThreadId` and the underlying platform's notion of a thread identifier -- -/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId` -/// can be retrieved from the [`id`] method on a [`Thread`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let other_thread = thread::spawn(|| { -/// thread::current().id() -/// }); -/// -/// let other_thread_id = other_thread.join().unwrap(); -/// assert!(thread::current().id() != other_thread_id); -/// ``` -/// -/// [`Thread`]: super::Thread -/// [`id`]: super::Thread::id -#[stable(feature = "thread_id", since = "1.19.0")] -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(NonZero); - -impl ThreadId { - // Generate a new unique thread ID. - pub(crate) fn new() -> ThreadId { - #[cold] - fn exhausted() -> ! { - panic!("failed to generate unique thread ID: bitspace exhausted") - } - - cfg_select! { - target_has_atomic = "64" => { - use crate::sync::atomic::AtomicU64; - - static COUNTER: Atomic = AtomicU64::new(0); - - let mut last = COUNTER.load(Ordering::Relaxed); - loop { - let Some(id) = last.checked_add(1) else { - exhausted(); - }; - - match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - Ok(_) => return ThreadId(NonZero::new(id).unwrap()), - Err(id) => last = id, - } - } - } - _ => { - use crate::cell::SyncUnsafeCell; - use crate::hint::spin_loop; - use crate::sync::atomic::AtomicBool; - use crate::thread::yield_now; - - // If we don't have a 64-bit atomic we use a small spinlock. We don't use Mutex - // here as we might be trying to get the current thread id in the global allocator, - // and on some platforms Mutex requires allocation. - static COUNTER_LOCKED: Atomic = AtomicBool::new(false); - static COUNTER: SyncUnsafeCell = SyncUnsafeCell::new(0); - - // Acquire lock. - let mut spin = 0; - // Miri doesn't like it when we yield here as it interferes with deterministically - // scheduling threads, so avoid `compare_exchange_weak` to avoid spurious yields. - while COUNTER_LOCKED.swap(true, Ordering::Acquire) { - if spin <= 3 { - for _ in 0..(1 << spin) { - spin_loop(); - } - } else { - yield_now(); - } - spin += 1; - } - // This was `false` before the swap, so we got the lock. - - // SAFETY: we have an exclusive lock on the counter. - unsafe { - if let Some(id) = (*COUNTER.get()).checked_add(1) { - *COUNTER.get() = id; - COUNTER_LOCKED.store(false, Ordering::Release); - ThreadId(NonZero::new(id).unwrap()) - } else { - COUNTER_LOCKED.store(false, Ordering::Release); - exhausted() - } - } - } - } - } - - #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] - pub(super) fn from_u64(v: u64) -> Option { - NonZero::new(v).map(ThreadId) - } - - /// This returns a numeric identifier for the thread identified by this - /// `ThreadId`. - /// - /// As noted in the documentation for the type itself, it is essentially an - /// opaque ID, but is guaranteed to be unique for each thread. The returned - /// value is entirely opaque -- only equality testing is stable. Note that - /// it is not guaranteed which values new threads will return, and this may - /// change across Rust versions. - #[must_use] - #[unstable(feature = "thread_id_value", issue = "67939")] - pub fn as_u64(&self) -> NonZero { - self.0 - } -} diff --git a/crates/std/src/thread/join_handle.rs b/crates/std/src/thread/join_handle.rs deleted file mode 100644 index 93dcc63..0000000 --- a/crates/std/src/thread/join_handle.rs +++ /dev/null @@ -1,186 +0,0 @@ -use super::Result; -use super::lifecycle::JoinInner; -use super::thread::Thread; -use crate::fmt; -use crate::sys::{AsInner, IntoInner, thread as imp}; - -/// An owned permission to join on a thread (block on its termination). -/// -/// A `JoinHandle` *detaches* the associated thread when it is dropped, which -/// means that there is no longer any handle to the thread and no way to `join` -/// on it. -/// -/// Due to platform restrictions, it is not possible to [`Clone`] this -/// handle: the ability to join a thread is a uniquely-owned permission. -/// -/// This `struct` is created by the [`thread::spawn`] function and the -/// [`thread::Builder::spawn`] method. -/// -/// # Examples -/// -/// Creation from [`thread::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { -/// // some work here -/// }); -/// ``` -/// -/// Creation from [`thread::Builder::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { -/// // some work here -/// }).unwrap(); -/// ``` -/// -/// A thread being detached and outliving the thread that spawned it: -/// -/// ```no_run -/// use std::thread; -/// use std::time::Duration; -/// -/// let original_thread = thread::spawn(|| { -/// let _detached_thread = thread::spawn(|| { -/// // Here we sleep to make sure that the first thread returns before. -/// thread::sleep(Duration::from_millis(10)); -/// // This will be called, even though the JoinHandle is dropped. -/// println!("♫ Still alive ♫"); -/// }); -/// }); -/// -/// original_thread.join().expect("The thread being joined has panicked"); -/// println!("Original thread is joined."); -/// -/// // We make sure that the new thread has time to run, before the main -/// // thread returns. -/// -/// thread::sleep(Duration::from_millis(1000)); -/// ``` -/// -/// [`thread::Builder::spawn`]: super::Builder::spawn -/// [`thread::spawn`]: super::spawn -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(target_os = "teeos", must_use)] -pub struct JoinHandle(pub(super) JoinInner<'static, T>); - -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Send for JoinHandle {} -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - /// Extracts a handle to the underlying thread. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// - /// let thread = join_handle.thread(); - /// println!("thread id: {:?}", thread.id()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn thread(&self) -> &Thread { - self.0.thread() - } - - /// Waits for the associated thread to finish. - /// - /// This function will return immediately if the associated thread has already finished. - /// Otherwise, it fully waits for the thread to finish, including all destructors - /// for thread-local variables that might be running after the main function of the thread. - /// - /// In terms of [atomic memory orderings], the completion of the associated - /// thread synchronizes with this function returning. In other words, all - /// operations performed by that thread [happen - /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all - /// operations that happen after `join` returns. - /// - /// If the associated thread panics, [`Err`] is returned with the parameter given - /// to [`panic!`] (though see the Notes below). - /// - /// [`Err`]: crate::result::Result::Err - /// [atomic memory orderings]: crate::sync::atomic - /// - /// # Panics - /// - /// This function may panic on some platforms if a thread attempts to join - /// itself or otherwise may create a deadlock with joining threads. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// join_handle.join().expect("Couldn't join on the associated thread"); - /// ``` - /// - /// # Notes - /// - /// If a "foreign" unwinding operation (e.g. an exception thrown from C++ - /// code, or a `panic!` in Rust code compiled or linked with a different - /// runtime) unwinds all the way to the thread root, the process may be - /// aborted; see the Notes on [`thread::spawn`]. If the process is not - /// aborted, this function will return a `Result::Err` containing an opaque - /// type. - /// - /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html - /// [`thread::spawn`]: super::spawn - #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(self) -> Result { - self.0.join() - } - - /// Checks if the associated thread has finished running its main function. - /// - /// `is_finished` supports implementing a non-blocking join operation, by checking - /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To - /// block while waiting on the thread to finish, use [`join`][Self::join]. - /// - /// This might return `true` for a brief moment after the thread's main - /// function has returned, but before the thread itself has stopped running. - /// However, once this returns `true`, [`join`][Self::join] can be expected - /// to return quickly, without blocking for any significant amount of time. - #[stable(feature = "thread_is_running", since = "1.61.0")] - pub fn is_finished(&self) -> bool { - self.0.is_finished() - } -} - -impl AsInner for JoinHandle { - fn as_inner(&self) -> &imp::Thread { - self.0.as_inner() - } -} - -impl IntoInner for JoinHandle { - fn into_inner(self) -> imp::Thread { - self.0.into_inner() - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for JoinHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinHandle").finish_non_exhaustive() - } -} diff --git a/crates/std/src/thread/lifecycle.rs b/crates/std/src/thread/lifecycle.rs deleted file mode 100644 index 0bb1f34..0000000 --- a/crates/std/src/thread/lifecycle.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! The inner logic for thread spawning and joining. - -use super::current::set_current; -use super::id::ThreadId; -use super::scoped::ScopeData; -use super::thread::Thread; -use super::{Result, spawnhook}; -use crate::cell::UnsafeCell; -use crate::marker::PhantomData; -use crate::mem::{ManuallyDrop, MaybeUninit}; -use crate::sync::Arc; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sys::{AsInner, IntoInner, thread as imp}; -use crate::{env, io, panic}; - -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -pub(super) unsafe fn spawn_unchecked<'scope, F, T>( - name: Option, - stack_size: Option, - no_hooks: bool, - scope_data: Option>, - f: F, -) -> io::Result> -where - F: FnOnce() -> T, - F: Send, - T: Send, -{ - let stack_size = stack_size.unwrap_or_else(|| { - static MIN: Atomic = AtomicUsize::new(0); - - match MIN.load(Ordering::Relaxed) { - 0 => {} - n => return n - 1, - } - - let amt = env::var_os("RUST_MIN_STACK") - .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) - .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); - - // 0 is our sentinel value, so ensure that we'll never see 0 after - // initialization has run - MIN.store(amt + 1, Ordering::Relaxed); - amt - }); - - let id = ThreadId::new(); - let thread = Thread::new(id, name); - - let hooks = if no_hooks { - spawnhook::ChildSpawnHooks::default() - } else { - spawnhook::run_spawn_hooks(&thread) - }; - - let my_packet: Arc> = - Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData }); - let their_packet = my_packet.clone(); - - // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. - // See for more details. - // To prevent leaks we use a wrapper that drops its contents. - #[repr(transparent)] - struct MaybeDangling(MaybeUninit); - impl MaybeDangling { - fn new(x: T) -> Self { - MaybeDangling(MaybeUninit::new(x)) - } - fn into_inner(self) -> T { - // Make sure we don't drop. - let this = ManuallyDrop::new(self); - // SAFETY: we are always initialized. - unsafe { this.0.assume_init_read() } - } - } - impl Drop for MaybeDangling { - fn drop(&mut self) { - // SAFETY: we are always initialized. - unsafe { self.0.assume_init_drop() }; - } - } - - let f = MaybeDangling::new(f); - - // The entrypoint of the Rust thread, after platform-specific thread - // initialization is done. - let rust_start = move || { - let f = f.into_inner(); - let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); - crate::sys::backtrace::__rust_begin_short_backtrace(f) - })); - // SAFETY: `their_packet` as been built just above and moved by the - // closure (it is an Arc<...>) and `my_packet` will be stored in the - // same `JoinInner` as this closure meaning the mutation will be - // safe (not modify it and affect a value far away). - unsafe { *their_packet.result.get() = Some(try_result) }; - // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that - // will call `decrement_num_running_threads` and therefore signal that this thread is - // done. - drop(their_packet); - // Here, the lifetime `'scope` can end. `main` keeps running for a bit - // after that before returning itself. - }; - - if let Some(scope_data) = &my_packet.scope { - scope_data.increment_num_running_threads(); - } - - // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the - // lifetime change is justified. - let rust_start = unsafe { - let ptr = Box::into_raw(Box::new(rust_start)); - let ptr = crate::mem::transmute::< - *mut (dyn FnOnce() + Send + '_), - *mut (dyn FnOnce() + Send + 'static), - >(ptr); - Box::from_raw(ptr) - }; - - let init = Box::new(ThreadInit { handle: thread.clone(), rust_start }); - - Ok(JoinInner { - // SAFETY: - // - // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed - // through FFI or otherwise used with low-level threading primitives that have no - // notion of or way to enforce lifetimes. - // - // As mentioned in the `Safety` section of this function's documentation, the caller of - // this function needs to guarantee that the passed-in lifetime is sufficiently long - // for the lifetime of the thread. - // - // Similarly, the `sys` implementation must guarantee that no references to the closure - // exist after the thread has terminated, which is signaled by `Thread::join` - // returning. - native: unsafe { imp::Thread::new(stack_size, init)? }, - thread, - packet: my_packet, - }) -} - -/// The data passed to the spawned thread for thread initialization. Any thread -/// implementation should start a new thread by calling .init() on this before -/// doing anything else to ensure the current thread is properly initialized and -/// the global allocator works. -pub(crate) struct ThreadInit { - pub handle: Thread, - pub rust_start: Box, -} - -impl ThreadInit { - /// Initialize the 'current thread' mechanism on this thread, returning the - /// Rust entry point. - pub fn init(self: Box) -> Box { - // Set the current thread before any (de)allocations on the global allocator occur, - // so that it may call std::thread::current() in its implementation. This is also - // why we take Box, to ensure the Box is not destroyed until after this point. - // Cloning the handle does not invoke the global allocator, it is an Arc. - if let Err(_thread) = set_current(self.handle.clone()) { - // The current thread should not have set yet. Use an abort to save binary size (see #123356). - rtabort!("current thread handle already set during thread spawn"); - } - - if let Some(name) = self.handle.cname() { - imp::set_name(name); - } - - self.rust_start - } -} - -// This packet is used to communicate the return value between the spawned -// thread and the rest of the program. It is shared through an `Arc` and -// there's no need for a mutex here because synchronization happens with `join()` -// (the caller will never read this packet until the thread has exited). -// -// An Arc to the packet is stored into a `JoinInner` which in turns is placed -// in `JoinHandle`. -struct Packet<'scope, T> { - scope: Option>, - result: UnsafeCell>>, - _marker: PhantomData>, -} - -// Due to the usage of `UnsafeCell` we need to manually implement Sync. -// The type `T` should already always be Send (otherwise the thread could not -// have been created) and the Packet is Sync because all access to the -// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. -unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} - -impl<'scope, T> Drop for Packet<'scope, T> { - fn drop(&mut self) { - // If this packet was for a thread that ran in a scope, the thread - // panicked, and nobody consumed the panic payload, we make sure - // the scope function will panic. - let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); - // Drop the result without causing unwinding. - // This is only relevant for threads that aren't join()ed, as - // join() will take the `result` and set it to None, such that - // there is nothing left to drop here. - // If this panics, we should handle that, because we're outside the - // outermost `catch_unwind` of our thread. - // We just abort in that case, since there's nothing else we can do. - // (And even if we tried to handle it somehow, we'd also need to handle - // the case where the panic payload we get out of it also panics on - // drop, and so on. See issue #86027.) - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { - *self.result.get_mut() = None; - })) { - rtabort!("thread result panicked on drop"); - } - // Book-keeping so the scope knows when it's done. - if let Some(scope) = &self.scope { - // Now that there will be no more user code running on this thread - // that can use 'scope, mark the thread as 'finished'. - // It's important we only do this after the `result` has been dropped, - // since dropping it might still use things it borrowed from 'scope. - scope.decrement_num_running_threads(unhandled_panic); - } - } -} - -/// Inner representation for JoinHandle -pub(super) struct JoinInner<'scope, T> { - native: imp::Thread, - thread: Thread, - packet: Arc>, -} - -impl<'scope, T> JoinInner<'scope, T> { - pub(super) fn is_finished(&self) -> bool { - Arc::strong_count(&self.packet) == 1 - } - - pub(super) fn thread(&self) -> &Thread { - &self.thread - } - - pub(super) fn join(mut self) -> Result { - self.native.join(); - Arc::get_mut(&mut self.packet) - // FIXME(fuzzypixelz): returning an error instead of panicking here - // would require updating the documentation of - // `std::thread::Result`; currently we can return `Err` if and only - // if the thread had panicked. - .expect("threads should not terminate unexpectedly") - .result - .get_mut() - .take() - .unwrap() - } -} - -impl AsInner for JoinInner<'static, T> { - fn as_inner(&self) -> &imp::Thread { - &self.native - } -} - -impl IntoInner for JoinInner<'static, T> { - fn into_inner(self) -> imp::Thread { - self.native - } -} diff --git a/crates/std/src/thread/local.rs b/crates/std/src/thread/local.rs deleted file mode 100644 index 1318d8d..0000000 --- a/crates/std/src/thread/local.rs +++ /dev/null @@ -1,865 +0,0 @@ -//! Thread local storage - -#![unstable(feature = "thread_local_internals", issue = "none")] - -use crate::cell::{Cell, RefCell}; -use crate::error::Error; -use crate::fmt; - -/// A thread local storage (TLS) key which owns its contents. -/// -/// This key uses the fastest implementation available on the target platform. -/// It is instantiated with the [`thread_local!`] macro and the -/// primary method is the [`with`] method, though there are helpers to make -/// working with [`Cell`] types easier. -/// -/// The [`with`] method yields a reference to the contained value which cannot -/// outlive the current thread or escape the given closure. -/// -/// [`thread_local!`]: crate::thread_local -/// -/// # Initialization and Destruction -/// -/// Initialization is dynamically performed on the first call to a setter (e.g. -/// [`with`]) within a thread, and values that implement [`Drop`] get -/// destructed when a thread exits. Some platform-specific caveats apply, which -/// are explained below. -/// Note that, should the destructor panic, the whole process will be [aborted]. -/// On platforms where initialization requires memory allocation, this is -/// performed directly through [`System`], allowing the [global allocator] -/// to make use of thread local storage. -/// -/// A `LocalKey`'s initializer cannot recursively depend on itself. Using a -/// `LocalKey` in this way may cause panics, aborts, or infinite recursion on -/// the first call to `with`. -/// -/// [`System`]: crate::alloc::System -/// [global allocator]: crate::alloc -/// [aborted]: crate::process::abort -/// -/// # Single-thread Synchronization -/// -/// Though there is no potential race with other threads, it is still possible to -/// obtain multiple references to the thread-local data in different places on -/// the call stack. For this reason, only shared (`&T`) references may be obtained. -/// -/// To allow obtaining an exclusive mutable reference (`&mut T`), typically a -/// [`Cell`] or [`RefCell`] is used (see the [`std::cell`] for more information -/// on how exactly this works). To make this easier there are specialized -/// implementations for [`LocalKey>`] and [`LocalKey>`]. -/// -/// [`std::cell`]: `crate::cell` -/// [`LocalKey>`]: struct.LocalKey.html#impl-LocalKey> -/// [`LocalKey>`]: struct.LocalKey.html#impl-LocalKey> -/// -/// -/// # Examples -/// -/// ``` -/// use std::cell::Cell; -/// use std::thread; -/// -/// // explicit `const {}` block enables more efficient initialization -/// thread_local!(static FOO: Cell = const { Cell::new(1) }); -/// -/// assert_eq!(FOO.get(), 1); -/// FOO.set(2); -/// -/// // each thread starts out with the initial value of 1 -/// let t = thread::spawn(move || { -/// assert_eq!(FOO.get(), 1); -/// FOO.set(3); -/// }); -/// -/// // wait for the thread to complete and bail out on panic -/// t.join().unwrap(); -/// -/// // we retain our original value of 2 despite the child thread -/// assert_eq!(FOO.get(), 2); -/// ``` -/// -/// # Platform-specific behavior -/// -/// Note that a "best effort" is made to ensure that destructors for types -/// stored in thread local storage are run, but not all platforms can guarantee -/// that destructors will be run for all types in thread local storage. For -/// example, there are a number of known caveats where destructors are not run: -/// -/// 1. On Unix systems when pthread-based TLS is being used, destructors will -/// not be run for TLS values on the main thread when it exits. Note that the -/// application will exit immediately after the main thread exits as well. -/// 2. On all platforms it's possible for TLS to re-initialize other TLS slots -/// during destruction. Some platforms ensure that this cannot happen -/// infinitely by preventing re-initialization of any slot that has been -/// destroyed, but not all platforms have this guard. Those platforms that do -/// not guard typically have a synthetic limit after which point no more -/// destructors are run. -/// 3. When the process exits on Windows systems, TLS destructors may only be -/// run on the thread that causes the process to exit. This is because the -/// other threads may be forcibly terminated. -/// -/// ## Synchronization in thread-local destructors -/// -/// On Windows, synchronization operations (such as [`JoinHandle::join`]) in -/// thread local destructors are prone to deadlocks and so should be avoided. -/// This is because the [loader lock] is held while a destructor is run. The -/// lock is acquired whenever a thread starts or exits or when a DLL is loaded -/// or unloaded. Therefore these events are blocked for as long as a thread -/// local destructor is running. -/// -/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices -/// [`JoinHandle::join`]: crate::thread::JoinHandle::join -/// [`with`]: LocalKey::with -#[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct LocalKey { - // This outer `LocalKey` type is what's going to be stored in statics, - // but actual data inside will sometimes be tagged with #[thread_local]. - // It's not valid for a true static to reference a #[thread_local] static, - // so we get around that by exposing an accessor through a layer of function - // indirection (this thunk). - // - // Note that the thunk is itself unsafe because the returned lifetime of the - // slot where data lives, `'static`, is not actually valid. The lifetime - // here is actually slightly shorter than the currently running thread! - // - // Although this is an extra layer of indirection, it should in theory be - // trivially devirtualizable by LLVM because the value of `inner` never - // changes and the constant should be readonly within a crate. This mainly - // only runs into problems when TLS statics are exported across crates. - inner: fn(Option<&mut Option>) -> *const T, -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for LocalKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LocalKey").finish_non_exhaustive() - } -} - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals)] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semiopaque"] -pub macro thread_local_process_attrs { - - // Parse `cfg_attr` to figure out whether it's a `rustc_align_static`. - // Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested. - - // finished parsing the `cfg_attr`, it had no `rustc_align_static` - ( - [] [$(#[$($prev_other_attrs:tt)*])*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; - [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]]; - $($rest)* - ); - ), - - // finished parsing the `cfg_attr`, it had nothing but `rustc_align_static` - ( - [$(#[$($prev_align_attrs:tt)*])+] []; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; - [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*]; - $($rest)* - ); - ), - - // finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs - ( - [$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; - [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]]; - $($rest)* - ); - ), - - // it's a `rustc_align_static` - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(true, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(false, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - - // it's a nested `cfg_attr(..., ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's some other attribute - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - - // Separate attributes into `rustc_align_static` and everything else: - - // `rustc_align_static` attribute - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // `cfg_attr(true, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // `cfg_attr(false, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // `cfg_attr(..., ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+]; - $vis static $($rest)* - ); - ), - - // 8 lines of doc comment; process them all at once to avoid blowing recursion limit - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - #[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*] - #[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*] - $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)*] [$($prev_other_attrs)* - #[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*] - #[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]]; - $($rest)* - ); - ), - - // other attribute - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]]; - $($rest)* - ); - ), - - - // Delegate to `thread_local_inner` once attributes are fully categorized: - - // process `const` declaration and recurse - ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => ( - $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init); - - $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? - ), - - // process non-`const` declaration and recurse - ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => ( - $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init); - - $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? - ), -} - -/// Declare a new thread local storage key of type [`std::thread::LocalKey`]. -/// -/// # Syntax -/// -/// The macro wraps any number of static declarations and makes them thread local. -/// Publicity and attributes for each static are allowed. Example: -/// -/// ``` -/// use std::cell::{Cell, RefCell}; -/// -/// thread_local! { -/// pub static FOO: Cell = const { Cell::new(1) }; -/// -/// static BAR: RefCell> = RefCell::new(vec![1.0, 2.0]); -/// } -/// -/// assert_eq!(FOO.get(), 1); -/// BAR.with_borrow(|v| assert_eq!(v[1], 2.0)); -/// ``` -/// -/// Note that only shared references (`&T`) to the inner data may be obtained, so a -/// type such as [`Cell`] or [`RefCell`] is typically used to allow mutating access. -/// -/// This macro supports a special `const {}` syntax that can be used -/// when the initialization expression can be evaluated as a constant. -/// This can enable a more efficient thread local implementation that -/// can avoid lazy initialization. For types that do not -/// [need to be dropped][crate::mem::needs_drop], this can enable an -/// even more efficient implementation that does not need to -/// track any additional state. -/// -/// ``` -/// use std::cell::RefCell; -/// -/// thread_local! { -/// pub static FOO: RefCell> = const { RefCell::new(Vec::new()) }; -/// } -/// -/// FOO.with_borrow(|v| assert_eq!(v.len(), 0)); -/// ``` -/// -/// See [`LocalKey` documentation][`std::thread::LocalKey`] for more -/// information. -/// -/// [`std::thread::LocalKey`]: crate::thread::LocalKey -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] -#[allow_internal_unstable(thread_local_internals)] -macro_rules! thread_local { - () => {}; - - ($($tt:tt)+) => { - $crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+); - }; -} - -/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). -#[stable(feature = "thread_local_try_with", since = "1.26.0")] -#[non_exhaustive] -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct AccessError; - -#[stable(feature = "thread_local_try_with", since = "1.26.0")] -impl fmt::Debug for AccessError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AccessError").finish() - } -} - -#[stable(feature = "thread_local_try_with", since = "1.26.0")] -impl fmt::Display for AccessError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt("already destroyed", f) - } -} - -#[stable(feature = "thread_local_try_with", since = "1.26.0")] -impl Error for AccessError {} - -// This ensures the panicking code is outlined from `with` for `LocalKey`. -#[cfg_attr(not(panic = "immediate-abort"), inline(never))] -#[track_caller] -#[cold] -fn panic_access_error(err: AccessError) -> ! { - panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}") -} - -impl LocalKey { - #[doc(hidden)] - #[unstable( - feature = "thread_local_internals", - reason = "recently added to create a key", - issue = "none" - )] - pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { - LocalKey { inner } - } - - /// Acquires a reference to the value in this TLS key. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// This function will `panic!()` if the key currently has its - /// destructor running, and it **may** panic if the destructor has - /// previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// thread_local! { - /// pub static STATIC: String = String::from("I am"); - /// } - /// - /// assert_eq!( - /// STATIC.with(|original_value| format!("{original_value} initialized")), - /// "I am initialized", - /// ); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with(&'static self, f: F) -> R - where - F: FnOnce(&T) -> R, - { - match self.try_with(f) { - Ok(r) => r, - Err(err) => panic_access_error(err), - } - } - - /// Acquires a reference to the value in this TLS key. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. If the key has been destroyed (which may happen if this is called - /// in a destructor), this function will return an [`AccessError`]. - /// - /// # Panics - /// - /// This function will still `panic!()` if the key is uninitialized and the - /// key's initializer panics. - /// - /// # Examples - /// - /// ``` - /// thread_local! { - /// pub static STATIC: String = String::from("I am"); - /// } - /// - /// assert_eq!( - /// STATIC.try_with(|original_value| format!("{original_value} initialized")), - /// Ok(String::from("I am initialized")), - /// ); - /// ``` - #[stable(feature = "thread_local_try_with", since = "1.26.0")] - #[inline] - pub fn try_with(&'static self, f: F) -> Result - where - F: FnOnce(&T) -> R, - { - let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? }; - Ok(f(thread_local)) - } - - /// Acquires a reference to the value in this TLS key, initializing it with - /// `init` if it wasn't already initialized on this thread. - /// - /// If `init` was used to initialize the thread local variable, `None` is - /// passed as the first argument to `f`. If it was already initialized, - /// `Some(init)` is passed to `f`. - /// - /// # Panics - /// - /// This function will panic if the key currently has its destructor - /// running, and it **may** panic if the destructor has previously been run - /// for this thread. - fn initialize_with(&'static self, init: T, f: F) -> R - where - F: FnOnce(Option, &T) -> R, - { - let mut init = Some(init); - - let reference = unsafe { - match (self.inner)(Some(&mut init)).as_ref() { - Some(r) => r, - None => panic_access_error(AccessError), - } - }; - - f(init, reference) - } -} - -impl LocalKey> { - /// Sets or initializes the contained value. - /// - /// Unlike the other methods, this will *not* run the lazy initializer of - /// the thread local. Instead, it will be directly initialized with the - /// given value if it wasn't initialized yet. - /// - /// # Panics - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::Cell; - /// - /// thread_local! { - /// static X: Cell = panic!("!"); - /// } - /// - /// // Calling X.get() here would result in a panic. - /// - /// X.set(123); // But X.set() is fine, as it skips the initializer above. - /// - /// assert_eq!(X.get(), 123); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn set(&'static self, value: T) { - self.initialize_with(Cell::new(value), |value, cell| { - if let Some(value) = value { - // The cell was already initialized, so `value` wasn't used to - // initialize it. So we overwrite the current value with the - // new one instead. - cell.set(value.into_inner()); - } - }); - } - - /// Returns a copy of the contained value. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::Cell; - /// - /// thread_local! { - /// static X: Cell = const { Cell::new(1) }; - /// } - /// - /// assert_eq!(X.get(), 1); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn get(&'static self) -> T - where - T: Copy, - { - self.with(Cell::get) - } - - /// Takes the contained value, leaving `Default::default()` in its place. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::Cell; - /// - /// thread_local! { - /// static X: Cell> = const { Cell::new(Some(1)) }; - /// } - /// - /// assert_eq!(X.take(), Some(1)); - /// assert_eq!(X.take(), None); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn take(&'static self) -> T - where - T: Default, - { - self.with(Cell::take) - } - - /// Replaces the contained value, returning the old value. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::Cell; - /// - /// thread_local! { - /// static X: Cell = const { Cell::new(1) }; - /// } - /// - /// assert_eq!(X.replace(2), 1); - /// assert_eq!(X.replace(3), 2); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - #[rustc_confusables("swap")] - pub fn replace(&'static self, value: T) -> T { - self.with(|cell| cell.replace(value)) - } - - /// Updates the contained value using a function. - /// - /// # Examples - /// - /// ``` - /// #![feature(local_key_cell_update)] - /// use std::cell::Cell; - /// - /// thread_local! { - /// static X: Cell = const { Cell::new(5) }; - /// } - /// - /// X.update(|x| x + 1); - /// assert_eq!(X.get(), 6); - /// ``` - #[unstable(feature = "local_key_cell_update", issue = "143989")] - pub fn update(&'static self, f: impl FnOnce(T) -> T) - where - T: Copy, - { - self.with(|cell| cell.update(f)) - } -} - -impl LocalKey> { - /// Acquires a reference to the contained value. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the value is currently mutably borrowed. - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::RefCell; - /// - /// thread_local! { - /// static X: RefCell> = RefCell::new(Vec::new()); - /// } - /// - /// X.with_borrow(|v| assert!(v.is_empty())); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn with_borrow(&'static self, f: F) -> R - where - F: FnOnce(&T) -> R, - { - self.with(|cell| f(&cell.borrow())) - } - - /// Acquires a mutable reference to the contained value. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::RefCell; - /// - /// thread_local! { - /// static X: RefCell> = RefCell::new(Vec::new()); - /// } - /// - /// X.with_borrow_mut(|v| v.push(1)); - /// - /// X.with_borrow(|v| assert_eq!(*v, vec![1])); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn with_borrow_mut(&'static self, f: F) -> R - where - F: FnOnce(&mut T) -> R, - { - self.with(|cell| f(&mut cell.borrow_mut())) - } - - /// Sets or initializes the contained value. - /// - /// Unlike the other methods, this will *not* run the lazy initializer of - /// the thread local. Instead, it will be directly initialized with the - /// given value if it wasn't initialized yet. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::RefCell; - /// - /// thread_local! { - /// static X: RefCell> = panic!("!"); - /// } - /// - /// // Calling X.with() here would result in a panic. - /// - /// X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above. - /// - /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3])); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn set(&'static self, value: T) { - self.initialize_with(RefCell::new(value), |value, cell| { - if let Some(value) = value { - // The cell was already initialized, so `value` wasn't used to - // initialize it. So we overwrite the current value with the - // new one instead. - *cell.borrow_mut() = value.into_inner(); - } - }); - } - - /// Takes the contained value, leaving `Default::default()` in its place. - /// - /// This will lazily initialize the value if this thread has not referenced - /// this key yet. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::RefCell; - /// - /// thread_local! { - /// static X: RefCell> = RefCell::new(Vec::new()); - /// } - /// - /// X.with_borrow_mut(|v| v.push(1)); - /// - /// let a = X.take(); - /// - /// assert_eq!(a, vec![1]); - /// - /// X.with_borrow(|v| assert!(v.is_empty())); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - pub fn take(&'static self) -> T - where - T: Default, - { - self.with(RefCell::take) - } - - /// Replaces the contained value, returning the old value. - /// - /// # Panics - /// - /// Panics if the value is currently borrowed. - /// - /// Panics if the key currently has its destructor running, - /// and it **may** panic if the destructor has previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// use std::cell::RefCell; - /// - /// thread_local! { - /// static X: RefCell> = RefCell::new(Vec::new()); - /// } - /// - /// let prev = X.replace(vec![1, 2, 3]); - /// assert!(prev.is_empty()); - /// - /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3])); - /// ``` - #[stable(feature = "local_key_cell_methods", since = "1.73.0")] - #[rustc_confusables("swap")] - pub fn replace(&'static self, value: T) -> T { - self.with(|cell| cell.replace(value)) - } -} diff --git a/crates/std/src/thread/main_thread.rs b/crates/std/src/thread/main_thread.rs deleted file mode 100644 index 394074a..0000000 --- a/crates/std/src/thread/main_thread.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Store the ID of the main thread. -//! -//! The thread handle for the main thread is created lazily, and this might even -//! happen pre-main. Since not every platform has a way to identify the main -//! thread when that happens – macOS's `pthread_main_np` function being a notable -//! exception – we cannot assign it the right name right then. Instead, in our -//! runtime startup code, we remember the thread ID of the main thread (through -//! this modules `set` function) and use it to identify the main thread from then -//! on. This works reliably and has the additional advantage that we can report -//! the right thread name on main even after the thread handle has been destroyed. -//! Note however that this also means that the name reported in pre-main functions -//! will be incorrect, but that's just something we have to live with. - -cfg_select! { - target_has_atomic = "64" => { - use super::id::ThreadId; - use crate::sync::atomic::{Atomic, AtomicU64}; - use crate::sync::atomic::Ordering::Relaxed; - - static MAIN: Atomic = AtomicU64::new(0); - - pub(super) fn get() -> Option { - ThreadId::from_u64(MAIN.load(Relaxed)) - } - - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - MAIN.store(id.as_u64().get(), Relaxed) - } - } - _ => { - use super::id::ThreadId; - use crate::mem::MaybeUninit; - use crate::sync::atomic::{Atomic, AtomicBool}; - use crate::sync::atomic::Ordering::{Acquire, Release}; - - static INIT: Atomic = AtomicBool::new(false); - static mut MAIN: MaybeUninit = MaybeUninit::uninit(); - - pub(super) fn get() -> Option { - if INIT.load(Acquire) { - Some(unsafe { MAIN.assume_init() }) - } else { - None - } - } - - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - unsafe { MAIN = MaybeUninit::new(id) }; - INIT.store(true, Release); - } - } -} diff --git a/crates/std/src/thread/mod.rs b/crates/std/src/thread/mod.rs deleted file mode 100644 index 00aeb70..0000000 --- a/crates/std/src/thread/mod.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Native threads. -//! -//! ## The threading model -//! -//! An executing Rust program consists of a collection of native OS threads, -//! each with their own stack and local state. Threads can be named, and -//! provide some built-in support for low-level synchronization. -//! -//! Communication between threads can be done through -//! [channels], Rust's message-passing types, along with [other forms of thread -//! synchronization](../../std/sync/index.html) and shared-memory data -//! structures. In particular, types that are guaranteed to be -//! threadsafe are easily shared between threads using the -//! atomically-reference-counted container, [`Arc`]. -//! -//! Fatal logic errors in Rust cause *thread panic*, during which -//! a thread will unwind the stack, running destructors and freeing -//! owned resources. While not meant as a 'try/catch' mechanism, panics -//! in Rust can nonetheless be caught (unless compiling with `panic=abort`) with -//! [`catch_unwind`](../../std/panic/fn.catch_unwind.html) and recovered -//! from, or alternatively be resumed with -//! [`resume_unwind`](../../std/panic/fn.resume_unwind.html). If the panic -//! is not caught the thread will exit, but the panic may optionally be -//! detected from a different thread with [`join`]. If the main thread panics -//! without the panic being caught, the application will exit with a -//! non-zero exit code. -//! -//! When the main thread of a Rust program terminates, the entire program shuts -//! down, even if other threads are still running. However, this module provides -//! convenient facilities for automatically waiting for the termination of a -//! thread (i.e., join). -//! -//! ## Spawning a thread -//! -//! A new thread can be spawned using the [`thread::spawn`][`spawn`] function: -//! -//! ```rust -//! use std::thread; -//! -//! thread::spawn(move || { -//! // some work here -//! }); -//! ``` -//! -//! In this example, the spawned thread is "detached," which means that there is -//! no way for the program to learn when the spawned thread completes or otherwise -//! terminates. -//! -//! To learn when a thread completes, it is necessary to capture the [`JoinHandle`] -//! object that is returned by the call to [`spawn`], which provides -//! a `join` method that allows the caller to wait for the completion of the -//! spawned thread: -//! -//! ```rust -//! use std::thread; -//! -//! let thread_join_handle = thread::spawn(move || { -//! // some work here -//! }); -//! // some work here -//! let res = thread_join_handle.join(); -//! ``` -//! -//! The [`join`] method returns a [`thread::Result`] containing [`Ok`] of the final -//! value produced by the spawned thread, or [`Err`] of the value given to -//! a call to [`panic!`] if the thread panicked. -//! -//! Note that there is no parent/child relationship between a thread that spawns a -//! new thread and the thread being spawned. In particular, the spawned thread may or -//! may not outlive the spawning thread, unless the spawning thread is the main thread. -//! -//! ## Configuring threads -//! -//! A new thread can be configured before it is spawned via the [`Builder`] type, -//! which currently allows you to set the name and stack size for the thread: -//! -//! ```rust -//! # #![allow(unused_must_use)] -//! use std::thread; -//! -//! thread::Builder::new().name("thread1".to_string()).spawn(move || { -//! println!("Hello, world!"); -//! }); -//! ``` -//! -//! ## The `Thread` type -//! -//! Threads are represented via the [`Thread`] type, which you can get in one of -//! two ways: -//! -//! * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] -//! function, and calling [`thread`][`JoinHandle::thread`] on the [`JoinHandle`]. -//! * By requesting the current thread, using the [`thread::current`] function. -//! -//! The [`thread::current`] function is available even for threads not spawned -//! by the APIs of this module. -//! -//! ## Thread-local storage -//! -//! This module also provides an implementation of thread-local storage for Rust -//! programs. Thread-local storage is a method of storing data into a global -//! variable that each thread in the program will have its own copy of. -//! Threads do not share this data, so accesses do not need to be synchronized. -//! -//! A thread-local key owns the value it contains and will destroy the value when the -//! thread exits. It is created with the [`thread_local!`] macro and can contain any -//! value that is `'static` (no borrowed pointers). It provides an accessor function, -//! [`with`], that yields a shared reference to the value to the specified -//! closure. Thread-local keys allow only shared access to values, as there would be no -//! way to guarantee uniqueness if mutable borrows were allowed. Most values -//! will want to make use of some form of **interior mutability** through the -//! [`Cell`] or [`RefCell`] types. -//! -//! ## Naming threads -//! -//! Threads are able to have associated names for identification purposes. By default, spawned -//! threads are unnamed. To specify a name for a thread, build the thread with [`Builder`] and pass -//! the desired thread name to [`Builder::name`]. To retrieve the thread name from within the -//! thread, use [`Thread::name`]. A couple of examples where the name of a thread gets used: -//! -//! * If a panic occurs in a named thread, the thread name will be printed in the panic message. -//! * The thread name is provided to the OS where applicable (e.g., `pthread_setname_np` in -//! unix-like platforms). -//! -//! ## Stack size -//! -//! The default stack size is platform-dependent and subject to change. -//! Currently, it is 2 MiB on all Tier-1 platforms. -//! -//! There are two ways to manually specify the stack size for spawned threads: -//! -//! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`]. -//! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack -//! size (in bytes). Note that setting [`Builder::stack_size`] will override this. Be aware that -//! changes to `RUST_MIN_STACK` may be ignored after program start. -//! -//! Note that the stack size of the main thread is *not* determined by Rust. -//! -//! [channels]: crate::sync::mpsc -//! [`Arc`]: crate::sync::Arc -//! [`join`]: JoinHandle::join -//! [`Result`]: crate::result::Result -//! [`Ok`]: crate::result::Result::Ok -//! [`Err`]: crate::result::Result::Err -//! [`thread::current`]: current::current -//! [`thread::Result`]: Result -//! [`unpark`]: Thread::unpark -//! [`thread::park_timeout`]: park_timeout -//! [`Cell`]: crate::cell::Cell -//! [`RefCell`]: crate::cell::RefCell -//! [`with`]: LocalKey::with -//! [`thread_local!`]: crate::thread_local - -#![stable(feature = "rust1", since = "1.0.0")] -#![deny(unsafe_op_in_unsafe_fn)] -// Under `test`, `__FastLocalKeyInner` seems unused. -#![cfg_attr(test, allow(dead_code))] - -use crate::any::Any; - -#[macro_use] -mod local; -mod builder; -mod current; -mod functions; -mod id; -mod join_handle; -mod lifecycle; -mod scoped; -mod spawnhook; -mod thread; - -pub(crate) mod main_thread; - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use builder::Builder; -#[stable(feature = "rust1", since = "1.0.0")] -pub use current::current; -#[unstable(feature = "current_thread_id", issue = "147194")] -pub use current::current_id; -pub(crate) use current::{current_or_unnamed, current_os_id, drop_current, with_current_name}; -#[stable(feature = "available_parallelism", since = "1.59.0")] -pub use functions::available_parallelism; -#[stable(feature = "park_timeout", since = "1.4.0")] -pub use functions::park_timeout; -#[stable(feature = "thread_sleep", since = "1.4.0")] -pub use functions::sleep; -#[unstable(feature = "thread_sleep_until", issue = "113752")] -pub use functions::sleep_until; -#[expect(deprecated)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use functions::{panicking, park, park_timeout_ms, sleep_ms, spawn, yield_now}; -#[stable(feature = "thread_id", since = "1.19.0")] -pub use id::ThreadId; -#[stable(feature = "rust1", since = "1.0.0")] -pub use join_handle::JoinHandle; -pub(crate) use lifecycle::ThreadInit; -#[stable(feature = "rust1", since = "1.0.0")] -pub use local::{AccessError, LocalKey}; -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub use scoped::{Scope, ScopedJoinHandle, scope}; -#[unstable(feature = "thread_spawn_hook", issue = "132951")] -pub use spawnhook::add_spawn_hook; -#[stable(feature = "rust1", since = "1.0.0")] -pub use thread::Thread; - -// Implementation details used by the thread_local!{} macro. -#[doc(hidden)] -#[unstable(feature = "thread_local_internals", issue = "none")] -pub mod local_impl { - pub use super::local::thread_local_process_attrs; - pub use crate::sys::thread_local::*; -} - -/// A specialized [`Result`] type for threads. -/// -/// Indicates the manner in which a thread exited. -/// -/// The value contained in the `Result::Err` variant -/// is the value the thread panicked with; -/// that is, the argument the `panic!` macro was called with. -/// Unlike with normal errors, this value doesn't implement -/// the [`Error`](crate::error::Error) trait. -/// -/// Thus, a sensible way to handle a thread panic is to either: -/// -/// 1. propagate the panic with [`std::panic::resume_unwind`] -/// 2. or in case the thread is intended to be a subsystem boundary -/// that is supposed to isolate system-level failures, -/// match on the `Err` variant and handle the panic in an appropriate way -/// -/// A thread that completes without panicking is considered to exit successfully. -/// -/// # Examples -/// -/// Matching on the result of a joined thread: -/// -/// ```no_run -/// use std::{fs, thread, panic}; -/// -/// fn copy_in_thread() -> thread::Result<()> { -/// thread::spawn(|| { -/// fs::copy("foo.txt", "bar.txt").unwrap(); -/// }).join() -/// } -/// -/// fn main() { -/// match copy_in_thread() { -/// Ok(_) => println!("copy succeeded"), -/// Err(e) => panic::resume_unwind(e), -/// } -/// } -/// ``` -/// -/// [`Result`]: crate::result::Result -/// [`std::panic::resume_unwind`]: crate::panic::resume_unwind -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = crate::result::Result>; - -fn _assert_sync_and_send() { - fn _assert_both() {} - _assert_both::>(); - _assert_both::(); -} diff --git a/crates/std/src/thread/scoped.rs b/crates/std/src/thread/scoped.rs deleted file mode 100644 index 929f7fd..0000000 --- a/crates/std/src/thread/scoped.rs +++ /dev/null @@ -1,358 +0,0 @@ -use super::Result; -use super::builder::Builder; -use super::current::current_or_unnamed; -use super::lifecycle::{JoinInner, spawn_unchecked}; -use super::thread::Thread; -use crate::marker::PhantomData; -use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; -use crate::sync::Arc; -use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; -use crate::{fmt, io}; - -/// A scope to spawn scoped threads in. -/// -/// See [`scope`] for details. -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub struct Scope<'scope, 'env: 'scope> { - data: Arc, - /// Invariance over 'scope, to make sure 'scope cannot shrink, - /// which is necessary for soundness. - /// - /// Without invariance, this would compile fine but be unsound: - /// - /// ```compile_fail,E0373 - /// std::thread::scope(|s| { - /// s.spawn(|| { - /// let a = String::from("abcd"); - /// s.spawn(|| println!("{a:?}")); // might run after `a` is dropped - /// }); - /// }); - /// ``` - scope: PhantomData<&'scope mut &'scope ()>, - env: PhantomData<&'env mut &'env ()>, -} - -/// An owned permission to join on a scoped thread (block on its termination). -/// -/// See [`Scope::spawn`] for details. -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); - -pub(super) struct ScopeData { - num_running_threads: Atomic, - a_thread_panicked: Atomic, - main_thread: Thread, -} - -impl ScopeData { - pub(super) fn increment_num_running_threads(&self) { - // We check for 'overflow' with usize::MAX / 2, to make sure there's no - // chance it overflows to 0, which would result in unsoundness. - if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 { - // This can only reasonably happen by mem::forget()'ing a lot of ScopedJoinHandles. - self.overflow(); - } - } - - #[cold] - fn overflow(&self) { - self.decrement_num_running_threads(false); - panic!("too many running threads in thread scope"); - } - - pub(super) fn decrement_num_running_threads(&self, panic: bool) { - if panic { - self.a_thread_panicked.store(true, Ordering::Relaxed); - } - if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 { - self.main_thread.unpark(); - } - } -} - -/// Creates a scope for spawning scoped threads. -/// -/// The function passed to `scope` will be provided a [`Scope`] object, -/// through which scoped threads can be [spawned][`Scope::spawn`]. -/// -/// Unlike non-scoped threads, scoped threads can borrow non-`'static` data, -/// as the scope guarantees all threads will be joined at the end of the scope. -/// -/// All threads spawned within the scope that haven't been manually joined -/// will be automatically joined before this function returns. -/// However, note that joining will only wait for the main function of these threads to finish; even -/// when this function returns, destructors of thread-local variables in these threads might still -/// be running. -/// -/// # Panics -/// -/// If any of the automatically joined threads panicked, this function will panic. -/// -/// If you want to handle panics from spawned threads, -/// [`join`][ScopedJoinHandle::join] them before the end of the scope. -/// -/// # Example -/// -/// ``` -/// use std::thread; -/// -/// let mut a = vec![1, 2, 3]; -/// let mut x = 0; -/// -/// thread::scope(|s| { -/// s.spawn(|| { -/// println!("hello from the first scoped thread"); -/// // We can borrow `a` here. -/// dbg!(&a); -/// }); -/// s.spawn(|| { -/// println!("hello from the second scoped thread"); -/// // We can even mutably borrow `x` here, -/// // because no other threads are using it. -/// x += a[0] + a[2]; -/// }); -/// println!("hello from the main thread"); -/// }); -/// -/// // After the scope, we can modify and access our variables again: -/// a.push(4); -/// assert_eq!(x, a.len()); -/// ``` -/// -/// # Lifetimes -/// -/// Scoped threads involve two lifetimes: `'scope` and `'env`. -/// -/// The `'scope` lifetime represents the lifetime of the scope itself. -/// That is: the time during which new scoped threads may be spawned, -/// and also the time during which they might still be running. -/// Once this lifetime ends, all scoped threads are joined. -/// This lifetime starts within the `scope` function, before `f` (the argument to `scope`) starts. -/// It ends after `f` returns and all scoped threads have been joined, but before `scope` returns. -/// -/// The `'env` lifetime represents the lifetime of whatever is borrowed by the scoped threads. -/// This lifetime must outlast the call to `scope`, and thus cannot be smaller than `'scope`. -/// It can be as small as the call to `scope`, meaning that anything that outlives this call, -/// such as local variables defined right before the scope, can be borrowed by the scoped threads. -/// -/// The `'env: 'scope` bound is part of the definition of the `Scope` type. -#[track_caller] -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub fn scope<'env, F, T>(f: F) -> T -where - F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T, -{ - // We put the `ScopeData` into an `Arc` so that other threads can finish their - // `decrement_num_running_threads` even after this function returns. - let scope = Scope { - data: Arc::new(ScopeData { - num_running_threads: AtomicUsize::new(0), - main_thread: current_or_unnamed(), - a_thread_panicked: AtomicBool::new(false), - }), - env: PhantomData, - scope: PhantomData, - }; - - // Run `f`, but catch panics so we can make sure to wait for all the threads to join. - let result = catch_unwind(AssertUnwindSafe(|| f(&scope))); - - // Wait until all the threads are finished. - while scope.data.num_running_threads.load(Ordering::Acquire) != 0 { - // SAFETY: this is the main thread, the handle belongs to us. - unsafe { scope.data.main_thread.park() }; - } - - // Throw any panic from `f`, or the return value of `f` if no thread panicked. - match result { - Err(e) => resume_unwind(e), - Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => { - panic!("a scoped thread panicked") - } - Ok(result) => result, - } -} - -impl<'scope, 'env> Scope<'scope, 'env> { - /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it. - /// - /// Unlike non-scoped threads, threads spawned with this function may - /// borrow non-`'static` data from the outside the scope. See [`scope`] for - /// details. - /// - /// The join handle provides a [`join`] method that can be used to join the spawned - /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing - /// the panic payload. - /// - /// If the join handle is dropped, the spawned thread will be implicitly joined at the - /// end of the scope. In that case, if the spawned thread panics, [`scope`] will - /// panic after all threads are joined. - /// - /// This function creates a thread with the default parameters of [`Builder`]. - /// To specify the new thread's stack size or the name, use [`Builder::spawn_scoped`]. - /// - /// # Panics - /// - /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`] - /// to recover from such errors. - /// - /// [`join`]: ScopedJoinHandle::join - #[stable(feature = "scoped_threads", since = "1.63.0")] - pub fn spawn(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> - where - F: FnOnce() -> T + Send + 'scope, - T: Send + 'scope, - { - Builder::new().spawn_scoped(self, f).expect("failed to spawn thread") - } -} - -impl Builder { - /// Spawns a new scoped thread using the settings set through this `Builder`. - /// - /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to - /// capture any failure to create the thread at the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Example - /// - /// ``` - /// use std::thread; - /// - /// let mut a = vec![1, 2, 3]; - /// let mut x = 0; - /// - /// thread::scope(|s| { - /// thread::Builder::new() - /// .name("first".to_string()) - /// .spawn_scoped(s, || - /// { - /// println!("hello from the {:?} scoped thread", thread::current().name()); - /// // We can borrow `a` here. - /// dbg!(&a); - /// }) - /// .unwrap(); - /// thread::Builder::new() - /// .name("second".to_string()) - /// .spawn_scoped(s, || - /// { - /// println!("hello from the {:?} scoped thread", thread::current().name()); - /// // We can even mutably borrow `x` here, - /// // because no other threads are using it. - /// x += a[0] + a[2]; - /// }) - /// .unwrap(); - /// println!("hello from the main thread"); - /// }); - /// - /// // After the scope, we can modify and access our variables again: - /// a.push(4); - /// assert_eq!(x, a.len()); - /// ``` - #[stable(feature = "scoped_threads", since = "1.63.0")] - pub fn spawn_scoped<'scope, 'env, F, T>( - self, - scope: &'scope Scope<'scope, 'env>, - f: F, - ) -> io::Result> - where - F: FnOnce() -> T + Send + 'scope, - T: Send + 'scope, - { - let Builder { name, stack_size, no_hooks } = self; - Ok(ScopedJoinHandle(unsafe { - spawn_unchecked(name, stack_size, no_hooks, Some(scope.data.clone()), f) - }?)) - } -} - -impl<'scope, T> ScopedJoinHandle<'scope, T> { - /// Extracts a handle to the underlying thread. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// thread::scope(|s| { - /// let t = s.spawn(|| { - /// println!("hello"); - /// }); - /// println!("thread id: {:?}", t.thread().id()); - /// }); - /// ``` - #[must_use] - #[stable(feature = "scoped_threads", since = "1.63.0")] - pub fn thread(&self) -> &Thread { - self.0.thread() - } - - /// Waits for the associated thread to finish. - /// - /// This function will return immediately if the associated thread has already finished. - /// Otherwise, it fully waits for the thread to finish, including all destructors - /// for thread-local variables that might be running after the main function of the thread. - /// - /// In terms of [atomic memory orderings], the completion of the associated - /// thread synchronizes with this function returning. - /// In other words, all operations performed by that thread - /// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) - /// all operations that happen after `join` returns. - /// - /// If the associated thread panics, [`Err`] is returned with the panic payload. - /// - /// [atomic memory orderings]: crate::sync::atomic - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// thread::scope(|s| { - /// let t = s.spawn(|| { - /// panic!("oh no"); - /// }); - /// assert!(t.join().is_err()); - /// }); - /// ``` - #[stable(feature = "scoped_threads", since = "1.63.0")] - pub fn join(self) -> Result { - self.0.join() - } - - /// Checks if the associated thread has finished running its main function. - /// - /// `is_finished` supports implementing a non-blocking join operation, by checking - /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To - /// block while waiting on the thread to finish, use [`join`][Self::join]. - /// - /// This might return `true` for a brief moment after the thread's main - /// function has returned, but before the thread itself has stopped running. - /// However, once this returns `true`, [`join`][Self::join] can be expected - /// to return quickly, without blocking for any significant amount of time. - #[stable(feature = "scoped_threads", since = "1.63.0")] - pub fn is_finished(&self) -> bool { - self.0.is_finished() - } -} - -#[stable(feature = "scoped_threads", since = "1.63.0")] -impl fmt::Debug for Scope<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Scope") - .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed)) - .field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed)) - .field("main_thread", &self.data.main_thread) - .finish_non_exhaustive() - } -} - -#[stable(feature = "scoped_threads", since = "1.63.0")] -impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ScopedJoinHandle").finish_non_exhaustive() - } -} diff --git a/crates/std/src/thread/spawnhook.rs b/crates/std/src/thread/spawnhook.rs deleted file mode 100644 index 254793a..0000000 --- a/crates/std/src/thread/spawnhook.rs +++ /dev/null @@ -1,153 +0,0 @@ -use super::thread::Thread; -use crate::cell::Cell; -use crate::iter; -use crate::sync::Arc; - -crate::thread_local! { - /// A thread local linked list of spawn hooks. - /// - /// It is a linked list of Arcs, such that it can very cheaply be inherited by spawned threads. - /// - /// (That technically makes it a set of linked lists with shared tails, so a linked tree.) - static SPAWN_HOOKS: Cell = const { Cell::new(SpawnHooks { first: None }) }; -} - -#[derive(Default, Clone)] -struct SpawnHooks { - first: Option>, -} - -// Manually implement drop to prevent deep recursion when dropping linked Arc list. -impl Drop for SpawnHooks { - fn drop(&mut self) { - let mut next = self.first.take(); - while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) { - drop(hook); - next = n; - } - } -} - -struct SpawnHook { - hook: Box Box>, - next: Option>, -} - -/// Registers a function to run for every newly thread spawned. -/// -/// The hook is executed in the parent thread, and returns a function -/// that will be executed in the new thread. -/// -/// The hook is called with the `Thread` handle for the new thread. -/// -/// The hook will only be added for the current thread and is inherited by the threads it spawns. -/// In other words, adding a hook has no effect on already running threads (other than the current -/// thread) and the threads they might spawn in the future. -/// -/// Hooks can only be added, not removed. -/// -/// The hooks will run in reverse order, starting with the most recently added. -/// -/// # Usage -/// -/// ``` -/// #![feature(thread_spawn_hook)] -/// -/// std::thread::add_spawn_hook(|_| { -/// ..; // This will run in the parent (spawning) thread. -/// move || { -/// ..; // This will run it the child (spawned) thread. -/// } -/// }); -/// ``` -/// -/// # Example -/// -/// A spawn hook can be used to "inherit" a thread local from the parent thread: -/// -/// ``` -/// #![feature(thread_spawn_hook)] -/// -/// use std::cell::Cell; -/// -/// thread_local! { -/// static X: Cell = Cell::new(0); -/// } -/// -/// // This needs to be done once in the main thread before spawning any threads. -/// std::thread::add_spawn_hook(|_| { -/// // Get the value of X in the spawning thread. -/// let value = X.get(); -/// // Set the value of X in the newly spawned thread. -/// move || X.set(value) -/// }); -/// -/// X.set(123); -/// -/// std::thread::spawn(|| { -/// assert_eq!(X.get(), 123); -/// }).join().unwrap(); -/// ``` -#[unstable(feature = "thread_spawn_hook", issue = "132951")] -pub fn add_spawn_hook(hook: F) -where - F: 'static + Send + Sync + Fn(&Thread) -> G, - G: 'static + Send + FnOnce(), -{ - SPAWN_HOOKS.with(|h| { - let mut hooks = h.take(); - let next = hooks.first.take(); - hooks.first = Some(Arc::new(SpawnHook { - hook: Box::new(move |thread| Box::new(hook(thread))), - next, - })); - h.set(hooks); - }); -} - -/// Runs all the spawn hooks. -/// -/// Called on the parent thread. -/// -/// Returns the functions to be called on the newly spawned thread. -pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { - // Get a snapshot of the spawn hooks. - // (Increments the refcount to the first node.) - if let Ok(hooks) = SPAWN_HOOKS.try_with(|hooks| { - let snapshot = hooks.take(); - hooks.set(snapshot.clone()); - snapshot - }) { - // Iterate over the hooks, run them, and collect the results in a vector. - let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) - .map(|hook| (hook.hook)(thread)) - .collect(); - // Pass on the snapshot of the hooks and the results to the new thread, - // which will then run SpawnHookResults::run(). - ChildSpawnHooks { hooks, to_run } - } else { - // TLS has been destroyed. Skip running the hooks. - // See https://github.com/rust-lang/rust/issues/138696 - ChildSpawnHooks::default() - } -} - -/// The results of running the spawn hooks. -/// -/// This struct is sent to the new thread. -/// It contains the inherited hooks and the closures to be run. -#[derive(Default)] -pub(super) struct ChildSpawnHooks { - hooks: SpawnHooks, - to_run: Vec>, -} - -impl ChildSpawnHooks { - // This is run on the newly spawned thread, directly at the start. - pub(super) fn run(self) { - SPAWN_HOOKS.set(self.hooks); - for run in self.to_run { - run(); - } - } -} diff --git a/crates/std/src/thread/tests.rs b/crates/std/src/thread/tests.rs deleted file mode 100644 index 4b934c0..0000000 --- a/crates/std/src/thread/tests.rs +++ /dev/null @@ -1,427 +0,0 @@ -use crate::any::Any; -use crate::panic::panic_any; -use crate::result; -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::{Sender, channel}; -use crate::sync::{Arc, Barrier}; -use crate::thread::{self, Builder, Scope, ThreadId}; -use crate::time::{Duration, Instant}; - -// !!! These tests are dangerous. If something is buggy, they will hang, !!! -// !!! instead of exiting cleanly. This might wedge the buildbots. !!! - -#[test] -fn test_unnamed_thread() { - thread::spawn(move || { - assert!(thread::current().name().is_none()); - }) - .join() - .ok() - .expect("thread panicked"); -} - -#[test] -fn test_named_thread() { - Builder::new() - .name("ada lovelace".to_string()) - .spawn(move || { - assert!(thread::current().name().unwrap() == "ada lovelace".to_string()); - }) - .unwrap() - .join() - .unwrap(); -} - -#[cfg(any( - // Note: musl didn't add pthread_getname_np until 1.2.3 - all(target_os = "linux", target_env = "gnu"), - target_vendor = "apple", -))] -#[test] -fn test_named_thread_truncation() { - use crate::ffi::CStr; - - let long_name = crate::iter::once("test_named_thread_truncation") - .chain(crate::iter::repeat(" yada").take(100)) - .collect::(); - - let result = Builder::new().name(long_name.clone()).spawn(move || { - // Rust remembers the full thread name itself. - assert_eq!(thread::current().name(), Some(long_name.as_str())); - - // But the system is limited -- make sure we successfully set a truncation. - let mut buf = vec![0u8; long_name.len() + 1]; - unsafe { - libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()); - } - let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); - assert!(cstr.to_bytes().len() > 0); - assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); - }); - result.unwrap().join().unwrap(); -} - -#[test] -#[should_panic] -fn test_invalid_named_thread() { - let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {}); -} - -#[test] -fn test_run_basic() { - let (tx, rx) = channel(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); -} - -#[test] -fn test_is_finished() { - let b = Arc::new(Barrier::new(2)); - let t = thread::spawn({ - let b = b.clone(); - move || { - b.wait(); - 1234 - } - }); - - // Thread is definitely running here, since it's still waiting for the barrier. - assert_eq!(t.is_finished(), false); - - // Unblock the barrier. - b.wait(); - - // Now check that t.is_finished() becomes true within a reasonable time. - let start = Instant::now(); - while !t.is_finished() { - assert!(start.elapsed() < Duration::from_secs(2)); - thread::sleep(Duration::from_millis(15)); - } - - // Joining the thread should not block for a significant time now. - let join_time = Instant::now(); - assert_eq!(t.join().unwrap(), 1234); - assert!(join_time.elapsed() < Duration::from_secs(2)); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_join_panic() { - match thread::spawn(move || panic!()).join() { - result::Result::Err(_) => (), - result::Result::Ok(()) => panic!(), - } -} - -#[test] -fn test_spawn_sched() { - let (tx, rx) = channel(); - - fn f(i: i32, tx: Sender<()>) { - let tx = tx.clone(); - thread::spawn(move || { - if i == 0 { - tx.send(()).unwrap(); - } else { - f(i - 1, tx); - } - }); - } - f(10, tx); - rx.recv().unwrap(); -} - -#[test] -fn test_spawn_sched_childs_on_default_sched() { - let (tx, rx) = channel(); - - thread::spawn(move || { - thread::spawn(move || { - tx.send(()).unwrap(); - }); - }); - - rx.recv().unwrap(); -} - -fn avoid_copying_the_body(spawnfn: F) -where - F: FnOnce(Box), -{ - let (tx, rx) = channel(); - - let x: Box<_> = Box::new(1); - let x_in_parent = (&*x) as *const i32 as usize; - - spawnfn(Box::new(move || { - let x_in_child = (&*x) as *const i32 as usize; - tx.send(x_in_child).unwrap(); - })); - - let x_in_child = rx.recv().unwrap(); - assert_eq!(x_in_parent, x_in_child); -} - -#[test] -fn test_avoid_copying_the_body_spawn() { - avoid_copying_the_body(|v| { - thread::spawn(move || v()); - }); -} - -#[test] -fn test_avoid_copying_the_body_thread_spawn() { - avoid_copying_the_body(|f| { - thread::spawn(move || { - f(); - }); - }) -} - -#[test] -fn test_avoid_copying_the_body_join() { - avoid_copying_the_body(|f| { - let _ = thread::spawn(move || f()).join(); - }) -} - -#[test] -fn test_child_doesnt_ref_parent() { - // If the child refcounts the parent thread, this will stack overflow when - // climbing the thread tree to dereference each ancestor. (See #1789) - // (well, it would if the constant were 8000+ - I lowered it to be more - // valgrind-friendly. try this at home, instead..!) - const GENERATIONS: u32 = 16; - fn child_no(x: u32) -> Box { - return Box::new(move || { - if x < GENERATIONS { - thread::spawn(move || child_no(x + 1)()); - } - }); - } - thread::spawn(|| child_no(0)()); -} - -#[test] -fn test_simple_newsched_spawn() { - thread::spawn(move || {}); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_try_panic_message_string_literal() { - match thread::spawn(move || { - panic!("static string"); - }) - .join() - { - Err(e) => { - type T = &'static str; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "static string"); - } - Ok(()) => panic!(), - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_try_panic_any_message_owned_str() { - match thread::spawn(move || { - panic_any("owned string".to_string()); - }) - .join() - { - Err(e) => { - type T = String; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); - } - Ok(()) => panic!(), - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_try_panic_any_message_any() { - match thread::spawn(move || { - panic_any(Box::new(413u16) as Box); - }) - .join() - { - Err(e) => { - type T = Box; - assert!(e.is::()); - let any = e.downcast::().unwrap(); - assert!(any.is::()); - assert_eq!(*any.downcast::().unwrap(), 413); - } - Ok(()) => panic!(), - } -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_try_panic_any_message_unit_struct() { - struct Juju; - - match thread::spawn(move || panic_any(Juju)).join() { - Err(ref e) if e.is::() => {} - Err(_) | Ok(()) => panic!(), - } -} - -#[test] -fn test_park_unpark_before() { - for _ in 0..10 { - thread::current().unpark(); - thread::park(); - } -} - -#[test] -fn test_park_unpark_called_other_thread() { - for _ in 0..10 { - let th = thread::current(); - - // Here we rely on `thread::spawn` (specifically the part that runs after spawning - // the thread) to not consume the parking token. - let _guard = thread::spawn(move || { - super::sleep(Duration::from_millis(50)); - th.unpark(); - }); - - thread::park(); - } -} - -#[test] -fn test_park_timeout_unpark_before() { - for _ in 0..10 { - thread::current().unpark(); - thread::park_timeout(Duration::from_millis(u32::MAX as u64)); - } -} - -#[test] -fn test_park_timeout_unpark_not_called() { - for _ in 0..10 { - thread::park_timeout(Duration::from_millis(10)); - } -} - -#[test] -fn test_park_timeout_unpark_called_other_thread() { - for _ in 0..10 { - let th = thread::current(); - - // Here we rely on `thread::spawn` (specifically the part that runs after spawning - // the thread) to not consume the parking token. - let _guard = thread::spawn(move || { - super::sleep(Duration::from_millis(50)); - th.unpark(); - }); - - thread::park_timeout(Duration::from_millis(u32::MAX as u64)); - } -} - -#[test] -fn sleep_ms_smoke() { - thread::sleep(Duration::from_millis(2)); -} - -#[test] -fn test_size_of_option_thread_id() { - assert_eq!(size_of::>(), size_of::()); -} - -#[test] -fn test_thread_id_equal() { - assert!(thread::current().id() == thread::current().id()); -} - -#[test] -fn test_thread_id_not_equal() { - let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); - assert!(thread::current().id() != spawned_id); -} - -#[test] -fn test_thread_os_id_not_equal() { - let spawned_id = thread::spawn(|| thread::current_os_id()).join().unwrap(); - let current_id = thread::current_os_id(); - assert!(current_id != spawned_id); -} - -#[test] -fn test_scoped_threads_drop_result_before_join() { - let actually_finished = &AtomicBool::new(false); - struct X<'scope, 'env>(&'scope Scope<'scope, 'env>, &'env AtomicBool); - impl Drop for X<'_, '_> { - fn drop(&mut self) { - thread::sleep(Duration::from_millis(20)); - let actually_finished = self.1; - self.0.spawn(move || { - thread::sleep(Duration::from_millis(20)); - actually_finished.store(true, Ordering::Relaxed); - }); - } - } - thread::scope(|s| { - s.spawn(move || { - thread::sleep(Duration::from_millis(20)); - X(s, actually_finished) - }); - }); - assert!(actually_finished.load(Ordering::Relaxed)); -} - -#[test] -fn test_scoped_threads_nll() { - // this is mostly a *compilation test* for this exact function: - fn foo(x: &u8) { - thread::scope(|s| { - s.spawn(|| match x { - _ => (), - }); - }); - } - // let's also run it for good measure - let x = 42_u8; - foo(&x); -} - -// Regression test for https://github.com/rust-lang/rust/issues/98498. -#[test] -#[cfg(miri)] // relies on Miri's data race detector -fn scope_join_race() { - for _ in 0..100 { - let a_bool = AtomicBool::new(false); - - thread::scope(|s| { - for _ in 0..5 { - s.spawn(|| a_bool.load(Ordering::Relaxed)); - } - - for _ in 0..5 { - s.spawn(|| a_bool.load(Ordering::Relaxed)); - } - }); - } -} - -// Test that the smallest value for stack_size works on Windows. -#[cfg(windows)] -#[test] -fn test_minimal_thread_stack() { - use crate::sync::atomic::AtomicU8; - static COUNT: AtomicU8 = AtomicU8::new(0); - - let builder = thread::Builder::new().stack_size(1); - let before = builder.spawn(|| COUNT.fetch_add(1, Ordering::Relaxed)).unwrap().join().unwrap(); - assert_eq!(before, 0); - assert_eq!(COUNT.load(Ordering::Relaxed), 1); -} diff --git a/crates/std/src/thread/thread.rs b/crates/std/src/thread/thread.rs deleted file mode 100644 index 7c9c91c..0000000 --- a/crates/std/src/thread/thread.rs +++ /dev/null @@ -1,326 +0,0 @@ -use super::id::ThreadId; -use super::main_thread; -use crate::alloc::System; -use crate::ffi::CStr; -use crate::fmt; -use crate::pin::Pin; -use crate::sync::Arc; -use crate::sys::sync::Parker; -use crate::time::Duration; - -// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. -mod thread_name_string { - use crate::ffi::{CStr, CString}; - use crate::str; - - /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. - pub(crate) struct ThreadNameString { - inner: CString, - } - - impl From for ThreadNameString { - fn from(s: String) -> Self { - Self { - inner: CString::new(s).expect("thread name may not contain interior null bytes"), - } - } - } - - impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { - &self.inner - } - - pub fn as_str(&self) -> &str { - // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } -} - -use thread_name_string::ThreadNameString; - -/// The internal representation of a `Thread` handle -/// -/// We explicitly set the alignment for our guarantee in Thread::into_raw. This -/// allows applications to stuff extra metadata bits into the alignment, which -/// can be rather useful when working with atomics. -#[repr(align(8))] -struct Inner { - name: Option, - id: ThreadId, - parker: Parker, -} - -impl Inner { - fn parker(self: Pin<&Self>) -> Pin<&Parker> { - unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } - } -} - -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -/// A handle to a thread. -/// -/// Threads are represented via the `Thread` type, which you can get in one of -/// two ways: -/// -/// * By spawning a new thread, e.g., using the [`thread::spawn`] -/// function, and calling [`thread`] on the [`JoinHandle`]. -/// * By requesting the current thread, using the [`thread::current`] function. -/// -/// The [`thread::current`] function is available even for threads not spawned -/// by the APIs of this module. -/// -/// There is usually no need to create a `Thread` struct yourself, one -/// should instead use a function like `spawn` to create new threads, see the -/// docs of [`Builder`] and [`spawn`] for more details. -/// -/// [`thread::spawn`]: super::spawn -/// [`thread`]: super::JoinHandle::thread -/// [`JoinHandle`]: super::JoinHandle -/// [`thread::current`]: super::current::current -/// [`Builder`]: super::Builder -/// [`spawn`]: super::spawn -pub struct Thread { - // We use the System allocator such that creating or dropping this handle - // does not interfere with a potential Global allocator using thread-local - // storage. - inner: Pin>, -} - -impl Thread { - pub(crate) fn new(id: ThreadId, name: Option) -> Thread { - let name = name.map(ThreadNameString::from); - - // We have to use `unsafe` here to construct the `Parker` in-place, - // which is required for the UNIX implementation. - // - // SAFETY: We pin the Arc immediately after creation, so its address never - // changes. - let inner = unsafe { - let mut arc = Arc::::new_uninit_in(System); - let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); - (&raw mut (*ptr).name).write(name); - (&raw mut (*ptr).id).write(id); - Parker::new_in_place(&raw mut (*ptr).parker); - Pin::new_unchecked(arc.assume_init()) - }; - - Thread { inner } - } - - /// Like the public [`park`], but callable on any handle. This is used to - /// allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - /// - /// [`park`]: super::park - pub(crate) unsafe fn park(&self) { - unsafe { self.inner.as_ref().parker().park() } - } - - /// Like the public [`park_timeout`], but callable on any handle. This is - /// used to allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - /// - /// [`park_timeout`]: super::park_timeout - pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.inner.as_ref().parker().park_timeout(dur) } - } - - /// Atomically makes the handle's token available if it is not already. - /// - /// Every thread is equipped with some basic low-level blocking support, via - /// the [`park`] function and the `unpark()` method. These can be used as a - /// more CPU-efficient implementation of a spinlock. - /// - /// See the [park documentation] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::atomic::{AtomicBool, Ordering}; - /// - /// static QUEUED: AtomicBool = AtomicBool::new(false); - /// - /// let parked_thread = thread::Builder::new() - /// .spawn(|| { - /// println!("Parking thread"); - /// QUEUED.store(true, Ordering::Release); - /// thread::park(); - /// println!("Thread unparked"); - /// }) - /// .unwrap(); - /// - /// // Let some time pass for the thread to be spawned. - /// thread::sleep(Duration::from_millis(10)); - /// - /// // Wait until the other thread is queued. - /// // This is crucial! It guarantees that the `unpark` below is not consumed - /// // by some other code in the parked thread (e.g. inside `println!`). - /// while !QUEUED.load(Ordering::Acquire) { - /// // Spinning is of course inefficient; in practice, this would more likely be - /// // a dequeue where we have no work to do if there's nobody queued. - /// std::hint::spin_loop(); - /// } - /// - /// println!("Unpark the thread"); - /// parked_thread.thread().unpark(); - /// - /// parked_thread.join().unwrap(); - /// ``` - /// - /// [`park`]: super::park - /// [park documentation]: super::park - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn unpark(&self) { - self.inner.as_ref().parker().unpark(); - } - - /// Gets the thread's unique identifier. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let other_thread = thread::spawn(|| { - /// thread::current().id() - /// }); - /// - /// let other_thread_id = other_thread.join().unwrap(); - /// assert!(thread::current().id() != other_thread_id); - /// ``` - #[stable(feature = "thread_id", since = "1.19.0")] - #[must_use] - pub fn id(&self) -> ThreadId { - self.inner.id - } - - /// Gets the thread's name. - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// Threads by default have no name specified: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// assert!(thread::current().name().is_none()); - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// Thread with a specified name: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn name(&self) -> Option<&str> { - if let Some(name) = &self.inner.name { - Some(name.as_str()) - } else if main_thread::get() == Some(self.inner.id) { - Some("main") - } else { - None - } - } - - /// Consumes the `Thread`, returning a raw pointer. - /// - /// To avoid a memory leak the pointer must be converted - /// back into a `Thread` using [`Thread::from_raw`]. The pointer is - /// guaranteed to be aligned to at least 8 bytes. - /// - /// # Examples - /// - /// ``` - /// #![feature(thread_raw)] - /// - /// use std::thread::{self, Thread}; - /// - /// let thread = thread::current(); - /// let id = thread.id(); - /// let ptr = Thread::into_raw(thread); - /// unsafe { - /// assert_eq!(Thread::from_raw(ptr).id(), id); - /// } - /// ``` - #[unstable(feature = "thread_raw", issue = "97523")] - pub fn into_raw(self) -> *const () { - // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. - let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; - Arc::into_raw_with_allocator(inner).0 as *const () - } - - /// Constructs a `Thread` from a raw pointer. - /// - /// The raw pointer must have been previously returned - /// by a call to [`Thread::into_raw`]. - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead - /// to memory unsafety, even if the returned `Thread` is never - /// accessed. - /// - /// Creating a `Thread` from a pointer other than one returned - /// from [`Thread::into_raw`] is **undefined behavior**. - /// - /// Calling this function twice on the same raw pointer can lead - /// to a double-free if both `Thread` instances are dropped. - #[unstable(feature = "thread_raw", issue = "97523")] - pub unsafe fn from_raw(ptr: *const ()) -> Thread { - // Safety: Upheld by caller. - unsafe { - Thread { inner: Pin::new_unchecked(Arc::from_raw_in(ptr as *const Inner, System)) } - } - } - - pub(crate) fn cname(&self) -> Option<&CStr> { - if let Some(name) = &self.inner.name { - Some(name.as_cstr()) - } else if main_thread::get() == Some(self.inner.id) { - Some(c"main") - } else { - None - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Thread") - .field("id", &self.id()) - .field("name", &self.name()) - .finish_non_exhaustive() - } -} diff --git a/justfile b/justfile index 87c9cb8..b6f0a78 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,7 @@ release := "" qemu_flags := "" cargo_flags := "" + if release != "" { "--release" } else { "" } +KERNEL_FLAGS := "-Zbuild-std=core,compiler_builtins,alloc -Zbuild-std-features=compiler-builtins-mem" bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" } default: run @@ -13,15 +14,18 @@ sync_filesystem: sync update-std: - @cd crates/std && just update-std + @cd library/std && just update-std + +build-sysroot: + @cd library/std && just build-sysroot build_user_prog prog: - RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }} + RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie --sysroot {{ justfile_directory() / "sysroot" }}" cargo b {{ cargo_flags }} --package {{ prog }} riscv64-elf-strip {{ bin_path / prog }} cp {{ bin_path / prog }} {{ "mnt/usr/bin" / prog }} build: mount_filesystem (map_dir "user" f"just release=\"{{release}}\" cargo_flags=\"{{cargo_flags}}\" build_user_prog") - cargo b {{ cargo_flags }} + cargo b {{ cargo_flags }} {{ KERNEL_FLAGS }} just sync_filesystem run: build (runner f"{{bin_path / "kernel-rust"}}") @@ -56,4 +60,5 @@ runner args: {{ qemu }} -kernel {{ args }} clean: + cd library && just clean cargo clean diff --git a/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..3885a18 --- /dev/null +++ b/library/.gitignore @@ -0,0 +1,14 @@ +portable-simd +core_arch +core +compiler-builtins +alloc +stdarch +unwind +std_detect +panic_abort +panic_unwind +rustc-std-workspace-alloc +rustc-std-workspace-core +rustc-std-workspace-std +windows_link diff --git a/library/Cargo.toml b/library/Cargo.toml new file mode 100644 index 0000000..ab037e8 --- /dev/null +++ b/library/Cargo.toml @@ -0,0 +1,88 @@ + +cargo-features = ["profile-rustflags"] + +[workspace] +resolver = "1" +members = [ + "std", + # "sysroot", + # "coretests", + # "alloctests", +] + +exclude = [ + # stdarch has its own Cargo workspace + "stdarch", + "windows_link" +] + +[profile.release.package.compiler_builtins] +# For compiler-builtins we always use a high number of codegen units. +# The goal here is to place every single intrinsic into its own object +# file to avoid symbol clashes with the system libgcc if possible. Note +# that this number doesn't actually produce this many object files, we +# just don't create more than this number of object files. +# +# It's a bit of a bummer that we have to pass this here, unfortunately. +# Ideally this would be specified through an env var to Cargo so Cargo +# knows how many CGUs are for this specific crate, but for now +# per-crate configuration isn't specifiable in the environment. +codegen-units = 10000 + +# These dependencies of the standard library implement symbolication for +# backtraces on most platforms. Their debuginfo causes both linking to be slower +# (more data to chew through) and binaries to be larger without really all that +# much benefit. This section turns them all to down to have no debuginfo which +# helps to improve link times a little bit. +[profile.release.package] +addr2line.debug = 0 +addr2line.opt-level = "s" +adler2.debug = 0 +gimli.debug = 0 +gimli.opt-level = "s" +miniz_oxide.debug = 0 +miniz_oxide.opt-level = "s" +# `opt-level = "s"` for `object` led to a size regression when tried previously +object.debug = 0 +rustc-demangle.debug = 0 +rustc-demangle.opt-level = "s" + +# panic_abort must always be compiled with panic=abort, even when the rest of the +# sysroot is panic=unwind. +[profile.dev.package.panic_abort] +rustflags = ["-Cpanic=abort"] + +[profile.release.package.panic_abort] +rustflags = ["-Cpanic=abort"] + +# The "dist" profile is used by bootstrap for prebuilt libstd artifacts +# These settings ensure that the prebuilt artifacts support a variety of features +# in the user's profile. +[profile.dist] +inherits = "release" +codegen-units = 1 +debug = 1 # "limited" +rustflags = [ + # `profile.lto=off` implies `-Cembed-bitcode=no`, but unconditionally embedding + # bitcode is necessary for when users enable LTO. + # Required until Cargo can re-build the standard library based on the value + # of `profile.lto` in the user's profile. + "-Cembed-bitcode=yes", + # Enable frame pointers + "-Zunstable-options", + "-Cforce-frame-pointers=non-leaf", +] + +[profile.dist.package.panic_abort] +rustflags = [ + "-Cpanic=abort", + "-Cembed-bitcode=yes", + "-Zunstable-options", + "-Cforce-frame-pointers=non-leaf", +] + +[patch.crates-io] +# See comments in `library/rustc-std-workspace-core/README.md` for what's going on here +rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } +rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } +# rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } diff --git a/crates/std/crates/backtrace-rs b/library/backtrace similarity index 100% rename from crates/std/crates/backtrace-rs rename to library/backtrace diff --git a/crates/std/justfile b/library/justfile similarity index 51% rename from crates/std/justfile rename to library/justfile index 37d4512..294bbb4 100644 --- a/crates/std/justfile +++ b/library/justfile @@ -1,6 +1,6 @@ -SRC_DIR := "src" -PATCH_DIR := "patches" -RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src" +SRC_DIR := "std/src" +PATCH_DIR := "std/patches" +RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library" patch-std: @echo "Start patching the std..." @@ -22,23 +22,28 @@ update-std: cp_std path: @echo "Copying {{ path }}" - @mkdir {{ "src" / parent_directory(path) }} -p - @cp {{ RUST_SRC / path }} {{ "src" / path }} - @sed -i -f patches.sed {{ "src" / path }} + @mkdir {{ "std/src" / parent_directory(path) }} -p + @cp {{ RUST_SRC / "std/src" / path }} {{ "std/src" / path }} setup-std: - @mkdir "crates/std_detect" -p - @cp {{ RUST_SRC / "../../std_detect/*" }} {{ "crates/std_detect/" }} -r - # Remove the depency to core and alloc - @sed -i "19d" "crates/std_detect/Cargo.toml" - @sed -i "19d" "crates/std_detect/Cargo.toml" - - @mkdir "crates/panic_abort" -p - @cp {{ RUST_SRC / "../../panic_abort/*" }} {{ "crates/panic_abort/" }} -r - # Remove the depency to core and alloc - @sed -i "21d" "crates/panic_abort/Cargo.toml" - @sed -i "15d" "crates/panic_abort/Cargo.toml" + ln -fs {{ RUST_SRC / "std_detect" }} "." + ln -fs {{ RUST_SRC / "panic_abort" }} "." + ln -fs {{ RUST_SRC / "panic_unwind" }} "." + ln -fs {{ RUST_SRC / "windows_link" }} "." + ln -fs {{ RUST_SRC / "unwind" }} "." + ln -fs {{ RUST_SRC / "alloc" }} "." + ln -fs {{ RUST_SRC / "rustc-std-workspace-alloc" }} "." + ln -fs {{ RUST_SRC / "rustc-std-workspace-core" }} "." + ln -fs {{ RUST_SRC / "rustc-std-workspace-std" }} "." + ln -fs {{ RUST_SRC / "compiler-builtins" }} "." + ln -fs {{ RUST_SRC / "core" }} "." + ln -fs {{ RUST_SRC / "stdarch" }} "." + ln -fs {{ RUST_SRC / "portable-simd" }} "." + ln -fs {{ RUST_SRC / "proc_macro" }} "." + ln -fs {{ RUST_SRC / "profiler_builtins" }} "." + ln -fs {{ RUST_SRC / "test" }} "." + @# Complete @just cp_std "alloc.rs" @just cp_std "ascii.rs" @just cp_std "backtrace.rs" @@ -47,6 +52,7 @@ setup-std: @just cp_std "error.rs" @just cp_std "fs.rs" @just cp_std "keyword_docs.rs" + @just cp_std "lib.rs" @just cp_std "macros.rs" @just cp_std "panic.rs" @just cp_std "panicking.rs" @@ -59,76 +65,28 @@ setup-std: @just cp_std "time.rs" @# Complete - @just cp_std "backtrace/tests.rs" + ln -fs {{ RUST_SRC / "std/src/backtrace" }} "std/src/" @# Complete - @just cp_std "collections/hash/map/tests.rs" - @just cp_std "collections/hash/set/tests.rs" - @just cp_std "collections/hash/map.rs" - @just cp_std "collections/hash/set.rs" - @just cp_std "collections/hash/mod.rs" - @just cp_std "collections/mod.rs" + ln -fs {{ RUST_SRC / "std/src/collections" }} "std/src/" @# Complete - @just cp_std "ffi/os_str/tests.rs" - @just cp_std "ffi/c_str.rs" - @just cp_std "ffi/mod.rs" - @just cp_std "ffi/os_str.rs" + ln -fs {{ RUST_SRC / "std/src/ffi" }} "std/src/" @# Complete - @just cp_std "fs/tests.rs" + ln -fs {{ RUST_SRC / "std/src/fs" }} "std/src/" @# Complete - @just cp_std "hash/mod.rs" - @just cp_std "hash/random.rs" + ln -fs {{ RUST_SRC / "std/src/hash" }} "std/src/" @# Complete - @just cp_std "io/buffered/bufreader/buffer.rs" - @just cp_std "io/buffered/bufreader.rs" - @just cp_std "io/buffered/bufwriter.rs" - @just cp_std "io/buffered/linewriter.rs" - @just cp_std "io/buffered/linewritershim.rs" - @just cp_std "io/buffered/mod.rs" - @just cp_std "io/buffered/tests.rs" - @just cp_std "io/copy/tests.rs" - @just cp_std "io/cursor/tests.rs" - @just cp_std "io/error/repr_bitpacked.rs" - @just cp_std "io/error/repr_unpacked.rs" - @just cp_std "io/error/tests.rs" - @just cp_std "io/impls/tests.rs" - @just cp_std "io/pipe/tests.rs" - @just cp_std "io/stdio/tests.rs" - @just cp_std "io/util/tests.rs" - @just cp_std "io/copy.rs" - @just cp_std "io/cursor.rs" - @just cp_std "io/error.rs" - @just cp_std "io/impls.rs" - @just cp_std "io/mod.rs" - @just cp_std "io/pipe.rs" - @just cp_std "io/prelude.rs" - @just cp_std "io/stdio.rs" - @just cp_std "io/tests.rs" - @just cp_std "io/util.rs" + ln -fs {{ RUST_SRC / "std/src/io" }} "std/src/" @# Complete - @just cp_std "net/ip_addr/tests.rs" - @just cp_std "net/socket_addr/tests.rs" - @just cp_std "net/tcp/tests.rs" - @just cp_std "net/udp/tests.rs" - @just cp_std "net/hostname.rs" - @just cp_std "net/ip_addr.rs" - @just cp_std "net/mod.rs" - @just cp_std "net/socket_addr.rs" - @just cp_std "net/tcp.rs" - @just cp_std "net/test.rs" - @just cp_std "net/udp.rs" + ln -fs {{ RUST_SRC / "std/src/net" }} "std/src/" @# Complete - @just cp_std "num/mod.rs" - @just cp_std "num/f16.rs" - @just cp_std "num/f32.rs" - @just cp_std "num/f64.rs" - @just cp_std "num/f128.rs" + ln -fs {{ RUST_SRC / "std/src/num" }} "std/src/" @# Complete @just cp_std "os/raw/mod.rs" @@ -136,42 +94,13 @@ setup-std: @just cp_std "os/mod.rs" @# Complete - @just cp_std "prelude/mod.rs" - @just cp_std "prelude/v1.rs" + ln -fs {{ RUST_SRC / "std/src/prelude" }} "std/src/" @# Complete - @just cp_std "process/tests.rs" + ln -fs {{ RUST_SRC / "std/src/process" }} "std/src/" @# Complete - @just cp_std "sync/mpmc/array.rs" - @just cp_std "sync/mpmc/context.rs" - @just cp_std "sync/mpmc/counter.rs" - @just cp_std "sync/mpmc/error.rs" - @just cp_std "sync/mpmc/list.rs" - @just cp_std "sync/mpmc/mod.rs" - @just cp_std "sync/mpmc/select.rs" - @just cp_std "sync/mpmc/tests.rs" - @just cp_std "sync/mpmc/utils.rs" - @just cp_std "sync/mpmc/waker.rs" - @just cp_std "sync/mpmc/zero.rs" - @just cp_std "sync/nonpoison/condvar.rs" - @just cp_std "sync/nonpoison/mutex.rs" - @just cp_std "sync/nonpoison/rwlock.rs" - @just cp_std "sync/poison/condvar.rs" - @just cp_std "sync/poison/mutex.rs" - @just cp_std "sync/poison/rwlock.rs" - @just cp_std "sync/nonpoison.rs" - @just cp_std "sync/barrier.rs" - @just cp_std "sync/lazy_lock.rs" - @just cp_std "sync/mod.rs" - @just cp_std "sync/mpsc.rs" - @just cp_std "sync/nonpoison.rs" - @just cp_std "sync/once.rs" - @just cp_std "sync/once_lock.rs" - @just cp_std "sync/oneshot.rs" - @just cp_std "sync/poison.rs" - @just cp_std "sync/reentrant_lock.rs" - + ln -fs {{ RUST_SRC / "std/src/sync" }} "std/src/" @# Complete @just cp_std "sys/alloc/mod.rs" @@ -270,17 +199,44 @@ setup-std: @just cp_std "sys/mod.rs" @# Complete - @just cp_std "thread/builder.rs" - @just cp_std "thread/current.rs" - @just cp_std "thread/functions.rs" - @just cp_std "thread/id.rs" - @just cp_std "thread/join_handle.rs" - @just cp_std "thread/lifecycle.rs" - @just cp_std "thread/local.rs" - @just cp_std "thread/main_thread.rs" - @just cp_std "thread/mod.rs" - @just cp_std "thread/scoped.rs" - @just cp_std "thread/spawnhook.rs" - @just cp_std "thread/tests.rs" - @just cp_std "thread/thread.rs" - @# Copied but edited for the moment + ln -fs {{ RUST_SRC / "std/src/thread" }} "std/src/" + +build-sysroot: update-std + RUSTFLAGS="-Zforce-unstable-if-unmarked -C relocation-model=pic -C link-arg=-pie" cargo build --target ../riscv64.json + mkdir ../sysroot/lib/rustlib/riscv64/lib -p + rm ../sysroot/lib/rustlib/riscv64/lib/* -rf + cp target/riscv64/debug/deps/*.rlib ../sysroot/lib/rustlib/riscv64/lib + +clean: + cargo clean + rm ../sysroot/lib/rustlib/riscv64/lib/* -rf + + rm -f alloc + rm -f compiler-builtins + rm -f panic_abort + rm -f panic_unwind + rm -f windows_link + rm -f portable-simd + rm -f unwind + rm -f std_detect + rm -f stdarch + rm -f rustc-std-workspace-alloc + rm -f rustc-std-workspace-core + rm -f proc_macro + rm -f profiler_builtins + rm -f test + + rm -rf std/src/backtrace + rm -rf std/src/thread + rm -rf std/src/sync + rm -rf std/src/collections + rm -rf std/src/ffi + rm -rf std/src/fs + rm -rf std/src/num + rm -rf std/src/net + rm -rf std/src/io + rm -rf std/src/hash + rm -rf std/src/prelude + rm -rf std/src/process + + # rm -f Cargo.toml diff --git a/library/proc_macro b/library/proc_macro new file mode 120000 index 0000000..0762ea5 --- /dev/null +++ b/library/proc_macro @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/proc_macro \ No newline at end of file diff --git a/library/profiler_builtins b/library/profiler_builtins new file mode 120000 index 0000000..79c75e5 --- /dev/null +++ b/library/profiler_builtins @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/profiler_builtins \ No newline at end of file diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml new file mode 100644 index 0000000..388d917 --- /dev/null +++ b/library/std/Cargo.toml @@ -0,0 +1,177 @@ +cargo-features = ["public-dependency"] + +[package] +name = "std" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library" +edition = "2024" +autobenches = false + +[lib] +crate-type = ["dylib", "rlib"] + +[dependencies] +alloc = { path = "../alloc", public = true } +# std no longer uses cfg-if directly, but the included copy of backtrace does. +cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } +core = { path = "../core", public = true } +unwind = { path = "../unwind" } +hashbrown = { version = "0.16.1", default-features = false, features = [ + 'rustc-dep-of-std', +] } +std_detect = { path = "../std_detect", public = true } + +# Dependencies of the `backtrace` crate +rustc-demangle = { version = "0.1.27", features = ['rustc-dep-of-std'] } + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] +miniz_oxide = { version = "0.8.0", optional = true, default-features = false } +addr2line = { version = "0.25.0", optional = true, default-features = false } + +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +libc = { version = "0.2.178", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } + +[target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] +object = { version = "0.37.1", default-features = false, optional = true, features = [ + 'read_core', + 'elf', + 'macho', + 'pe', + 'unaligned', + 'archive', +] } + +[target.'cfg(target_os = "aix")'.dependencies] +object = { version = "0.37.1", default-features = false, optional = true, features = [ + 'read_core', + 'xcoff', + 'unaligned', + 'archive', +] } + +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-link] +path = "../windows_link" + +[dev-dependencies] +rand = { version = "0.9.0", default-features = false, features = ["alloc"] } +rand_xorshift = "0.4.0" + +[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] } + +[target.x86_64-fortanix-unknown-sgx.dependencies] +fortanix-sgx-abi = { version = "0.6.1", features = [ + 'rustc-dep-of-std', +], public = true } + +[target.'cfg(target_os = "motor")'.dependencies] +moto-rt = { version = "0.16", features = ['rustc-dep-of-std'], public = true } + +[target.'cfg(target_os = "hermit")'.dependencies] +hermit-abi = { version = "0.5.0", features = [ + 'rustc-dep-of-std', +], public = true } + +[target.'cfg(all(target_os = "wasi", target_env = "p1"))'.dependencies] +wasi = { version = "0.11.0", features = [ + 'rustc-dep-of-std', +], default-features = false } + +[target.'cfg(all(target_os = "wasi", target_env = "p2"))'.dependencies] +wasip2 = { version = '0.14.4', features = [ + 'rustc-dep-of-std', +], default-features = false, package = 'wasi' } + +[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies] +wasip2 = { version = '0.14.4', features = [ + 'rustc-dep-of-std', +], default-features = false, package = 'wasi' } + +[target.'cfg(target_os = "uefi")'.dependencies] +r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } +r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } + +[target.'cfg(target_os = "vexos")'.dependencies] +vex-sdk = { version = "0.27.0", features = [ + 'rustc-dep-of-std', +], default-features = false } + +[features] +default = ["compiler-builtins-mem"] +backtrace = [ + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', +] +# Disable symbolization in backtraces. For use with -Zbuild-std. +# FIXME: Ideally this should be an additive backtrace-symbolization feature +backtrace-trace-only = [] + +panic-unwind = ["dep:panic_unwind"] +compiler-builtins-c = ["alloc/compiler-builtins-c"] +compiler-builtins-mem = ["alloc/compiler-builtins-mem"] +llvm-libunwind = ["unwind/llvm-libunwind"] +system-llvm-libunwind = ["unwind/system-llvm-libunwind"] + +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] + +# Make `RefCell` store additional debugging information, which is printed out when +# a borrow error occurs +debug_refcell = ["core/debug_refcell"] + +llvm_enzyme = ["core/llvm_enzyme"] + +# Enable using raw-dylib for Windows imports. +# This will eventually be the default. +windows_raw_dylib = ["windows-link/windows_raw_dylib"] + +[package.metadata.fortanix-sgx] +# Maximum possible number of threads when testing +threads = 125 +# Maximum heap size +heap_size = 0x8000000 + +[[test]] +name = "pipe-subprocess" +path = "tests/pipe_subprocess.rs" +harness = false + +[[test]] +name = "sync" +path = "tests/sync/lib.rs" + +[[test]] +name = "thread_local" +path = "tests/thread_local/lib.rs" + +[[bench]] +name = "stdbenches" +path = "benches/lib.rs" +test = true + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # std use #[path] imports to portable-simd `std_float` crate + # and to the `backtrace` crate which messes-up with Cargo list + # of declared features, we therefor expect any feature cfg + 'cfg(feature, values(any()))', + # Internal features aren't marked known config by default, we use these to + # gate tests. + 'cfg(target_has_reliable_f16)', + 'cfg(target_has_reliable_f16_math)', + 'cfg(target_has_reliable_f128)', + 'cfg(target_has_reliable_f128_math)', +] + + + +# shared = { path = "../shared", features = ["user"] } +# io_crate = { package = "io", path = "../io", features = ["alloc_crate"] } diff --git a/crates/std/build.rs b/library/std/build.rs similarity index 100% rename from crates/std/build.rs rename to library/std/build.rs diff --git a/crates/std/patches/sys/args/mod.sed b/library/std/patches/sys/args/mod.sed similarity index 100% rename from crates/std/patches/sys/args/mod.sed rename to library/std/patches/sys/args/mod.sed diff --git a/crates/std/patches/sys/io/error/mod.sed b/library/std/patches/sys/io/error/mod.sed similarity index 100% rename from crates/std/patches/sys/io/error/mod.sed rename to library/std/patches/sys/io/error/mod.sed diff --git a/library/std/patches/sys/pal/mod.sed b/library/std/patches/sys/pal/mod.sed new file mode 100644 index 0000000..acd2d76 --- /dev/null +++ b/library/std/patches/sys/pal/mod.sed @@ -0,0 +1,6 @@ +62a \ target_os = "survos" => { \ + mod unsupported; \ + pub use self::unsupported::*; \ + mod survos; \ + pub use self::survos::*; \ + } diff --git a/crates/std/patches/sys/random/mod.sed b/library/std/patches/sys/random/mod.sed similarity index 100% rename from crates/std/patches/sys/random/mod.sed rename to library/std/patches/sys/random/mod.sed diff --git a/crates/std/patches/sys/thread_local/mod.sed b/library/std/patches/sys/thread_local/mod.sed similarity index 100% rename from crates/std/patches/sys/thread_local/mod.sed rename to library/std/patches/sys/thread_local/mod.sed diff --git a/library/std/riscv64.json b/library/std/riscv64.json new file mode 100644 index 0000000..1801bfe --- /dev/null +++ b/library/std/riscv64.json @@ -0,0 +1,22 @@ +{ + "llvm-target": "riscv64", + "llvm-abiname": "lp64", + "abi": "lp64", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + "target-endian": "little", + "target-pointer-width": 64, + "arch": "riscv64", + "os": "survos", + "vendor": "unknown", + "env": "", + "features": "+i,+m,+a,+zicsr", + "linker": "ld.lld", + "linker-flavor": "ld", + "executables": true, + "panic-strategy": "abort", + "relocation-model": "static", + "disable-redzone": true, + "emit-debug-gdb-scripts": false, + "eh-frame-header": false, + "code-model": "medium" +} diff --git a/crates/std/src/alloc.rs b/library/std/src/alloc.rs similarity index 100% rename from crates/std/src/alloc.rs rename to library/std/src/alloc.rs diff --git a/crates/std/src/ascii.rs b/library/std/src/ascii.rs similarity index 100% rename from crates/std/src/ascii.rs rename to library/std/src/ascii.rs diff --git a/library/std/src/backtrace b/library/std/src/backtrace new file mode 120000 index 0000000..5f2b4b9 --- /dev/null +++ b/library/std/src/backtrace @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/backtrace \ No newline at end of file diff --git a/crates/std/src/backtrace.rs b/library/std/src/backtrace.rs similarity index 100% rename from crates/std/src/backtrace.rs rename to library/std/src/backtrace.rs diff --git a/crates/std/src/bstr.rs b/library/std/src/bstr.rs similarity index 70% rename from crates/std/src/bstr.rs rename to library/std/src/bstr.rs index 3f96d02..dd49177 100644 --- a/crates/std/src/bstr.rs +++ b/library/std/src/bstr.rs @@ -1,4 +1,4 @@ //! The `ByteStr` and `ByteString` types and trait implementations. #[unstable(feature = "bstr", issue = "134915")] -pub use alloc_crate::bstr::{ByteStr, ByteString}; +pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/library/std/src/collections b/library/std/src/collections new file mode 120000 index 0000000..832b356 --- /dev/null +++ b/library/std/src/collections @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/collections \ No newline at end of file diff --git a/crates/std/src/env.rs b/library/std/src/env.rs similarity index 100% rename from crates/std/src/env.rs rename to library/std/src/env.rs diff --git a/crates/std/src/error.rs b/library/std/src/error.rs similarity index 99% rename from crates/std/src/error.rs rename to library/std/src/error.rs index 895d43f..def5f98 100644 --- a/crates/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,4 +1,4 @@ - // todo retreive docs +#![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/ffi b/library/std/src/ffi new file mode 120000 index 0000000..8140a6f --- /dev/null +++ b/library/std/src/ffi @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/ffi \ No newline at end of file diff --git a/library/std/src/fs b/library/std/src/fs new file mode 120000 index 0000000..85172c0 --- /dev/null +++ b/library/std/src/fs @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/fs \ No newline at end of file diff --git a/crates/std/src/fs.rs b/library/std/src/fs.rs similarity index 100% rename from crates/std/src/fs.rs rename to library/std/src/fs.rs diff --git a/library/std/src/hash b/library/std/src/hash new file mode 120000 index 0000000..58875b8 --- /dev/null +++ b/library/std/src/hash @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/hash \ No newline at end of file diff --git a/library/std/src/io b/library/std/src/io new file mode 120000 index 0000000..dcda1d7 --- /dev/null +++ b/library/std/src/io @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io \ No newline at end of file diff --git a/crates/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs similarity index 100% rename from crates/std/src/keyword_docs.rs rename to library/std/src/keyword_docs.rs diff --git a/crates/std/src/lib.rs b/library/std/src/lib.rs similarity index 78% rename from crates/std/src/lib.rs rename to library/std/src/lib.rs index 085206e..6fcb28e 100644 --- a/crates/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -236,7 +236,7 @@ // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind -// #![needs_panic_runtime] +#![needs_panic_runtime] // // Lints: #![warn(deprecated_in_future)] @@ -255,10 +255,7 @@ #![allow(unused_features)] // // Features: -#![cfg_attr( - test, - feature(internal_output_capture, print_internals, update_panic_count, rt) -)] +#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) @@ -386,7 +383,7 @@ // // Library features (unwind): // tidy-alphabetical-start -// #![feature(panic_unwind)] +#![feature(panic_unwind)] // tidy-alphabetical-end // // Library features (std_detect): @@ -437,6 +434,18 @@ extern crate test; #[macro_use] extern crate alloc as alloc_crate; +// Many compiler tests depend on libc being pulled in by std +// so include it here even if it's unused. +#[doc(masked)] +#[allow(unused_extern_crates)] +#[cfg(not(all(windows, target_env = "msvc")))] +extern crate libc; + +// We always need an unwinder currently for backtraces +#[doc(masked)] +#[allow(unused_extern_crates)] +extern crate unwind; + // FIXME: #94122 this extern crate definition only exist here to stop // miniz_oxide docs leaking into std docs. Find better way to do it. // Remove exclusion from tidy platform check when this removed. @@ -605,39 +614,145 @@ pub mod random; pub mod sync; pub mod time; -#[unstable(feature = "custom_std", issue = "none")] -#[rustc_std_internal_symbol] -pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -> u32 { - todo!() +// Pull in `std_float` crate into std. The contents of +// `std_float` are in a different repository: rust-lang/portable-simd. +#[path = "../../portable-simd/crates/std_float/src/lib.rs"] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn)] +#[allow(rustdoc::bare_urls)] +#[unstable(feature = "portable_simd", issue = "86656")] +mod std_float; + +#[unstable(feature = "portable_simd", issue = "86656")] +pub mod simd { + #![doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")] + + #[doc(inline)] + pub use core::simd::*; + + #[doc(inline)] + pub use crate::std_float::StdFloat; } -#[unstable(feature = "custom_std", issue = "none")] -pub use core::format_args_nl; -#[unstable(feature = "custom_std", issue = "none")] -pub use core::unimplemented; -#[unstable(feature = "custom_std", issue = "none")] -pub use std_detect::is_x86_feature_detected; -pub mod alloc; -#[unstable(feature = "custom_std", issue = "none")] -pub mod panicking; -#[unstable(feature = "custom_std", issue = "none")] -pub mod sys; +#[unstable(feature = "autodiff", issue = "124509")] +/// This module provides support for automatic differentiation. +pub mod autodiff { + /// This macro handles automatic differentiation. + pub use core::autodiff::{autodiff_forward, autodiff_reverse}; +} -#[path = "../crates/backtrace-rs/src/lib.rs"] -#[allow( - dead_code, - unused_attributes, - fuzzy_provenance_casts, - unsafe_op_in_unsafe_fn -)] -#[unstable(feature = "custom_std", issue = "none")] +#[stable(feature = "futures_api", since = "1.36.0")] +pub mod task { + //! Types and Traits for working with asynchronous tasks. + + #[doc(inline)] + #[stable(feature = "wake_trait", since = "1.51.0")] + pub use alloc::task::*; + #[doc(inline)] + #[stable(feature = "futures_api", since = "1.36.0")] + pub use core::task::*; +} + +#[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] +#[stable(feature = "simd_arch", since = "1.27.0")] +pub mod arch { + #[stable(feature = "simd_arch", since = "1.27.0")] + // The `no_inline`-attribute is required to make the documentation of all + // targets available. + // See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for + // more information. + #[doc(no_inline)] // Note (#82861): required for correct documentation + pub use core::arch::*; + + #[stable(feature = "simd_aarch64", since = "1.60.0")] + pub use std_detect::is_aarch64_feature_detected; + #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] + pub use std_detect::is_arm_feature_detected; + #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")] + pub use std_detect::is_loongarch_feature_detected; + #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] + pub use std_detect::is_riscv_feature_detected; + #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] + pub use std_detect::is_s390x_feature_detected; + #[stable(feature = "simd_x86", since = "1.27.0")] + pub use std_detect::is_x86_feature_detected; + #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] + pub use std_detect::{is_mips_feature_detected, is_mips64_feature_detected}; + #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] + pub use std_detect::{is_powerpc_feature_detected, is_powerpc64_feature_detected}; +} + +// This was stabilized in the crate root so we have to keep it there. +#[stable(feature = "simd_x86", since = "1.27.0")] +pub use std_detect::is_x86_feature_detected; + +mod sys; + +pub mod alloc; + +// Private support modules +mod panicking; + +#[path = "../../backtrace/src/lib.rs"] +#[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; -#[prelude_import] -#[allow(unused_imports)] -pub use prelude::rust_2024::*; +#[stable(feature = "cfg_select", since = "1.95.0")] +pub use core::cfg_select; +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +pub use core::concat_bytes; +#[stable(feature = "matches_macro", since = "1.42.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::matches; +#[stable(feature = "core_primitive", since = "1.43.0")] +pub use core::primitive; +#[stable(feature = "todo_macro", since = "1.40.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::todo; +// Re-export built-in macros defined through core. +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use core::{ + assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, +}; +// Re-export macros defined in core. +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::{ + assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, + unreachable, write, writeln, +}; +#[stable(feature = "assert_matches", since = "1.95.0")] +pub use core::{assert_matches, debug_assert_matches}; + +// Re-export unstable derive macro defined through core. +#[unstable(feature = "derive_from", issue = "144889")] +/// Unstable module containing the unstable `From` derive macro. +pub mod from { + #[unstable(feature = "derive_from", issue = "144889")] + pub use core::from::From; +} + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("../../core/src/primitive_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// This is required to avoid an unstable error when `restricted-std` is not +// enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std +// is unconditional, so the unstable feature needs to be defined somewhere. +#[unstable(feature = "restricted_std", issue = "none")] +mod __restricted_std_workaround {} -#[allow(unused)] mod sealed { /// This trait being unreachable from outside the crate /// prevents outside implementations of our extension traits. @@ -646,55 +761,6 @@ mod sealed { pub trait Sealed {} } -#[unstable(feature = "custom_std", issue = "none")] -pub use shared::fs as other_fs; -#[unstable(feature = "custom_std", issue = "none")] -pub use shared::syscall; - -use crate::io::Stdin; - -// #[macro_export] -// macro_rules! print { -// ($($args:expr),*) => { -// $crate::syscall::write_string_temp(&format!($($args),*)) -// }; -// } -// #[macro_export] -// macro_rules! println { -// () => { -// $crate::print!(""); -// // $crate::print!("\n\r"); -// }; -// ($($args:expr),*) => { -// $crate::print!($($args),*); -// // $crate::println!(); -// }; -// } - -#[allow(ineffective_unstable_trait_impl)] -#[unstable(feature = "custom_std", issue = "none")] -impl io_crate::IoBase for Stdin { - type Error = (); -} - -#[allow(ineffective_unstable_trait_impl)] -#[unstable(feature = "custom_std", issue = "none")] -impl io_crate::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> core::result::Result { - unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) } - } -} -#[global_allocator] -static GLOBAL_ALLOCATOR: crate::alloc::System = crate::alloc::System; - -/// # Safety -/// `argc` and `argv` are passed by the kernel -#[unsafe(no_mangle)] -#[stable(feature = "rust1", since = "1.0.0")] -pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize { - unsafe extern "C" { - fn main(argc: isize, argv: *const *const u8) -> isize; - } - - unsafe { main(argc, argv) } -} +#[cfg(test)] +#[allow(dead_code)] // Not used in all configurations. +pub(crate) mod test_helpers; diff --git a/crates/std/src/macros.rs b/library/std/src/macros.rs similarity index 99% rename from crates/std/src/macros.rs rename to library/std/src/macros.rs index 9618cdc..0bb1455 100644 --- a/crates/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -5,7 +5,7 @@ //! library. // ignore-tidy-dbg - // todo retreive docs +#[doc = include_str!("../../core/src/macros/panic.md")] #[macro_export] #[rustc_builtin_macro(std_panic)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/net b/library/std/src/net new file mode 120000 index 0000000..703e90d --- /dev/null +++ b/library/std/src/net @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/net \ No newline at end of file diff --git a/library/std/src/num b/library/std/src/num new file mode 120000 index 0000000..dc799d6 --- /dev/null +++ b/library/std/src/num @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/num \ No newline at end of file diff --git a/crates/std/src/os/mod.rs b/library/std/src/os/mod.rs similarity index 100% rename from crates/std/src/os/mod.rs rename to library/std/src/os/mod.rs diff --git a/crates/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs similarity index 84% rename from crates/std/src/os/raw/mod.rs rename to library/std/src/os/raw/mod.rs index 680d553..5b302e3 100644 --- a/crates/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -8,7 +8,7 @@ mod tests; macro_rules! alias_core_ffi { ($($t:ident)*) => {$( #[stable(feature = "raw_os", since = "1.1.0")] - // todo retreive docs + #[doc = include_str!(concat!("../../../../core/src/ffi/", stringify!($t), ".md"))] #[doc(cfg(all()))] pub type $t = core::ffi::$t; )*} diff --git a/crates/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs similarity index 100% rename from crates/std/src/os/raw/tests.rs rename to library/std/src/os/raw/tests.rs diff --git a/crates/std/src/panic.rs b/library/std/src/panic.rs similarity index 100% rename from crates/std/src/panic.rs rename to library/std/src/panic.rs diff --git a/crates/std/src/panicking.rs b/library/std/src/panicking.rs similarity index 100% rename from crates/std/src/panicking.rs rename to library/std/src/panicking.rs diff --git a/crates/std/src/pat.rs b/library/std/src/pat.rs similarity index 100% rename from crates/std/src/pat.rs rename to library/std/src/pat.rs diff --git a/crates/std/src/path.rs b/library/std/src/path.rs similarity index 100% rename from crates/std/src/path.rs rename to library/std/src/path.rs diff --git a/library/std/src/prelude b/library/std/src/prelude new file mode 120000 index 0000000..054dced --- /dev/null +++ b/library/std/src/prelude @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude \ No newline at end of file diff --git a/library/std/src/process b/library/std/src/process new file mode 120000 index 0000000..6e85d33 --- /dev/null +++ b/library/std/src/process @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/process \ No newline at end of file diff --git a/crates/std/src/process.rs b/library/std/src/process.rs similarity index 100% rename from crates/std/src/process.rs rename to library/std/src/process.rs diff --git a/crates/std/src/random.rs b/library/std/src/random.rs similarity index 100% rename from crates/std/src/random.rs rename to library/std/src/random.rs diff --git a/crates/std/src/rt.rs b/library/std/src/rt.rs similarity index 100% rename from crates/std/src/rt.rs rename to library/std/src/rt.rs diff --git a/library/std/src/sync b/library/std/src/sync new file mode 120000 index 0000000..5a71d24 --- /dev/null +++ b/library/std/src/sync @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sync \ No newline at end of file diff --git a/crates/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs similarity index 100% rename from crates/std/src/sys/args/mod.rs rename to library/std/src/sys/args/mod.rs diff --git a/crates/std/src/sys/args/unsupported.rs b/library/std/src/sys/args/unsupported.rs similarity index 100% rename from crates/std/src/sys/args/unsupported.rs rename to library/std/src/sys/args/unsupported.rs diff --git a/crates/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs similarity index 100% rename from crates/std/src/sys/backtrace.rs rename to library/std/src/sys/backtrace.rs diff --git a/crates/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs similarity index 100% rename from crates/std/src/sys/cmath.rs rename to library/std/src/sys/cmath.rs diff --git a/crates/std/src/sys/configure_builtins.rs b/library/std/src/sys/configure_builtins.rs similarity index 100% rename from crates/std/src/sys/configure_builtins.rs rename to library/std/src/sys/configure_builtins.rs diff --git a/crates/std/src/sys/env/common.rs b/library/std/src/sys/env/common.rs similarity index 100% rename from crates/std/src/sys/env/common.rs rename to library/std/src/sys/env/common.rs diff --git a/crates/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs similarity index 100% rename from crates/std/src/sys/env/mod.rs rename to library/std/src/sys/env/mod.rs diff --git a/crates/std/src/sys/env/unsupported.rs b/library/std/src/sys/env/unsupported.rs similarity index 100% rename from crates/std/src/sys/env/unsupported.rs rename to library/std/src/sys/env/unsupported.rs diff --git a/crates/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs similarity index 100% rename from crates/std/src/sys/env_consts.rs rename to library/std/src/sys/env_consts.rs diff --git a/crates/std/src/sys/exit.rs b/library/std/src/sys/exit.rs similarity index 100% rename from crates/std/src/sys/exit.rs rename to library/std/src/sys/exit.rs diff --git a/crates/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs similarity index 100% rename from crates/std/src/sys/fd/mod.rs rename to library/std/src/sys/fd/mod.rs diff --git a/crates/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs similarity index 100% rename from crates/std/src/sys/fs/common.rs rename to library/std/src/sys/fs/common.rs diff --git a/crates/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs similarity index 100% rename from crates/std/src/sys/fs/mod.rs rename to library/std/src/sys/fs/mod.rs diff --git a/crates/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs similarity index 100% rename from crates/std/src/sys/fs/unsupported.rs rename to library/std/src/sys/fs/unsupported.rs diff --git a/crates/std/src/sys/helpers/mod.rs b/library/std/src/sys/helpers/mod.rs similarity index 100% rename from crates/std/src/sys/helpers/mod.rs rename to library/std/src/sys/helpers/mod.rs diff --git a/crates/std/src/sys/helpers/small_c_string.rs b/library/std/src/sys/helpers/small_c_string.rs similarity index 100% rename from crates/std/src/sys/helpers/small_c_string.rs rename to library/std/src/sys/helpers/small_c_string.rs diff --git a/crates/std/src/sys/helpers/tests.rs b/library/std/src/sys/helpers/tests.rs similarity index 100% rename from crates/std/src/sys/helpers/tests.rs rename to library/std/src/sys/helpers/tests.rs diff --git a/crates/std/src/sys/helpers/wstr.rs b/library/std/src/sys/helpers/wstr.rs similarity index 100% rename from crates/std/src/sys/helpers/wstr.rs rename to library/std/src/sys/helpers/wstr.rs diff --git a/crates/std/src/sys/io/error/generic.rs b/library/std/src/sys/io/error/generic.rs similarity index 100% rename from crates/std/src/sys/io/error/generic.rs rename to library/std/src/sys/io/error/generic.rs diff --git a/crates/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs similarity index 100% rename from crates/std/src/sys/io/error/mod.rs rename to library/std/src/sys/io/error/mod.rs diff --git a/crates/std/src/sys/io/io_slice/unsupported.rs b/library/std/src/sys/io/io_slice/unsupported.rs similarity index 100% rename from crates/std/src/sys/io/io_slice/unsupported.rs rename to library/std/src/sys/io/io_slice/unsupported.rs diff --git a/crates/std/src/sys/io/is_terminal/unsupported.rs b/library/std/src/sys/io/is_terminal/unsupported.rs similarity index 100% rename from crates/std/src/sys/io/is_terminal/unsupported.rs rename to library/std/src/sys/io/is_terminal/unsupported.rs diff --git a/crates/std/src/sys/io/kernel_copy/mod.rs b/library/std/src/sys/io/kernel_copy/mod.rs similarity index 100% rename from crates/std/src/sys/io/kernel_copy/mod.rs rename to library/std/src/sys/io/kernel_copy/mod.rs diff --git a/crates/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs similarity index 100% rename from crates/std/src/sys/io/mod.rs rename to library/std/src/sys/io/mod.rs diff --git a/crates/std/src/sys/mod.rs b/library/std/src/sys/mod.rs similarity index 100% rename from crates/std/src/sys/mod.rs rename to library/std/src/sys/mod.rs diff --git a/crates/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs similarity index 100% rename from crates/std/src/sys/net/connection/mod.rs rename to library/std/src/sys/net/connection/mod.rs diff --git a/crates/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs similarity index 100% rename from crates/std/src/sys/net/connection/unsupported.rs rename to library/std/src/sys/net/connection/unsupported.rs diff --git a/crates/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs similarity index 100% rename from crates/std/src/sys/net/hostname/mod.rs rename to library/std/src/sys/net/hostname/mod.rs diff --git a/crates/std/src/sys/net/hostname/unsupported.rs b/library/std/src/sys/net/hostname/unsupported.rs similarity index 100% rename from crates/std/src/sys/net/hostname/unsupported.rs rename to library/std/src/sys/net/hostname/unsupported.rs diff --git a/crates/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs similarity index 100% rename from crates/std/src/sys/net/mod.rs rename to library/std/src/sys/net/mod.rs diff --git a/crates/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs similarity index 100% rename from crates/std/src/sys/os_str/bytes.rs rename to library/std/src/sys/os_str/bytes.rs diff --git a/crates/std/src/sys/os_str/bytes/tests.rs b/library/std/src/sys/os_str/bytes/tests.rs similarity index 100% rename from crates/std/src/sys/os_str/bytes/tests.rs rename to library/std/src/sys/os_str/bytes/tests.rs diff --git a/crates/std/src/sys/os_str/mod.rs b/library/std/src/sys/os_str/mod.rs similarity index 100% rename from crates/std/src/sys/os_str/mod.rs rename to library/std/src/sys/os_str/mod.rs diff --git a/crates/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs similarity index 90% rename from crates/std/src/sys/pal/mod.rs rename to library/std/src/sys/pal/mod.rs index 88d9d42..dadd85b 100644 --- a/crates/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -60,6 +60,12 @@ cfg_select! { mod zkvm; pub use self::zkvm::*; } + target_os = "survos" => { + mod unsupported; + pub use self::unsupported::*; + mod survos; + pub use self::survos::*; + } _ => { mod unsupported; pub use self::unsupported::*; diff --git a/library/std/src/sys/pal/survos.rs b/library/std/src/sys/pal/survos.rs new file mode 100644 index 0000000..e68ea1d --- /dev/null +++ b/library/std/src/sys/pal/survos.rs @@ -0,0 +1,11 @@ +/// # Safety +/// `argc` and `argv` are passed by the kernel +#[unsafe(no_mangle)] +#[stable(feature = "rust1", since = "1.0.0")] +pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize { + unsafe extern "C" { + fn main(argc: isize, argv: *const *const u8) -> isize; + } + + unsafe { main(argc, argv) } +} diff --git a/crates/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs similarity index 100% rename from crates/std/src/sys/pal/unsupported/common.rs rename to library/std/src/sys/pal/unsupported/common.rs diff --git a/crates/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs similarity index 100% rename from crates/std/src/sys/pal/unsupported/mod.rs rename to library/std/src/sys/pal/unsupported/mod.rs diff --git a/crates/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs similarity index 100% rename from crates/std/src/sys/pal/unsupported/os.rs rename to library/std/src/sys/pal/unsupported/os.rs diff --git a/crates/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs similarity index 100% rename from crates/std/src/sys/path/mod.rs rename to library/std/src/sys/path/mod.rs diff --git a/crates/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs similarity index 100% rename from crates/std/src/sys/path/unix.rs rename to library/std/src/sys/path/unix.rs diff --git a/crates/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs similarity index 100% rename from crates/std/src/sys/personality/dwarf/eh.rs rename to library/std/src/sys/personality/dwarf/eh.rs diff --git a/crates/std/src/sys/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs similarity index 100% rename from crates/std/src/sys/personality/dwarf/mod.rs rename to library/std/src/sys/personality/dwarf/mod.rs diff --git a/crates/std/src/sys/personality/dwarf/tests.rs b/library/std/src/sys/personality/dwarf/tests.rs similarity index 100% rename from crates/std/src/sys/personality/dwarf/tests.rs rename to library/std/src/sys/personality/dwarf/tests.rs diff --git a/crates/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs similarity index 100% rename from crates/std/src/sys/personality/mod.rs rename to library/std/src/sys/personality/mod.rs diff --git a/crates/std/src/sys/pipe/mod.rs b/library/std/src/sys/pipe/mod.rs similarity index 100% rename from crates/std/src/sys/pipe/mod.rs rename to library/std/src/sys/pipe/mod.rs diff --git a/crates/std/src/sys/pipe/unsupported.rs b/library/std/src/sys/pipe/unsupported.rs similarity index 100% rename from crates/std/src/sys/pipe/unsupported.rs rename to library/std/src/sys/pipe/unsupported.rs diff --git a/crates/std/src/sys/platform_version/mod.rs b/library/std/src/sys/platform_version/mod.rs similarity index 100% rename from crates/std/src/sys/platform_version/mod.rs rename to library/std/src/sys/platform_version/mod.rs diff --git a/crates/std/src/sys/process/env.rs b/library/std/src/sys/process/env.rs similarity index 100% rename from crates/std/src/sys/process/env.rs rename to library/std/src/sys/process/env.rs diff --git a/crates/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs similarity index 100% rename from crates/std/src/sys/process/mod.rs rename to library/std/src/sys/process/mod.rs diff --git a/crates/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs similarity index 100% rename from crates/std/src/sys/process/unsupported.rs rename to library/std/src/sys/process/unsupported.rs diff --git a/crates/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs similarity index 100% rename from crates/std/src/sys/random/mod.rs rename to library/std/src/sys/random/mod.rs diff --git a/crates/std/src/sys/random/unsupported.rs b/library/std/src/sys/random/unsupported.rs similarity index 100% rename from crates/std/src/sys/random/unsupported.rs rename to library/std/src/sys/random/unsupported.rs diff --git a/crates/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs similarity index 100% rename from crates/std/src/sys/stdio/mod.rs rename to library/std/src/sys/stdio/mod.rs diff --git a/crates/std/src/sys/stdio/unsupported.rs b/library/std/src/sys/stdio/unsupported.rs similarity index 100% rename from crates/std/src/sys/stdio/unsupported.rs rename to library/std/src/sys/stdio/unsupported.rs diff --git a/crates/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs similarity index 100% rename from crates/std/src/sys/sync/condvar/mod.rs rename to library/std/src/sys/sync/condvar/mod.rs diff --git a/crates/std/src/sys/sync/condvar/no_threads.rs b/library/std/src/sys/sync/condvar/no_threads.rs similarity index 100% rename from crates/std/src/sys/sync/condvar/no_threads.rs rename to library/std/src/sys/sync/condvar/no_threads.rs diff --git a/crates/std/src/sys/sync/mod.rs b/library/std/src/sys/sync/mod.rs similarity index 100% rename from crates/std/src/sys/sync/mod.rs rename to library/std/src/sys/sync/mod.rs diff --git a/crates/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs similarity index 100% rename from crates/std/src/sys/sync/mutex/mod.rs rename to library/std/src/sys/sync/mutex/mod.rs diff --git a/crates/std/src/sys/sync/mutex/no_threads.rs b/library/std/src/sys/sync/mutex/no_threads.rs similarity index 100% rename from crates/std/src/sys/sync/mutex/no_threads.rs rename to library/std/src/sys/sync/mutex/no_threads.rs diff --git a/crates/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs similarity index 100% rename from crates/std/src/sys/sync/once/mod.rs rename to library/std/src/sys/sync/once/mod.rs diff --git a/crates/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs similarity index 100% rename from crates/std/src/sys/sync/once/no_threads.rs rename to library/std/src/sys/sync/once/no_threads.rs diff --git a/crates/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs similarity index 100% rename from crates/std/src/sys/sync/once_box.rs rename to library/std/src/sys/sync/once_box.rs diff --git a/crates/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs similarity index 100% rename from crates/std/src/sys/sync/rwlock/mod.rs rename to library/std/src/sys/sync/rwlock/mod.rs diff --git a/crates/std/src/sys/sync/rwlock/no_threads.rs b/library/std/src/sys/sync/rwlock/no_threads.rs similarity index 100% rename from crates/std/src/sys/sync/rwlock/no_threads.rs rename to library/std/src/sys/sync/rwlock/no_threads.rs diff --git a/crates/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs similarity index 100% rename from crates/std/src/sys/sync/thread_parking/mod.rs rename to library/std/src/sys/sync/thread_parking/mod.rs diff --git a/crates/std/src/sys/sync/thread_parking/unsupported.rs b/library/std/src/sys/sync/thread_parking/unsupported.rs similarity index 100% rename from crates/std/src/sys/sync/thread_parking/unsupported.rs rename to library/std/src/sys/sync/thread_parking/unsupported.rs diff --git a/crates/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs similarity index 100% rename from crates/std/src/sys/thread/mod.rs rename to library/std/src/sys/thread/mod.rs diff --git a/crates/std/src/sys/thread/unsupported.rs b/library/std/src/sys/thread/unsupported.rs similarity index 100% rename from crates/std/src/sys/thread/unsupported.rs rename to library/std/src/sys/thread/unsupported.rs diff --git a/crates/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs similarity index 100% rename from crates/std/src/sys/thread_local/mod.rs rename to library/std/src/sys/thread_local/mod.rs diff --git a/crates/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs similarity index 100% rename from crates/std/src/sys/thread_local/no_threads.rs rename to library/std/src/sys/thread_local/no_threads.rs diff --git a/crates/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs similarity index 100% rename from crates/std/src/sys/thread_local/os.rs rename to library/std/src/sys/thread_local/os.rs diff --git a/crates/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs similarity index 100% rename from crates/std/src/sys/time/mod.rs rename to library/std/src/sys/time/mod.rs diff --git a/crates/std/src/sys/time/unsupported.rs b/library/std/src/sys/time/unsupported.rs similarity index 100% rename from crates/std/src/sys/time/unsupported.rs rename to library/std/src/sys/time/unsupported.rs diff --git a/library/std/src/thread b/library/std/src/thread new file mode 120000 index 0000000..c7331ad --- /dev/null +++ b/library/std/src/thread @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread \ No newline at end of file diff --git a/crates/std/src/time.rs b/library/std/src/time.rs similarity index 100% rename from crates/std/src/time.rs rename to library/std/src/time.rs diff --git a/library/test b/library/test new file mode 120000 index 0000000..03a5f8a --- /dev/null +++ b/library/test @@ -0,0 +1 @@ +/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/test \ No newline at end of file diff --git a/sysroot/lib/rustlib/src/rust/library/library b/sysroot/lib/rustlib/src/rust/library/library new file mode 120000 index 0000000..7036b83 --- /dev/null +++ b/sysroot/lib/rustlib/src/rust/library/library @@ -0,0 +1 @@ +../../../../../../library \ No newline at end of file diff --git a/user/shell/Cargo.toml b/user/shell/Cargo.toml index a973d7f..6b3a444 100644 --- a/user/shell/Cargo.toml +++ b/user/shell/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -std = { path = "../../crates/std" } +# std = { path = "../../crates/std" } +# shared = { path = "../../crates/shared", features = ["user"] } +# core = { path = "../../crates/std/crates/core" } diff --git a/user/shell/src/main.rs b/user/shell/src/main.rs index b9a3d2a..f0784f1 100644 --- a/user/shell/src/main.rs +++ b/user/shell/src/main.rs @@ -1,5 +1,3 @@ -#![feature(custom_std)] - fn main() { let a = std::env::args(); for a in a { diff --git a/user/test_pic/Cargo.toml b/user/test_pic/Cargo.toml index 4047b22..4ccb322 100644 --- a/user/test_pic/Cargo.toml +++ b/user/test_pic/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -std = { path = "../../crates/std" } +# std = { path = "../../crates/std" } +shared = { path = "../../crates/shared", features = ["user"] } io = { path = "../../crates/io" } +bffs = { path = "../../crates/bffs" } diff --git a/user/test_pic/src/main.rs b/user/test_pic/src/main.rs index 65f471e..e7f321c 100644 --- a/user/test_pic/src/main.rs +++ b/user/test_pic/src/main.rs @@ -1,22 +1,24 @@ -#![feature(custom_std)] #![allow(unused)] use io::{Read, Write}; -use std::{io::{Stdin, stdin}, syscall}; +use std::io::{Stdin, stdin}; +use shared::syscall; fn main() { - // let mut input = String::new(); + let mut input = String::new(); + input.push('a'); // let mut file = syscall::open("/dev/fb0"); // syscall::seek(&mut file, SeekFrom::End(-3)); // syscall::write(&mut file, &[255; 6400 * 50]); // syscall::sleep(Duration::from_secs_f64(2.0)); syscall::close(0); let mut tty = syscall::open("/dev/tty0"); + tty.write(input.as_bytes()).unwrap(); syscall::spawn("/usr/bin/shell"); loop { let mut test = [0; 2]; let len = tty.read(&mut test).unwrap(); tty.write(str::from_utf8(&test[..len as usize]).unwrap().as_bytes()) - .unwrap(); + .unwrap(); } }