├── .gitignore ├── verify_readme.sh ├── Cargo.toml ├── LICENSE.txt ├── src ├── free.rs ├── lib.rs ├── internal.rs ├── sentinel.rs └── mbox.rs ├── .github └── workflows │ └── rust.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.swp 4 | *.swo 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /verify_readme.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | diff -u <( fgrep -v '[![' README.md ) <( fgrep '//!' src/lib.rs | cut -c 5- ) 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mbox" 3 | version = "0.7.1" 4 | authors = ["kennytm "] 5 | edition = "2018" 6 | rust-version = "1.36.0" 7 | 8 | license = "MIT" 9 | keywords = ["malloc", "free", "ffi", "box", "cstr"] 10 | categories = [ 11 | "api-bindings", 12 | "development-tools::ffi", 13 | "memory-management", 14 | "no-std", 15 | "os", 16 | ] 17 | repository = "https://github.com/kennytm/mbox" 18 | documentation = "https://docs.rs/mbox/" 19 | readme = "README.md" 20 | description = """malloc-based box. 21 | 22 | Supports wrapping pointers or null-terminated strings returned from malloc as a Rust type, which 23 | will be free'd on drop. 24 | """ 25 | 26 | exclude = [".gitignore", ".github"] 27 | 28 | [dependencies] 29 | libc = "0.2" 30 | # Feature provided as a way to cut down on dependencies 31 | stable_deref_trait = { version = "1.0", optional = true, default-features = false } 32 | 33 | [features] 34 | default = ["std", "stable_deref_trait"] 35 | std = [] 36 | nightly = [] 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kenny Chan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /src/free.rs: -------------------------------------------------------------------------------- 1 | //! Trait to instruct how to properly drop and free pointers. 2 | 3 | use std::ptr::{drop_in_place, NonNull}; 4 | 5 | use crate::internal::gen_free; 6 | 7 | /// Implemented for pointers which can be freed. 8 | pub trait Free { 9 | /// Drops the content pointed by this pointer and frees it. 10 | /// 11 | /// # Safety 12 | /// 13 | /// The `ptr` must be allocated through `malloc()`. 14 | /// 15 | /// Do not call this method if the pointer has been freed. Users of this trait should maintain a 16 | /// flag to track if the pointer has been freed or not (the Rust compiler will automatically do 17 | /// this with a `Drop` type). 18 | unsafe fn free(ptr: NonNull); 19 | } 20 | 21 | /// Drops the content of `*ptr`, then frees the `ptr` itself. 22 | unsafe fn free_ptr_ref(ptr: NonNull) { 23 | drop_in_place(ptr.as_ptr()); 24 | gen_free(ptr); 25 | } 26 | 27 | impl Free for T { 28 | #[cfg(feature = "nightly")] 29 | default unsafe fn free(ptr_ref: NonNull) { 30 | free_ptr_ref(ptr_ref); 31 | } 32 | 33 | #[cfg(not(feature = "nightly"))] 34 | unsafe fn free(ptr_ref: NonNull) { 35 | free_ptr_ref(ptr_ref); 36 | } 37 | } 38 | 39 | impl Free for [T] { 40 | unsafe fn free(fat_ptr: NonNull) { 41 | let fat_ptr = fat_ptr.as_ptr(); 42 | drop_in_place(fat_ptr); 43 | gen_free(NonNull::new_unchecked(fat_ptr as *mut T)); 44 | } 45 | } 46 | 47 | impl Free for str { 48 | unsafe fn free(fat_ptr: NonNull) { 49 | Free::free(NonNull::new_unchecked(fat_ptr.as_ptr() as *mut [u8])); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | stable-test: 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 5 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Clippy 18 | run: cargo clippy 19 | - name: Format 20 | run: cargo fmt -- --check 21 | - name: Crate doc is perfect copy of README.md 22 | run: ./verify_readme.sh 23 | - name: Test 24 | run: cargo test 25 | - name: Test (no features) 26 | run: cargo test --no-default-features 27 | - name: Test (no stable_deref_trait) 28 | run: cargo test --no-default-features --features std 29 | - name: Test (no-std) 30 | run: cargo test --no-default-features --features stable_deref_trait 31 | 32 | platform-test: 33 | strategy: 34 | fail-fast: true 35 | matrix: 36 | platform: 37 | - os: ubuntu-latest 38 | rust: 1.36.0 39 | - os: ubuntu-latest 40 | rust: nightly 41 | - os: windows-latest 42 | rust: stable 43 | - os: macos-latest 44 | rust: stable 45 | runs-on: ${{ matrix.platform.os }} 46 | timeout-minutes: 5 47 | steps: 48 | - uses: actions/checkout@v2 49 | - uses: actions-rs/toolchain@v1 50 | name: Install Rust 51 | with: 52 | toolchain: ${{ matrix.platform.rust }} 53 | profile: minimal 54 | default: true 55 | - name: Test 56 | run: cargo test 57 | - name: Test (no-std) 58 | run: cargo test --no-default-features 59 | 60 | nightly-test: 61 | runs-on: ubuntu-latest 62 | timeout-minutes: 5 63 | steps: 64 | - uses: actions/checkout@v2 65 | - uses: actions-rs/toolchain@v1 66 | name: Install Rust 67 | with: 68 | toolchain: nightly 69 | profile: minimal 70 | default: true 71 | components: miri, rust-src 72 | - name: Test + nightly 73 | run: cargo test --features nightly 74 | - name: ASan 75 | run: RUSTFLAGS=-Zsanitizer=address cargo test --lib --features nightly 76 | - name: Miri 77 | run: cargo miri test --features nightly 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `mbox`: `malloc`-based box 2 | ========================== 3 | [![Crates.io](https://img.shields.io/crates/v/mbox.svg)](https://crates.io/crates/mbox) 4 | [![docs.rs](https://docs.rs/mbox/badge.svg)](https://docs.rs/mbox) 5 | [![Build status](https://github.com/kennytm/mbox/workflows/Rust/badge.svg)](https://github.com/kennytm/mbox/actions?query=workflow%3ARust) 6 | [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.txt) 7 | 8 | This crate provides structures that wrap pointers returned from `malloc` as a Box, and 9 | automatically `free` them on drop. These types allow you to interact with pointers and 10 | null-terminated strings and arrays in a Rusty style. 11 | 12 | ## Examples 13 | 14 | ```rust 15 | extern crate libc; 16 | extern crate mbox; 17 | 18 | use libc::{c_char, malloc, strcpy}; 19 | use mbox::MString; 20 | 21 | // Assume we have a C function that returns a malloc'ed string. 22 | unsafe extern "C" fn create_str() -> *mut c_char { 23 | let ptr = malloc(12) as *mut c_char; 24 | strcpy(ptr, b"Hello world\0".as_ptr() as *const c_char); 25 | ptr 26 | } 27 | 28 | fn main() { 29 | // we wrap the null-terminated string into an MString. 30 | let string = unsafe { MString::from_raw_unchecked(create_str()) }; 31 | 32 | // check the content is expected. 33 | assert_eq!(&*string, "Hello world"); 34 | 35 | // the string will be dropped by `free` after the code is done. 36 | } 37 | ``` 38 | 39 | > Note: This crate does not support Windows in general. 40 | > 41 | > Pointers in Rust are required to be aligned to be sound. However, there is no API on 42 | > Windows that are both compatible with `free()` and supports aligned-malloc. 43 | > 44 | > Because the primary purpose of this crate is interoperability with C code working 45 | > with `malloc()`, it is impossible for us to switch to the safe variant like 46 | > [`_aligned_malloc()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc) 47 | > (which requires [`_aligned_free()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-free)). 48 | > 49 | > On Windows, trying to use `MBox` or `MArray` with `T`'s alignment not equal to 1 50 | > will not compile. 51 | > 52 | > ```rust,compile_fail 53 | > # #[cfg(not(windows))] { _ }; 54 | > 55 | > use mbox::MBox; 56 | > let value = MBox::new(1_u64); // will not compile, 57 | > ``` 58 | 59 | 60 | ## Installation 61 | 62 | Add this to your Cargo.toml: 63 | 64 | ```toml 65 | [dependencies] 66 | mbox = "0.7" 67 | ``` 68 | 69 | ## Usage 70 | 71 | This crate provides three main types, all of which uses the system's `malloc`/`free` as the 72 | allocator. 73 | 74 | * `MBox` — Similar to `Box`. 75 | * `MString` — Similar to `std::ffi::CString`. 76 | * `MArray` — A null-terminated array, which can be used to represent e.g. array of C strings 77 | terminated by a null pointer. 78 | 79 | ### `#![no_std]` 80 | 81 | You may compile `mbox` and disable the `std` feature to not link to `std` (it will still link to 82 | `core`. 83 | 84 | ```toml 85 | [dependencies] 86 | mbox = { version = "0.7", default-features = false } 87 | ``` 88 | 89 | When `#![no_std]` is activated, you cannot convert an `MString` into a `std::ffi::CStr`, as the 90 | type simply does not exist 🙂. 91 | 92 | ### Nightly 93 | 94 | To use nightly-channel features (if you need support for custom dynamic-sized types), enable the 95 | `nightly` feature: 96 | 97 | ```toml 98 | [dependencies] 99 | mbox = { version = "0.7", features = ["nightly"] } 100 | ``` 101 | 102 | ## Migrating from other crates 103 | 104 | Note that `MBox` does not support custom allocator. If the type requires custom allocation, 105 | `MBox` cannot serve you. 106 | 107 | * [`malloc_buf`](https://crates.io/crates/malloc_buf) — `Malloc` is equivalent to `MBox`. 108 | Note however that `MBox<[T]>::from_raw_parts` will not allow null, 0-length buffers; use a 109 | dangling pointer instead. 110 | 111 | * [`cbox`](https://crates.io/crates/cbox) — When not using a custom `DisposeRef`, the 112 | `CSemiBox<'static, T>` type is equivalent to `MBox`, and `CBox` is equivalent to 113 | `&'static T`. 114 | 115 | * [`c_vec`](https://crates.io/crates/c_vec) — When using `free` as the destructor, `CVec` is 116 | equivalent to `MBox<[T]>` and `CSlice` as `[T]`. 117 | 118 | * [`malloced`](https://crates.io/crates/malloced) — `Malloced` is equivalent to `MBox`. 119 | Note however that `mbox` depends on `libc` (more stable, but also longer build-time) and 120 | doesn't support `dyn Any` downcasting. 121 | 122 | * [`malloc-array`](https://crates.io/crates/malloc-array) — `HeapArray` is similar to 123 | `MBox`, but this crate focuses more on raw memory management. 124 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `mbox`: `malloc`-based box 2 | //! ========================== 3 | //! 4 | //! This crate provides structures that wrap pointers returned from `malloc` as a Box, and 5 | //! automatically `free` them on drop. These types allow you to interact with pointers and 6 | //! null-terminated strings and arrays in a Rusty style. 7 | //! 8 | //! ## Examples 9 | //! 10 | //! ```rust 11 | //! extern crate libc; 12 | //! extern crate mbox; 13 | //! 14 | //! use libc::{c_char, malloc, strcpy}; 15 | //! use mbox::MString; 16 | //! 17 | //! // Assume we have a C function that returns a malloc'ed string. 18 | //! unsafe extern "C" fn create_str() -> *mut c_char { 19 | //! let ptr = malloc(12) as *mut c_char; 20 | //! strcpy(ptr, b"Hello world\0".as_ptr() as *const c_char); 21 | //! ptr 22 | //! } 23 | //! 24 | //! fn main() { 25 | //! // we wrap the null-terminated string into an MString. 26 | //! let string = unsafe { MString::from_raw_unchecked(create_str()) }; 27 | //! 28 | //! // check the content is expected. 29 | //! assert_eq!(&*string, "Hello world"); 30 | //! 31 | //! // the string will be dropped by `free` after the code is done. 32 | //! } 33 | //! ``` 34 | //! 35 | //! > Note: This crate does not support Windows in general. 36 | //! > 37 | //! > Pointers in Rust are required to be aligned to be sound. However, there is no API on 38 | //! > Windows that are both compatible with `free()` and supports aligned-malloc. 39 | //! > 40 | //! > Because the primary purpose of this crate is interoperability with C code working 41 | //! > with `malloc()`, it is impossible for us to switch to the safe variant like 42 | //! > [`_aligned_malloc()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc) 43 | //! > (which requires [`_aligned_free()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-free)). 44 | //! > 45 | //! > On Windows, trying to use `MBox` or `MArray` with `T`'s alignment not equal to 1 46 | //! > will not compile. 47 | //! > 48 | //! > ```rust,compile_fail 49 | //! > # #[cfg(not(windows))] { _ }; 50 | //! > 51 | //! > use mbox::MBox; 52 | //! > let value = MBox::new(1_u64); // will not compile, 53 | //! > ``` 54 | //! 55 | //! 56 | //! ## Installation 57 | //! 58 | //! Add this to your Cargo.toml: 59 | //! 60 | //! ```toml 61 | //! [dependencies] 62 | //! mbox = "0.7" 63 | //! ``` 64 | //! 65 | //! ## Usage 66 | //! 67 | //! This crate provides three main types, all of which uses the system's `malloc`/`free` as the 68 | //! allocator. 69 | //! 70 | //! * `MBox` — Similar to `Box`. 71 | //! * `MString` — Similar to `std::ffi::CString`. 72 | //! * `MArray` — A null-terminated array, which can be used to represent e.g. array of C strings 73 | //! terminated by a null pointer. 74 | //! 75 | //! ### `#![no_std]` 76 | //! 77 | //! You may compile `mbox` and disable the `std` feature to not link to `std` (it will still link to 78 | //! `core`. 79 | //! 80 | //! ```toml 81 | //! [dependencies] 82 | //! mbox = { version = "0.7", default-features = false } 83 | //! ``` 84 | //! 85 | //! When `#![no_std]` is activated, you cannot convert an `MString` into a `std::ffi::CStr`, as the 86 | //! type simply does not exist 🙂. 87 | //! 88 | //! ### Nightly 89 | //! 90 | //! To use nightly-channel features (if you need support for custom dynamic-sized types), enable the 91 | //! `nightly` feature: 92 | //! 93 | //! ```toml 94 | //! [dependencies] 95 | //! mbox = { version = "0.7", features = ["nightly"] } 96 | //! ``` 97 | //! 98 | //! ## Migrating from other crates 99 | //! 100 | //! Note that `MBox` does not support custom allocator. If the type requires custom allocation, 101 | //! `MBox` cannot serve you. 102 | //! 103 | //! * [`malloc_buf`](https://crates.io/crates/malloc_buf) — `Malloc` is equivalent to `MBox`. 104 | //! Note however that `MBox<[T]>::from_raw_parts` will not allow null, 0-length buffers; use a 105 | //! dangling pointer instead. 106 | //! 107 | //! * [`cbox`](https://crates.io/crates/cbox) — When not using a custom `DisposeRef`, the 108 | //! `CSemiBox<'static, T>` type is equivalent to `MBox`, and `CBox` is equivalent to 109 | //! `&'static T`. 110 | //! 111 | //! * [`c_vec`](https://crates.io/crates/c_vec) — When using `free` as the destructor, `CVec` is 112 | //! equivalent to `MBox<[T]>` and `CSlice` as `[T]`. 113 | //! 114 | //! * [`malloced`](https://crates.io/crates/malloced) — `Malloced` is equivalent to `MBox`. 115 | //! Note however that `mbox` depends on `libc` (more stable, but also longer build-time) and 116 | //! doesn't support `dyn Any` downcasting. 117 | //! 118 | //! * [`malloc-array`](https://crates.io/crates/malloc-array) — `HeapArray` is similar to 119 | //! `MBox`, but this crate focuses more on raw memory management. 120 | 121 | #![cfg_attr( 122 | feature = "nightly", 123 | feature(min_specialization, unsize, coerce_unsized) 124 | )] 125 | #![cfg_attr(not(feature = "std"), no_std)] 126 | 127 | #[cfg(not(feature = "std"))] 128 | extern crate core as std; 129 | extern crate libc; 130 | #[cfg(feature = "stable_deref_trait")] 131 | extern crate stable_deref_trait; 132 | 133 | pub mod free; 134 | mod internal; 135 | pub mod mbox; 136 | pub mod sentinel; 137 | 138 | pub use self::mbox::MBox; 139 | pub use self::sentinel::{MArray, MString}; 140 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "std"))] 2 | extern crate alloc; 3 | 4 | use libc::c_void; 5 | 6 | use std::alloc::Layout; 7 | use std::marker::PhantomData; 8 | use std::mem::{align_of, size_of}; 9 | use std::ptr::{copy_nonoverlapping, NonNull}; 10 | 11 | #[cfg(not(feature = "std"))] 12 | use self::alloc::alloc::handle_alloc_error; 13 | #[cfg(feature = "std")] 14 | use std::alloc::handle_alloc_error; 15 | 16 | #[cfg(feature = "nightly")] 17 | use std::marker::Unsize; 18 | #[cfg(feature = "nightly")] 19 | use std::ops::CoerceUnsized; 20 | 21 | //{{{ Unique -------------------------------------------------------------------------------------- 22 | 23 | /// Same as `std::ptr::Unique`, but provides a close-enough representation on stable channel. 24 | pub struct Unique { 25 | pointer: NonNull, 26 | marker: PhantomData, 27 | } 28 | 29 | unsafe impl Send for Unique {} 30 | 31 | unsafe impl Sync for Unique {} 32 | 33 | impl Unique { 34 | /// Creates a new raw unique pointer. 35 | /// 36 | /// # Safety 37 | /// 38 | /// The `pointer`'s ownership must be transferred into the result. That is, 39 | /// it is no longer valid to touch `pointer` and its copies directly after 40 | /// calling this function. 41 | pub const unsafe fn new(pointer: NonNull) -> Self { 42 | Self { 43 | pointer, 44 | marker: PhantomData, 45 | } 46 | } 47 | } 48 | 49 | impl Unique { 50 | pub fn as_non_null_ptr(&self) -> NonNull { 51 | self.pointer 52 | } 53 | } 54 | 55 | #[cfg(feature = "nightly")] 56 | impl, U: ?Sized> CoerceUnsized> for Unique {} 57 | 58 | //}}} 59 | 60 | //{{{ gen_malloc ---------------------------------------------------------------------------------- 61 | 62 | #[cfg(windows)] 63 | unsafe fn malloc_aligned(size: usize) -> *mut c_void { 64 | struct AlignmentChecker(PhantomData); 65 | impl AlignmentChecker { 66 | // Ensure in compile-time that the alignment of T is 1. 67 | // If the alignment is >1, the subtraction here will overflow to stop compilation. 68 | // (This hack is needed for targeting Rust 1.36.) 69 | const ENSURE_ALIGNMENT_IS_1: usize = 1 - align_of::(); 70 | } 71 | // The assert here should be eliminated by optimization, 72 | // but it is used to ensure the const evaluation actually does happen. 73 | assert_eq!( 74 | 0, 75 | AlignmentChecker::::ENSURE_ALIGNMENT_IS_1, 76 | "Windows malloc() only support alignment of 1" 77 | ); 78 | 79 | libc::malloc(size) 80 | } 81 | 82 | #[cfg(all(not(windows), target_os = "android"))] 83 | unsafe fn malloc_aligned(size: usize) -> *mut c_void { 84 | libc::memalign(align_of::(), size) 85 | } 86 | 87 | #[cfg(all(not(windows), not(target_os = "android")))] 88 | unsafe fn malloc_aligned(size: usize) -> *mut c_void { 89 | let mut result = std::ptr::null_mut(); 90 | let align = align_of::().max(size_of::<*mut ()>()); 91 | libc::posix_memalign(&mut result, align, size); 92 | result 93 | } 94 | 95 | /// Generic malloc function. 96 | /// 97 | /// This function allocates memory capable of storing the array `[T; count]`. 98 | /// The memory content will not be initialized. 99 | /// 100 | /// If `T` is zero-sized or `count == 0`, we will call `malloc(0)` which the C 101 | /// standard permits returning NULL. In that case, we will *allocate at least 1 102 | /// byte* to respect the `NonNull` constraint (we cannot use `NonNull::danging()` 103 | /// because we allow the result to be passed directly to C's `free()`). 104 | pub fn gen_malloc(count: usize) -> NonNull { 105 | let requested_size = count.checked_mul(size_of::()).expect("memory overflow"); 106 | 107 | let mut res; 108 | // SAFETY: allocating should be safe, duh. 109 | unsafe { 110 | res = malloc_aligned::(requested_size); 111 | if res.is_null() && requested_size == 0 { 112 | res = malloc_aligned::(align_of::()); 113 | } 114 | } 115 | NonNull::new(res as *mut T).unwrap_or_else(|| handle_alloc_error(Layout::new::())) 116 | } 117 | 118 | /// Generic free function. 119 | /// 120 | /// # Safety 121 | /// 122 | /// The `ptr` must be obtained from `malloc()` or similar C functions. 123 | /// The memory content will not be dropped. 124 | pub unsafe fn gen_free(ptr: NonNull) { 125 | libc::free(ptr.as_ptr() as *mut c_void); 126 | } 127 | 128 | /// Generic realloc function. 129 | /// 130 | /// Unlike C's `realloc()`, calling `gen_realloc(ptr, x, 0)` is not equivalent 131 | /// to `gen_free(ptr)`. Rather, it will return a properly-aligned and allocated 132 | /// pointer to satisfy the `NonNull` constraint. 133 | /// 134 | /// # Safety 135 | /// 136 | /// The `ptr` must be obtained from `malloc()` or similar C functions. 137 | pub unsafe fn gen_realloc(ptr: NonNull, old_count: usize, new_count: usize) -> NonNull { 138 | if size_of::() == 0 { 139 | return ptr; 140 | } 141 | 142 | (|| { 143 | // ensure `requested_size > 0` to avoid `realloc()` returning a successful NULL. 144 | let requested_size = new_count.checked_mul(size_of::())?.max(align_of::()); 145 | let mut res = libc::realloc(ptr.as_ptr() as *mut c_void, requested_size); 146 | if res.is_null() { 147 | return None; 148 | } 149 | 150 | // Most system don't provide an `aligned_realloc`. 151 | // If `libc::realloc()` does not give us an aligned pointer, 152 | // we have to perform an additional aligned allocation and memcpy over. 153 | if res as usize % align_of::() != 0 { 154 | let actual_res = malloc_aligned::(requested_size); 155 | if actual_res.is_null() { 156 | return None; 157 | } 158 | 159 | // no need to do checked_mul() here since it must be <= `requested_size`. 160 | let copy_len = old_count.min(new_count) * size_of::(); 161 | copy_nonoverlapping(res, actual_res, copy_len); 162 | libc::free(res); 163 | res = actual_res; 164 | } 165 | 166 | NonNull::new(res as *mut T) 167 | })() 168 | .unwrap_or_else(|| handle_alloc_error(Layout::new::())) 169 | } 170 | 171 | //}}} 172 | 173 | //{{{ Drop counter -------------------------------------------------------------------------------- 174 | 175 | #[cfg(all(test, not(windows)))] 176 | #[derive(Clone, Debug, PartialEq, Eq)] 177 | #[cfg_attr(feature = "std", derive(Default))] 178 | pub(crate) struct SharedCounter { 179 | #[cfg(feature = "std")] 180 | counter: std::rc::Rc>, 181 | 182 | /// A shared, mutable counter on heap. 183 | #[cfg(not(feature = "std"))] 184 | counter: NonNull, 185 | } 186 | 187 | #[cfg(all(test, not(windows), not(feature = "std")))] 188 | impl Default for SharedCounter { 189 | fn default() -> Self { 190 | let counter = gen_malloc(1); 191 | // SAFETY: malloc() returns an uninitialized integer which is then filled in. 192 | unsafe { 193 | std::ptr::write(counter.as_ptr(), 0); 194 | Self { counter } 195 | } 196 | } 197 | } 198 | 199 | #[cfg(all(test, not(windows)))] 200 | impl SharedCounter { 201 | /// Gets the counter value. 202 | pub(crate) fn get(&self) -> usize { 203 | #[cfg(feature = "std")] 204 | { 205 | self.counter.get() 206 | } 207 | // SAFETY: `self.counter` is malloc()'ed, initialized and never freed. 208 | #[cfg(not(feature = "std"))] 209 | unsafe { 210 | *self.counter.as_ref() 211 | } 212 | } 213 | 214 | /// Asserts the counter value equals to the input. Panics when different. 215 | pub(crate) fn assert_eq(&self, value: usize) { 216 | assert_eq!(self.get(), value); 217 | } 218 | 219 | /// Increases the counter by 1. 220 | fn inc(&self) { 221 | #[cfg(feature = "std")] 222 | { 223 | self.counter.set(self.counter.get() + 1); 224 | } 225 | // SAFETY: `self.counter` is malloc()'ed, initialized and never freed. 226 | // Since `SharedCounter` is not Sync nor Send, we are sure the 227 | // modification happens in the single thread, so we don't worry about 228 | // the interior mutation. 229 | #[cfg(not(feature = "std"))] 230 | unsafe { 231 | *self.counter.as_ptr() += 1; 232 | } 233 | } 234 | } 235 | 236 | /// A test structure to count how many times the value has been dropped. 237 | #[cfg(all(test, not(windows)))] 238 | #[derive(Clone, Debug, PartialEq, Eq, Default)] 239 | pub(crate) struct DropCounter(SharedCounter); 240 | 241 | #[cfg(all(test, not(windows)))] 242 | impl std::ops::Deref for DropCounter { 243 | type Target = SharedCounter; 244 | fn deref(&self) -> &Self::Target { 245 | &self.0 246 | } 247 | } 248 | 249 | #[cfg(all(test, not(windows)))] 250 | impl Drop for DropCounter { 251 | fn drop(&mut self) { 252 | self.0.inc(); 253 | } 254 | } 255 | 256 | //}}} 257 | 258 | //{{{ Panic-on-clone ------------------------------------------------------------------------------ 259 | 260 | /// A test structure which panics when it is cloned. 261 | #[cfg(test)] 262 | #[derive(Default)] 263 | #[repr(C)] // silence the dead code warning, we don't want a ZST here to complicate things. 264 | pub struct PanicOnClone(u8); 265 | 266 | #[cfg(test)] 267 | impl Clone for PanicOnClone { 268 | fn clone(&self) -> Self { 269 | panic!("panic on clone"); 270 | } 271 | } 272 | 273 | //}}} 274 | -------------------------------------------------------------------------------- /src/sentinel.rs: -------------------------------------------------------------------------------- 1 | //! Sentinel-terminated types. 2 | 3 | use libc::{c_char, strlen}; 4 | #[cfg(feature = "stable_deref_trait")] 5 | use stable_deref_trait::StableDeref; 6 | 7 | use std::borrow::{Borrow, BorrowMut}; 8 | use std::convert::{AsMut, AsRef}; 9 | use std::default::Default; 10 | #[cfg(feature = "std")] 11 | use std::ffi::CStr; 12 | use std::hash::{Hash, Hasher}; 13 | use std::iter::once; 14 | use std::ops::{Deref, DerefMut}; 15 | use std::ptr::{copy_nonoverlapping, null, null_mut, write}; 16 | use std::str::Utf8Error; 17 | 18 | use crate::internal::gen_malloc; 19 | use crate::mbox::MBox; 20 | 21 | #[cfg(all(test, not(windows)))] 22 | use crate::internal::DropCounter; 23 | 24 | /// Implemented for types which has a sentinel value. 25 | pub trait Sentinel: Eq { 26 | /// Obtains the sentinel value. 27 | const SENTINEL: Self; 28 | } 29 | 30 | impl Sentinel for *const T { 31 | const SENTINEL: Self = null(); 32 | } 33 | 34 | impl Sentinel for *mut T { 35 | const SENTINEL: Self = null_mut(); 36 | } 37 | 38 | impl Sentinel for Option { 39 | const SENTINEL: Self = None; 40 | } 41 | 42 | macro_rules! impl_zero_for_sentinel { 43 | ($($ty:ty)+) => { 44 | $(impl Sentinel for $ty { 45 | const SENTINEL: Self = 0; 46 | })+ 47 | } 48 | } 49 | 50 | impl_zero_for_sentinel!(u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize); 51 | 52 | /// A `malloc`-backed array with an explicit sentinel at the end. 53 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] 54 | pub struct MArray(MBox<[T]>); 55 | 56 | /// A `malloc`-backed null-terminated string (similar to `CString`). 57 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] 58 | pub struct MString(MBox); 59 | 60 | impl MArray { 61 | /// Constructs a new malloc-backed slice from a pointer to the null-terminated array. 62 | /// 63 | /// # Safety 64 | /// 65 | /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is 66 | /// expected to be deallocated using `free()`. It must not be null. The content of the pointer 67 | /// must be already initialized, and terminated by `T::SENTINEL`. The array's ownership is 68 | /// passed into the result, and thus should not be used after this function returns. 69 | pub unsafe fn from_raw(base: *mut T) -> MArray { 70 | let mut len = 0; 71 | while *base.add(len) != T::SENTINEL { 72 | len += 1; 73 | } 74 | MArray(MBox::from_raw_parts(base, len + 1)) 75 | } 76 | 77 | /// Converts into an `MBox` including the sentinel. 78 | pub fn into_mbox_with_sentinel(self) -> MBox<[T]> { 79 | self.0 80 | } 81 | 82 | /// Converts into an `MBox` excluding the sentinel. 83 | pub fn into_mbox(self) -> MBox<[T]> { 84 | let (ptr, len) = self.0.into_raw_parts(); 85 | unsafe { MBox::from_raw_parts(ptr, len - 1) } 86 | } 87 | } 88 | 89 | impl MArray { 90 | /// Creates a null-terminated array from the clone of a slice. 91 | pub fn from_slice(slice: &[T]) -> MArray { 92 | MArray(slice.iter().cloned().chain(once(T::SENTINEL)).collect()) 93 | } 94 | } 95 | 96 | impl MString { 97 | /// Constructs a new malloc-backed string from a null-terminated C string. 98 | /// 99 | /// # Safety 100 | /// 101 | /// The `base` must be allocated via `malloc()`, `calloc()` or similar C functions that is 102 | /// expected to be deallocated using `free()`. It must not be null. The content of the string 103 | /// must be already initialized, and terminated by `'\0'`. The string's ownership is passed into 104 | /// the result, and thus should not be used after this function returns. 105 | /// 106 | /// The string must be valid UTF-8. 107 | pub unsafe fn from_raw_unchecked(base: *mut c_char) -> MString { 108 | let len = strlen(base); 109 | MString(MBox::from_raw_utf8_parts_unchecked( 110 | base as *mut u8, 111 | len + 1, 112 | )) 113 | } 114 | 115 | /// Constructs a new malloc-backed string from a null-terminated C string. Errors with 116 | /// `Utf8Error` if the string is not in valid UTF-8. 117 | /// 118 | /// # Safety 119 | /// 120 | /// The `base` must be allocated via `malloc()`, `calloc()` or similar C functions that is 121 | /// expected to be deallocated using `free()`. It must not be null. The content of the string 122 | /// must be already initialized, and terminated by `'\0'`. The string's ownership is passed into 123 | /// the result, and thus should not be used after this function returns. 124 | pub unsafe fn from_raw(base: *mut c_char) -> Result { 125 | let len = strlen(base); 126 | let mbox = MBox::from_raw_utf8_parts(base as *mut u8, len + 1)?; 127 | Ok(MString(mbox)) 128 | } 129 | 130 | pub fn into_bytes(self) -> MArray { 131 | MArray(self.0.into_bytes()) 132 | } 133 | 134 | /// Converts into an `MBox` including the sentinel. 135 | pub fn into_mbox_with_sentinel(self) -> MBox { 136 | self.0 137 | } 138 | 139 | /// Converts into an `MBox` excluding the sentinel. 140 | pub fn into_mbox(self) -> MBox { 141 | unsafe { MBox::from_utf8_unchecked(self.into_bytes().into_mbox()) } 142 | } 143 | 144 | /// Converts to a C string. This allows users to borrow an MString in FFI code. 145 | #[cfg(all(feature = "std"))] 146 | pub fn as_c_str(&self) -> &CStr { 147 | unsafe { CStr::from_bytes_with_nul_unchecked(self.0.as_bytes()) } 148 | } 149 | 150 | /// Obtains the raw bytes including the sentinel. 151 | pub fn as_bytes_with_sentinel(&self) -> &[u8] { 152 | self.0.as_bytes() 153 | } 154 | } 155 | 156 | impl From<&str> for MString { 157 | /// Creates a null-terminated string from the clone of a string. 158 | fn from(string: &str) -> MString { 159 | unsafe { 160 | let len = string.len(); 161 | let ptr = gen_malloc(len + 1).as_ptr(); 162 | copy_nonoverlapping(string.as_ptr(), ptr, len); 163 | write(ptr.add(len), 0); 164 | MString(MBox::from_raw_utf8_parts_unchecked(ptr, len + 1)) 165 | } 166 | } 167 | } 168 | 169 | impl Deref for MArray { 170 | type Target = [T]; 171 | fn deref(&self) -> &[T] { 172 | let actual_len = self.0.len() - 1; 173 | &self.0[..actual_len] 174 | } 175 | } 176 | 177 | impl Deref for MString { 178 | type Target = str; 179 | fn deref(&self) -> &str { 180 | let actual_len = self.0.len() - 1; 181 | &self.0[..actual_len] 182 | } 183 | } 184 | 185 | #[cfg(feature = "stable_deref_trait")] 186 | unsafe impl StableDeref for MArray {} 187 | #[cfg(feature = "stable_deref_trait")] 188 | unsafe impl StableDeref for MString {} 189 | 190 | impl DerefMut for MArray { 191 | fn deref_mut(&mut self) -> &mut [T] { 192 | let actual_len = self.0.len() - 1; 193 | &mut self.0[..actual_len] 194 | } 195 | } 196 | 197 | #[allow(clippy::derive_hash_xor_eq)] 198 | impl Hash for MString { 199 | fn hash(&self, state: &mut H) { 200 | self.deref().hash(state); 201 | } 202 | } 203 | 204 | #[allow(clippy::derive_hash_xor_eq)] 205 | impl Hash for MArray { 206 | fn hash(&self, state: &mut H) { 207 | self.deref().hash(state); 208 | } 209 | } 210 | 211 | impl DerefMut for MString { 212 | fn deref_mut(&mut self) -> &mut str { 213 | let actual_len = self.0.len() - 1; 214 | &mut self.0[..actual_len] 215 | } 216 | } 217 | 218 | impl Default for MArray { 219 | fn default() -> Self { 220 | unsafe { 221 | let arr = gen_malloc(1).as_ptr(); 222 | write(arr, T::SENTINEL); 223 | MArray(MBox::from_raw_parts(arr, 1)) 224 | } 225 | } 226 | } 227 | 228 | impl Default for MString { 229 | fn default() -> Self { 230 | unsafe { 231 | let arr = gen_malloc(1).as_ptr(); 232 | write(arr, 0); 233 | MString(MBox::from_raw_utf8_parts_unchecked(arr, 1)) 234 | } 235 | } 236 | } 237 | 238 | impl AsRef<[T]> for MArray { 239 | fn as_ref(&self) -> &[T] { 240 | self 241 | } 242 | } 243 | 244 | impl AsMut<[T]> for MArray { 245 | fn as_mut(&mut self) -> &mut [T] { 246 | self 247 | } 248 | } 249 | 250 | impl Borrow<[T]> for MArray { 251 | fn borrow(&self) -> &[T] { 252 | self 253 | } 254 | } 255 | 256 | impl BorrowMut<[T]> for MArray { 257 | fn borrow_mut(&mut self) -> &mut [T] { 258 | self 259 | } 260 | } 261 | 262 | impl AsRef for MString { 263 | fn as_ref(&self) -> &str { 264 | self 265 | } 266 | } 267 | 268 | impl AsMut for MString { 269 | fn as_mut(&mut self) -> &mut str { 270 | self 271 | } 272 | } 273 | 274 | impl Borrow for MString { 275 | fn borrow(&self) -> &str { 276 | self 277 | } 278 | } 279 | 280 | impl BorrowMut for MString { 281 | fn borrow_mut(&mut self) -> &mut str { 282 | self 283 | } 284 | } 285 | 286 | #[cfg(feature = "std")] 287 | impl AsRef for MString { 288 | fn as_ref(&self) -> &CStr { 289 | self.as_c_str() 290 | } 291 | } 292 | 293 | #[test] 294 | fn test_array() { 295 | unsafe { 296 | let src = gen_malloc::(6).as_ptr(); 297 | *src.offset(0) = 56; 298 | *src.offset(1) = 18; 299 | *src.offset(2) = 200; 300 | *src.offset(3) = 0; 301 | *src.offset(4) = 105; 302 | *src.offset(5) = 0; 303 | 304 | let mut array = MArray::from_raw(src); 305 | assert_eq!(&*array, &[56u8, 18, 200]); 306 | array[1] = 19; 307 | assert_eq!(*src.offset(1), 19); 308 | } 309 | } 310 | 311 | #[cfg(not(windows))] 312 | #[test] 313 | fn test_array_with_drop() { 314 | let counter = DropCounter::default(); 315 | unsafe { 316 | let src = gen_malloc::>(3).as_ptr(); 317 | write(src.offset(0), Some(counter.clone())); 318 | write(src.offset(1), Some(counter.clone())); 319 | write(src.offset(2), None); 320 | 321 | counter.assert_eq(0); 322 | let array = MArray::from_raw(src); 323 | assert_eq!(array.len(), 2); 324 | array[0].as_ref().unwrap().assert_eq(0); 325 | array[1].as_ref().unwrap().assert_eq(0); 326 | } 327 | counter.assert_eq(2); 328 | } 329 | 330 | #[test] 331 | fn test_string() { 332 | unsafe { 333 | let src = gen_malloc::(5).as_ptr(); 334 | *src.offset(0) = 0x61; 335 | *src.offset(1) = -0x19i8 as c_char; 336 | *src.offset(2) = -0x6ci8 as c_char; 337 | *src.offset(3) = -0x4ei8 as c_char; 338 | *src.offset(4) = 0; 339 | 340 | let string = MString::from_raw_unchecked(src); 341 | assert_eq!(&*string, "a甲"); 342 | } 343 | } 344 | 345 | #[test] 346 | fn test_non_utf8_string() { 347 | unsafe { 348 | let src = gen_malloc::(2).as_ptr(); 349 | *src.offset(0) = -1i8 as c_char; 350 | *src.offset(1) = 0; 351 | 352 | let string = MString::from_raw(src); 353 | assert!(string.is_err()); 354 | 355 | let src2 = gen_malloc::(2).as_ptr(); 356 | *src2.offset(0) = 1; 357 | *src2.offset(1) = 0; 358 | 359 | let string2 = MString::from_raw(src2); 360 | assert_eq!(string2.unwrap().deref(), "\u{1}"); 361 | } 362 | } 363 | 364 | #[cfg(feature = "std")] 365 | #[test] 366 | fn test_c_str() { 367 | unsafe { 368 | let src = gen_malloc::(2).as_ptr(); 369 | *src.offset(0) = 1; 370 | *src.offset(1) = 0; 371 | let string = MString::from_raw_unchecked(src); 372 | let c_str = string.as_c_str(); 373 | assert_eq!(c_str, CStr::from_ptr(b"\x01\x00".as_ptr() as *const c_char)); 374 | } 375 | } 376 | 377 | #[cfg(not(windows))] 378 | #[test] 379 | fn test_array_into_mbox() { 380 | let first = MArray::from_slice(&[123, 456, 789]); 381 | let second = first.clone(); 382 | 383 | assert_eq!(&*first.into_mbox(), &[123, 456, 789]); 384 | assert_eq!(&*second.into_mbox_with_sentinel(), &[123, 456, 789, 0]); 385 | } 386 | 387 | #[test] 388 | fn test_string_into_mbox() { 389 | let first = MString::from("abcde"); 390 | let second = first.clone(); 391 | 392 | assert_eq!(first.as_bytes(), b"abcde"); 393 | assert_eq!(&*first.into_mbox(), "abcde"); 394 | assert_eq!(second.as_bytes_with_sentinel(), b"abcde\0"); 395 | assert_eq!(&*second.into_mbox_with_sentinel(), "abcde\0"); 396 | } 397 | 398 | #[cfg(not(windows))] 399 | #[test] 400 | fn test_default_array() { 401 | let arr = MArray::::default(); 402 | assert_eq!(arr.into_mbox_with_sentinel(), MBox::from_slice(&[0u64])); 403 | } 404 | 405 | #[test] 406 | fn test_default_string() { 407 | let string = MString::default(); 408 | assert_eq!(string.into_mbox_with_sentinel(), MBox::::from("\0")); 409 | } 410 | 411 | #[cfg(feature = "std")] 412 | #[test] 413 | fn test_hash_string() { 414 | use std::collections::HashSet; 415 | 416 | let mut hs: HashSet = HashSet::new(); 417 | hs.insert(MString::from("a")); 418 | hs.insert(MString::from("bcd")); 419 | 420 | let hs = hs; 421 | assert!(hs.contains("bcd")); 422 | assert!(!hs.contains("ef")); 423 | assert!(!hs.contains("bcd\0")); 424 | assert!(hs.contains("a")); 425 | assert!(hs.contains(&MString::from("bcd"))); 426 | assert!(!hs.contains(&MString::from("ef"))); 427 | assert!(hs.contains(&MString::from("a"))); 428 | } 429 | 430 | #[cfg(feature = "std")] 431 | #[test] 432 | fn test_hash_array() { 433 | use std::collections::HashSet; 434 | 435 | let mut hs: HashSet> = HashSet::new(); 436 | hs.insert(MArray::from_slice(b"a")); 437 | hs.insert(MArray::from_slice(b"bcd")); 438 | 439 | let hs = hs; 440 | assert!(hs.contains(&b"bcd"[..])); 441 | assert!(!hs.contains(&b"ef"[..])); 442 | assert!(!hs.contains(&b"bcd\0"[..])); 443 | assert!(hs.contains(&b"a"[..])); 444 | assert!(hs.contains(&MArray::from_slice(b"bcd"))); 445 | assert!(!hs.contains(&MArray::from_slice(b"ef"))); 446 | assert!(hs.contains(&MArray::from_slice(b"a"))); 447 | } 448 | -------------------------------------------------------------------------------- /src/mbox.rs: -------------------------------------------------------------------------------- 1 | //! `malloc`-based Box. 2 | 3 | #[cfg(feature = "stable_deref_trait")] 4 | use stable_deref_trait::StableDeref; 5 | 6 | use std::cmp::Ordering; 7 | use std::convert::{AsMut, AsRef}; 8 | use std::fmt::{Debug, Display, Formatter, Pointer, Result as FormatResult}; 9 | use std::hash::{Hash, Hasher}; 10 | use std::iter::{DoubleEndedIterator, FromIterator, IntoIterator}; 11 | use std::marker::Unpin; 12 | use std::mem::{forget, MaybeUninit}; 13 | use std::ops::{Deref, DerefMut}; 14 | use std::pin::Pin; 15 | use std::ptr::{copy_nonoverlapping, drop_in_place, read, write}; 16 | use std::slice::{Iter, IterMut}; 17 | use std::str::{from_utf8, Utf8Error}; 18 | use std::{ 19 | borrow::{Borrow, BorrowMut}, 20 | ptr::NonNull, 21 | }; 22 | 23 | use crate::internal::{gen_free, gen_malloc, gen_realloc, Unique}; 24 | 25 | #[cfg(all(test, not(windows)))] 26 | use crate::internal::DropCounter; 27 | #[cfg(test)] 28 | use crate::internal::PanicOnClone; 29 | #[cfg(test)] 30 | use std::iter::{once, repeat}; 31 | #[cfg(all(test, not(windows)))] 32 | use std::mem::size_of; 33 | 34 | #[cfg(feature = "nightly")] 35 | use std::marker::Unsize; 36 | #[cfg(feature = "nightly")] 37 | use std::ops::CoerceUnsized; 38 | 39 | use crate::free::Free; 40 | 41 | //{{{ Basic structure ----------------------------------------------------------------------------- 42 | 43 | /// A malloc-backed box. This structure allows Rust to exchange objects with C without cloning. 44 | pub struct MBox(Unique); 45 | 46 | impl MBox { 47 | /// Constructs a new malloc-backed box from a pointer allocated by `malloc`. 48 | /// 49 | /// # Safety 50 | /// 51 | /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is 52 | /// expected to be deallocated using `free()`. It must be aligned and not null. The content of the pointer 53 | /// must be already initialized. The pointer's ownership is passed into the box, and thus should 54 | /// not be used after this function returns. 55 | /// 56 | /// Note that even when `T` is zero-sized, the input `ptr` is *still* expected to be released using 57 | /// `free()`. Therefore, you must not use a conceived dangling pointer such as `NonNull::dangling()` 58 | /// here. Consider using `malloc(1)` in case of ZSTs. 59 | pub unsafe fn from_raw(ptr: *mut T) -> Self { 60 | Self::from_non_null_raw(NonNull::new_unchecked(ptr)) 61 | } 62 | 63 | /// Constructs a new malloc-backed box from a non-null pointer allocated by `malloc`. 64 | /// 65 | /// # Safety 66 | /// 67 | /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is 68 | /// expected to be deallocated using `free()`. The content of the pointer must be already 69 | /// initialized. The pointer's ownership is passed into the box, and thus should not be used 70 | /// after this function returns. 71 | /// 72 | /// Note that even when `T` is zero-sized, the input `ptr` is *still* expected to be released using 73 | /// `free()`. Therefore, you must not use a conceived dangling pointer such as `NonNull::dangling()` 74 | /// here. Consider using `malloc(1)` in case of ZSTs. 75 | pub unsafe fn from_non_null_raw(ptr: NonNull) -> Self { 76 | Self(Unique::new(ptr)) 77 | } 78 | 79 | /// Obtains the pointer owned by the box. 80 | pub fn as_ptr(boxed: &Self) -> *const T { 81 | boxed.0.as_non_null_ptr().as_ptr() 82 | } 83 | 84 | /// Obtains the mutable pointer owned by the box. 85 | pub fn as_mut_ptr(boxed: &mut Self) -> *mut T { 86 | boxed.0.as_non_null_ptr().as_ptr() 87 | } 88 | 89 | /// Consumes the box and returns the original pointer. 90 | /// 91 | /// The caller is responsible for `free`ing the pointer after this. 92 | pub fn into_raw(boxed: Self) -> *mut T { 93 | Self::into_non_null_raw(boxed).as_ptr() 94 | } 95 | 96 | /// Consumes the box and returns the original non-null pointer. 97 | /// 98 | /// The caller is responsible for `free`ing the pointer after this. 99 | pub fn into_non_null_raw(boxed: Self) -> NonNull { 100 | let ptr = boxed.0.as_non_null_ptr(); 101 | forget(boxed); 102 | ptr 103 | } 104 | } 105 | 106 | impl Drop for MBox { 107 | fn drop(&mut self) { 108 | // SAFETY: the pointer is assumed to be obtained from `malloc()`. 109 | unsafe { T::free(self.0.as_non_null_ptr()) }; 110 | } 111 | } 112 | 113 | impl Deref for MBox { 114 | type Target = T; 115 | fn deref(&self) -> &T { 116 | unsafe { &*Self::as_ptr(self) } 117 | } 118 | } 119 | 120 | #[cfg(feature = "stable_deref_trait")] 121 | unsafe impl StableDeref for MBox {} 122 | 123 | impl Unpin for MBox {} 124 | 125 | impl DerefMut for MBox { 126 | fn deref_mut(&mut self) -> &mut T { 127 | unsafe { &mut *Self::as_mut_ptr(self) } 128 | } 129 | } 130 | 131 | impl AsRef for MBox { 132 | fn as_ref(&self) -> &T { 133 | self 134 | } 135 | } 136 | 137 | impl AsMut for MBox { 138 | fn as_mut(&mut self) -> &mut T { 139 | self 140 | } 141 | } 142 | 143 | impl Borrow for MBox { 144 | fn borrow(&self) -> &T { 145 | self 146 | } 147 | } 148 | 149 | impl BorrowMut for MBox { 150 | fn borrow_mut(&mut self) -> &mut T { 151 | self 152 | } 153 | } 154 | 155 | #[cfg(feature = "nightly")] 156 | impl, U: ?Sized + Free> CoerceUnsized> for MBox {} 157 | 158 | impl Pointer for MBox { 159 | fn fmt(&self, formatter: &mut Formatter) -> FormatResult { 160 | Pointer::fmt(&Self::as_ptr(self), formatter) 161 | } 162 | } 163 | 164 | impl Debug for MBox { 165 | fn fmt(&self, formatter: &mut Formatter) -> FormatResult { 166 | self.deref().fmt(formatter) 167 | } 168 | } 169 | 170 | impl Display for MBox { 171 | fn fmt(&self, formatter: &mut Formatter) -> FormatResult { 172 | self.deref().fmt(formatter) 173 | } 174 | } 175 | 176 | impl Hash for MBox { 177 | fn hash(&self, state: &mut H) { 178 | self.deref().hash(state) 179 | } 180 | } 181 | 182 | impl> PartialEq> for MBox { 183 | fn eq(&self, other: &MBox) -> bool { 184 | self.deref().eq(other.deref()) 185 | } 186 | } 187 | 188 | impl Eq for MBox {} 189 | 190 | impl> PartialOrd> for MBox { 191 | fn partial_cmp(&self, other: &MBox) -> Option { 192 | self.deref().partial_cmp(other.deref()) 193 | } 194 | } 195 | 196 | impl Ord for MBox { 197 | fn cmp(&self, other: &Self) -> Ordering { 198 | self.deref().cmp(other.deref()) 199 | } 200 | } 201 | 202 | //}}} 203 | 204 | //{{{ Single object ------------------------------------------------------------------------------- 205 | 206 | impl MBox { 207 | /// Constructs a new malloc-backed box, and move an initialized value into it. 208 | pub fn new(value: T) -> Self { 209 | let storage = gen_malloc(1); 210 | // SAFETY: the `storage` is uninitialized and enough to store T. 211 | // this pointer is obtained via `malloc` and thus good for `from_raw`. 212 | unsafe { 213 | write(storage.as_ptr(), value); 214 | Self::from_non_null_raw(storage) 215 | } 216 | } 217 | 218 | /// Constructs a new malloc-backed box with uninitialized content. 219 | pub fn new_uninit() -> MBox> { 220 | let storage = gen_malloc(1); 221 | // SAFETY: The storage is allowed to be uninitialized. 222 | unsafe { MBox::from_non_null_raw(storage) } 223 | } 224 | 225 | /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then `value` will be 226 | /// pinned in memory and cannot be moved. 227 | pub fn pin(value: T) -> Pin { 228 | Self::into_pin(Self::new(value)) 229 | } 230 | 231 | /// Converts an `MBox` into a single-item `MBox<[T]>`. 232 | /// 233 | /// This conversion does not allocate on the heap and happens in place. 234 | pub fn into_boxed_slice(boxed: Self) -> MBox<[T]> { 235 | // SAFETY: free() only cares about the allocated size, and `T` and 236 | // `[T; 1]` are equivalent in terms of drop() and free(). 237 | unsafe { MBox::from_raw_parts(Self::into_raw(boxed), 1) } 238 | } 239 | 240 | /// Consumes the `MBox`, returning the wrapped value. 241 | pub fn into_inner(boxed: Self) -> T { 242 | let mut dst = MaybeUninit::uninit(); 243 | let src = Self::into_non_null_raw(boxed); 244 | // SAFETY: after calling `into_raw` above, we have the entire ownership of the malloc'ed 245 | // pointer `src`. The content is moved into the destination. After that, we can free `src` 246 | // without touching the content. So there is a single copy of the content fully initialized 247 | // into `dst` which is safe to assume_init. 248 | unsafe { 249 | copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), 1); 250 | gen_free(src); 251 | dst.assume_init() 252 | } 253 | } 254 | 255 | /// Converts an `MBox` into a `Pin>`. 256 | /// 257 | /// This conversion does not allocate on the heap and happens in place. 258 | pub fn into_pin(boxed: Self) -> Pin { 259 | // SAFETY: Same reason as why `Box::into_pin` is safe. 260 | unsafe { Pin::new_unchecked(boxed) } 261 | } 262 | 263 | /// Consumes and leaks the `MBox`, returning a mutable reference, `&'a mut T`. 264 | pub fn leak<'a>(boxed: Self) -> &'a mut T 265 | where 266 | T: 'a, 267 | { 268 | // SAFETY: into_raw takes the ownership of the box, which is then immediately leaked. Thus, 269 | // no one is able to call `gen_free` on this pointer and thus safe to be used in the rest of 270 | // its lifetime. 271 | unsafe { &mut *Self::into_non_null_raw(boxed).as_ptr() } 272 | } 273 | } 274 | 275 | impl MBox> { 276 | /// Converts into an initialized box. 277 | /// 278 | /// # Safety 279 | /// 280 | /// The caller should guarantee `*self` is indeed initialized. 281 | pub unsafe fn assume_init(self) -> MBox { 282 | MBox::from_non_null_raw(Self::into_non_null_raw(self).cast()) 283 | } 284 | } 285 | 286 | impl From for MBox { 287 | fn from(value: T) -> MBox { 288 | MBox::new(value) 289 | } 290 | } 291 | 292 | impl Clone for MBox { 293 | fn clone(&self) -> MBox { 294 | Self::new(self.deref().clone()) 295 | } 296 | 297 | fn clone_from(&mut self, source: &Self) { 298 | self.deref_mut().clone_from(source); 299 | } 300 | } 301 | 302 | impl Default for MBox { 303 | fn default() -> MBox { 304 | MBox::new(T::default()) 305 | } 306 | } 307 | 308 | #[cfg(not(windows))] 309 | #[test] 310 | fn test_single_object() { 311 | let counter = DropCounter::default(); 312 | { 313 | let mbox = MBox::new(counter.clone()); 314 | counter.assert_eq(0); 315 | drop(mbox); 316 | } 317 | counter.assert_eq(1); 318 | } 319 | 320 | #[test] 321 | fn test_into_raw() { 322 | let mbox = MBox::new(66u8); 323 | let raw = MBox::into_raw(mbox); 324 | unsafe { 325 | assert_eq!(*raw, 66u8); 326 | gen_free(NonNull::new(raw).unwrap()); 327 | } 328 | } 329 | 330 | #[cfg(not(windows))] 331 | #[test] 332 | fn test_clone() { 333 | let counter = DropCounter::default(); 334 | { 335 | let first_mbox = MBox::new(counter.clone()); 336 | { 337 | let second_mbox = first_mbox.clone(); 338 | counter.assert_eq(0); 339 | drop(second_mbox); 340 | } 341 | counter.assert_eq(1); 342 | } 343 | counter.assert_eq(2); 344 | } 345 | 346 | #[cfg(not(windows))] 347 | #[test] 348 | fn test_clone_from() { 349 | let counter = DropCounter::default(); 350 | { 351 | let first_mbox = MBox::new(counter.clone()); 352 | { 353 | let mut second_mbox = MBox::new(counter.clone()); 354 | counter.assert_eq(0); 355 | second_mbox.clone_from(&first_mbox); 356 | counter.assert_eq(1); 357 | } 358 | counter.assert_eq(2); 359 | } 360 | counter.assert_eq(3); 361 | } 362 | 363 | #[cfg(not(windows))] 364 | #[test] 365 | fn test_no_drop_flag() { 366 | fn do_test_for_drop_flag(branch: bool, expected: usize) { 367 | let counter = DropCounter::default(); 368 | let inner_counter = counter.deref().clone(); 369 | { 370 | let mbox; 371 | if branch { 372 | mbox = MBox::new(counter.clone()); 373 | let _ = &mbox; 374 | } 375 | inner_counter.assert_eq(0); 376 | } 377 | inner_counter.assert_eq(expected); 378 | } 379 | 380 | do_test_for_drop_flag(true, 1); 381 | do_test_for_drop_flag(false, 0); 382 | 383 | assert_eq!( 384 | size_of::>(), 385 | size_of::<*mut DropCounter>() 386 | ); 387 | } 388 | 389 | #[cfg(feature = "std")] 390 | #[test] 391 | fn test_format() { 392 | let a = MBox::new(3u8); 393 | assert_eq!(format!("{:p}", a), format!("{:p}", MBox::as_ptr(&a))); 394 | assert_eq!(format!("{}", a), "3"); 395 | assert_eq!(format!("{:?}", a), "3"); 396 | } 397 | 398 | #[test] 399 | fn test_standard_traits() { 400 | let mut a = MBox::new(0u8); 401 | assert_eq!(*a, 0); 402 | *a = 3; 403 | assert_eq!(*a, 3); 404 | assert_eq!(*a.as_ref(), 3); 405 | assert_eq!(*a.as_mut(), 3); 406 | assert_eq!(*(a.borrow() as &u8), 3); 407 | assert_eq!(*(a.borrow_mut() as &mut u8), 3); 408 | assert!(a == MBox::new(3u8)); 409 | assert!(a != MBox::new(0u8)); 410 | assert!(a < MBox::new(4u8)); 411 | assert!(a > MBox::new(2u8)); 412 | assert!(a <= MBox::new(4u8)); 413 | assert!(a >= MBox::new(2u8)); 414 | assert_eq!(a.cmp(&MBox::new(7u8)), Ordering::Less); 415 | assert_eq!(MBox::::default(), MBox::new(0u8)); 416 | } 417 | 418 | #[test] 419 | fn test_zero_sized_type() { 420 | let a = MBox::new(()); 421 | assert!(!MBox::as_ptr(&a).is_null()); 422 | } 423 | 424 | #[cfg(not(windows))] 425 | #[test] 426 | fn test_non_zero() { 427 | let b = 0u64; 428 | assert!(!Some(MBox::new(0u64)).is_none()); 429 | assert!(!Some(MBox::new(())).is_none()); 430 | assert!(!Some(MBox::new(&b)).is_none()); 431 | 432 | assert_eq!(size_of::>>(), size_of::>()); 433 | assert_eq!(size_of::>>(), size_of::>()); 434 | assert_eq!( 435 | size_of::>>(), 436 | size_of::>() 437 | ); 438 | } 439 | 440 | #[cfg(not(windows))] 441 | #[test] 442 | fn test_aligned() { 443 | use std::mem::align_of; 444 | 445 | let b = MBox::new(1u16); 446 | assert_eq!(MBox::as_ptr(&b) as usize % align_of::(), 0); 447 | 448 | let b = MBox::new(1u32); 449 | assert_eq!(MBox::as_ptr(&b) as usize % align_of::(), 0); 450 | 451 | let b = MBox::new(1u64); 452 | assert_eq!(MBox::as_ptr(&b) as usize % align_of::(), 0); 453 | 454 | #[repr(C, align(4096))] 455 | struct A(u8); 456 | 457 | let b = MBox::new(A(2)); 458 | assert_eq!(MBox::as_ptr(&b) as usize % 4096, 0); 459 | } 460 | 461 | //}}} 462 | 463 | //{{{ Slice helpers ------------------------------------------------------------------------------- 464 | 465 | mod slice_helper { 466 | use super::*; 467 | 468 | /// A `Vec`-like structure backed by `malloc()`. 469 | pub struct MSliceBuilder { 470 | ptr: NonNull, 471 | cap: usize, 472 | len: usize, 473 | } 474 | 475 | impl MSliceBuilder { 476 | /// Creates a new slice builder with an initial capacity. 477 | pub fn with_capacity(cap: usize) -> MSliceBuilder { 478 | MSliceBuilder { 479 | ptr: gen_malloc(cap), 480 | cap, 481 | len: 0, 482 | } 483 | } 484 | 485 | pub fn push(&mut self, obj: T) { 486 | if self.len >= self.cap { 487 | let new_cap = (self.cap * 2).max(1); 488 | // SAFETY: 489 | // - ptr is initialized from gen_malloc() so it can be placed into gen_realloc() 490 | unsafe { 491 | self.ptr = gen_realloc(self.ptr, self.cap, new_cap); 492 | } 493 | self.cap = new_cap; 494 | } 495 | 496 | // SAFETY: 497 | // - we guarantee that `ptr `points to an array of nonzero length `cap`, and 498 | // the `if` condition ensures the invariant `self.len < cap`, so 499 | // `ptr.add(self.len)` is always a valid (but uninitialized) object. 500 | // - since `ptr[self.len]` is not yet initialized, we can `write()` into it safely. 501 | unsafe { 502 | write(self.ptr.as_ptr().add(self.len), obj); 503 | } 504 | self.len += 1; 505 | } 506 | 507 | pub fn into_mboxed_slice(self) -> MBox<[T]> { 508 | // SAFETY: `self.ptr` has been allocated by malloc(), and its length is self.cap 509 | // (>= self.len). 510 | let slice = unsafe { MBox::from_raw_parts(self.ptr.as_ptr(), self.len) }; 511 | forget(self); 512 | slice 513 | } 514 | } 515 | 516 | impl MSliceBuilder> { 517 | /// Sets the length of the builder to the same as the capacity. The elements in the 518 | /// uninitialized tail remains uninitialized. 519 | pub fn set_len_to_cap(&mut self) { 520 | self.len = self.cap; 521 | } 522 | } 523 | 524 | impl Drop for MSliceBuilder { 525 | fn drop(&mut self) { 526 | // SAFETY: `ptr` has been allocated by `gen_malloc()`. 527 | unsafe { 528 | gen_free(self.ptr); 529 | } 530 | } 531 | } 532 | 533 | #[repr(C)] 534 | struct SliceParts { 535 | ptr: *mut T, 536 | len: usize, 537 | } 538 | 539 | impl Clone for SliceParts { 540 | fn clone(&self) -> Self { 541 | Self { 542 | ptr: self.ptr, 543 | len: self.len, 544 | } 545 | } 546 | } 547 | impl Copy for SliceParts {} 548 | 549 | #[repr(C)] 550 | union SliceTransformer { 551 | fat_ptr: *mut [T], 552 | parts: SliceParts, 553 | } 554 | 555 | // TODO: maybe upgrade Rust to 1.42 to get rid of this function. 556 | pub fn slice_from_raw_parts_mut(ptr: *mut T, len: usize) -> *mut [T] { 557 | // SAFETY: just the same code of the function from std. 558 | unsafe { 559 | SliceTransformer { 560 | parts: SliceParts { ptr, len }, 561 | } 562 | .fat_ptr 563 | } 564 | } 565 | 566 | pub fn slice_into_raw_parts_mut(fat_ptr: *mut [T]) -> (*mut T, usize) { 567 | let parts = unsafe { SliceTransformer { fat_ptr }.parts }; 568 | (parts.ptr, parts.len) 569 | } 570 | } 571 | 572 | use self::slice_helper::{slice_from_raw_parts_mut, slice_into_raw_parts_mut, MSliceBuilder}; 573 | 574 | /// The iterator returned from `MBox<[T]>::into_iter()`. 575 | pub struct MSliceIntoIter { 576 | ptr: NonNull, 577 | begin: usize, 578 | end: usize, 579 | } 580 | 581 | impl Iterator for MSliceIntoIter { 582 | type Item = T; 583 | 584 | fn next(&mut self) -> Option { 585 | if self.begin == self.end { 586 | None 587 | } else { 588 | unsafe { 589 | let ptr = self.ptr.as_ptr().add(self.begin); 590 | self.begin += 1; 591 | Some(read(ptr)) 592 | } 593 | } 594 | } 595 | 596 | fn size_hint(&self) -> (usize, Option) { 597 | let len = self.end - self.begin; 598 | (len, Some(len)) 599 | } 600 | } 601 | 602 | impl DoubleEndedIterator for MSliceIntoIter { 603 | fn next_back(&mut self) -> Option { 604 | if self.begin == self.end { 605 | None 606 | } else { 607 | unsafe { 608 | self.end -= 1; 609 | let ptr = self.ptr.as_ptr().add(self.end); 610 | Some(read(ptr)) 611 | } 612 | } 613 | } 614 | } 615 | 616 | unsafe impl Send for MSliceIntoIter {} 617 | unsafe impl Sync for MSliceIntoIter {} 618 | 619 | impl ExactSizeIterator for MSliceIntoIter {} 620 | 621 | impl Drop for MSliceIntoIter { 622 | fn drop(&mut self) { 623 | unsafe { 624 | let base = self.ptr.as_ptr().add(self.begin); 625 | let len = self.end - self.begin; 626 | let slice = slice_from_raw_parts_mut(base, len); 627 | drop_in_place(slice); 628 | gen_free(self.ptr); 629 | } 630 | } 631 | } 632 | 633 | //}}} 634 | 635 | //{{{ Slice --------------------------------------------------------------------------------------- 636 | 637 | impl MBox<[T]> { 638 | /// Constructs a new malloc-backed slice from the pointer and the length (number of items). 639 | /// 640 | /// # Safety 641 | /// 642 | /// `ptr` must be allocated via `malloc()` or similar C functions. It must be aligned and not null. 643 | /// 644 | /// The `malloc`ed size of the pointer must be at least `len * size_of::()`. The content 645 | /// must already been initialized. 646 | pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { 647 | Self::from_raw(slice_from_raw_parts_mut(ptr, len)) 648 | } 649 | 650 | /// Constructs a new boxed slice with uninitialized contents. 651 | pub fn new_uninit_slice(len: usize) -> MBox<[MaybeUninit]> { 652 | let mut builder = MSliceBuilder::with_capacity(len); 653 | builder.set_len_to_cap(); 654 | builder.into_mboxed_slice() 655 | } 656 | 657 | /// Decomposes the boxed slice into a pointer to the first element and the slice length. 658 | pub fn into_raw_parts(mut self) -> (*mut T, usize) { 659 | let (ptr, len) = slice_into_raw_parts_mut(Self::as_mut_ptr(&mut self)); 660 | forget(self); 661 | (ptr, len) 662 | } 663 | } 664 | 665 | impl MBox<[MaybeUninit]> { 666 | /// Converts into an initialized boxed slice. 667 | /// 668 | /// # Safety 669 | /// 670 | /// The caller should guarantee `*self` is indeed initialized. 671 | pub unsafe fn assume_init(self) -> MBox<[T]> { 672 | MBox::from_raw(Self::into_raw(self) as *mut [T]) 673 | } 674 | } 675 | 676 | impl Default for MBox<[T]> { 677 | fn default() -> Self { 678 | unsafe { Self::from_raw_parts(gen_malloc(0).as_ptr(), 0) } 679 | } 680 | } 681 | 682 | impl Clone for MBox<[T]> { 683 | fn clone(&self) -> Self { 684 | Self::from_slice(self) 685 | } 686 | } 687 | 688 | impl MBox<[T]> { 689 | /// Creates a new `malloc`-boxed slice by cloning the content of an existing slice. 690 | pub fn from_slice(slice: &[T]) -> MBox<[T]> { 691 | let mut builder = MSliceBuilder::with_capacity(slice.len()); 692 | for item in slice { 693 | builder.push(item.clone()); 694 | } 695 | builder.into_mboxed_slice() 696 | } 697 | } 698 | 699 | impl FromIterator for MBox<[T]> { 700 | fn from_iter>(iter: I) -> Self { 701 | let iter = iter.into_iter(); 702 | let (lower_size, upper_size) = iter.size_hint(); 703 | let initial_capacity = upper_size.unwrap_or(lower_size).max(1); 704 | let mut builder = MSliceBuilder::with_capacity(initial_capacity); 705 | for item in iter { 706 | builder.push(item); 707 | } 708 | builder.into_mboxed_slice() 709 | } 710 | } 711 | 712 | impl IntoIterator for MBox<[T]> { 713 | type Item = T; 714 | type IntoIter = MSliceIntoIter; 715 | fn into_iter(self) -> MSliceIntoIter { 716 | let (ptr, len) = self.into_raw_parts(); 717 | MSliceIntoIter { 718 | ptr: unsafe { NonNull::new_unchecked(ptr) }, 719 | begin: 0, 720 | end: len, 721 | } 722 | } 723 | } 724 | 725 | impl<'a, T> IntoIterator for &'a MBox<[T]> { 726 | type Item = &'a T; 727 | type IntoIter = Iter<'a, T>; 728 | fn into_iter(self) -> Iter<'a, T> { 729 | self.iter() 730 | } 731 | } 732 | 733 | impl<'a, T> IntoIterator for &'a mut MBox<[T]> { 734 | type Item = &'a mut T; 735 | type IntoIter = IterMut<'a, T>; 736 | fn into_iter(self) -> IterMut<'a, T> { 737 | self.iter_mut() 738 | } 739 | } 740 | 741 | #[cfg(not(windows))] 742 | #[test] 743 | fn test_slice() { 744 | unsafe { 745 | let slice_content = gen_malloc::(5).as_ptr(); 746 | *slice_content.offset(0) = 16458340076686561191; 747 | *slice_content.offset(1) = 15635007859502065083; 748 | *slice_content.offset(2) = 4845947824042606450; 749 | *slice_content.offset(3) = 8907026173756975745; 750 | *slice_content.offset(4) = 7378932587879886134; 751 | let mbox = MBox::from_raw_parts(slice_content, 5); 752 | assert_eq!( 753 | &mbox as &[u64], 754 | &[ 755 | 16458340076686561191, 756 | 15635007859502065083, 757 | 4845947824042606450, 758 | 8907026173756975745, 759 | 7378932587879886134 760 | ] 761 | ); 762 | } 763 | } 764 | 765 | #[cfg(not(windows))] 766 | #[test] 767 | fn test_slice_with_drops() { 768 | let counter = DropCounter::default(); 769 | unsafe { 770 | let slice_content = gen_malloc::(3).as_ptr(); 771 | { 772 | write(slice_content.offset(0), counter.clone()); 773 | write(slice_content.offset(1), counter.clone()); 774 | write(slice_content.offset(2), counter.clone()); 775 | } 776 | counter.assert_eq(0); 777 | let mbox = MBox::from_raw_parts(slice_content, 3); 778 | mbox[0].assert_eq(0); 779 | mbox[1].assert_eq(0); 780 | mbox[2].assert_eq(0); 781 | assert_eq!(mbox.len(), 3); 782 | } 783 | counter.assert_eq(3); 784 | } 785 | 786 | #[cfg(feature = "nightly")] 787 | #[test] 788 | fn test_coerce_unsized() { 789 | let counter = DropCounter::default(); 790 | { 791 | let pre_box = MBox::new([counter.clone(), counter.clone()]); 792 | counter.assert_eq(0); 793 | pre_box[0].assert_eq(0); 794 | pre_box[1].assert_eq(0); 795 | assert_eq!(pre_box.len(), 2); 796 | 797 | let post_box: MBox<[DropCounter]> = pre_box; 798 | counter.assert_eq(0); 799 | post_box[0].assert_eq(0); 800 | post_box[1].assert_eq(0); 801 | assert_eq!(post_box.len(), 2); 802 | } 803 | counter.assert_eq(2); 804 | } 805 | 806 | #[cfg(not(windows))] 807 | #[test] 808 | #[allow(useless_ptr_null_checks)] 809 | fn test_empty_slice() { 810 | let mbox = MBox::<[DropCounter]>::default(); 811 | let sl: &[DropCounter] = &mbox; 812 | assert_eq!(sl.len(), 0); 813 | assert!(!sl.as_ptr().is_null()); 814 | } 815 | 816 | #[cfg(all(feature = "nightly", not(windows)))] 817 | #[test] 818 | #[allow(useless_ptr_null_checks)] 819 | fn test_coerce_from_empty_slice() { 820 | let pre_box = MBox::<[DropCounter; 0]>::new([]); 821 | assert_eq!(pre_box.len(), 0); 822 | assert!(!pre_box.as_ptr().is_null()); 823 | 824 | let post_box: MBox<[DropCounter]> = pre_box; 825 | let sl: &[DropCounter] = &post_box; 826 | assert_eq!(sl.len(), 0); 827 | assert!(!sl.as_ptr().is_null()); 828 | } 829 | 830 | #[cfg(not(windows))] 831 | #[test] 832 | fn test_clone_slice() { 833 | let counter = DropCounter::default(); 834 | unsafe { 835 | let slice_content = gen_malloc::(3).as_ptr(); 836 | { 837 | write(slice_content.offset(0), counter.clone()); 838 | write(slice_content.offset(1), counter.clone()); 839 | write(slice_content.offset(2), counter.clone()); 840 | } 841 | let mbox = MBox::from_raw_parts(slice_content, 3); 842 | assert_eq!(mbox.len(), 3); 843 | 844 | { 845 | let cloned_mbox = mbox.clone(); 846 | counter.assert_eq(0); 847 | assert_eq!(cloned_mbox.len(), 3); 848 | cloned_mbox[0].assert_eq(0); 849 | cloned_mbox[1].assert_eq(0); 850 | cloned_mbox[2].assert_eq(0); 851 | } 852 | 853 | counter.assert_eq(3); 854 | mbox[0].assert_eq(3); 855 | mbox[1].assert_eq(3); 856 | mbox[2].assert_eq(3); 857 | } 858 | 859 | counter.assert_eq(6); 860 | } 861 | 862 | #[cfg(not(windows))] 863 | #[test] 864 | fn test_from_iterator() { 865 | let counter = DropCounter::default(); 866 | { 867 | let slice = repeat(counter.clone()).take(18).collect::>(); 868 | counter.assert_eq(1); 869 | assert_eq!(slice.len(), 18); 870 | for c in &slice { 871 | c.assert_eq(1); 872 | } 873 | } 874 | counter.assert_eq(19); 875 | } 876 | 877 | #[test] 878 | fn test_from_iterator_with_no_size_hint() { 879 | struct RedactSizeHint(I); 880 | 881 | impl Iterator for RedactSizeHint { 882 | type Item = I::Item; 883 | 884 | fn next(&mut self) -> Option { 885 | self.0.next() 886 | } 887 | } 888 | 889 | let it = RedactSizeHint(b"1234567890".iter().copied()); 890 | assert_eq!(it.size_hint(), (0, None)); 891 | let slice = it.collect::>(); 892 | assert_eq!(&*slice, b"1234567890"); 893 | } 894 | 895 | #[cfg(not(windows))] 896 | #[test] 897 | fn test_into_iterator() { 898 | let counter = DropCounter::default(); 899 | { 900 | let slice = repeat(counter.clone()).take(18).collect::>(); 901 | counter.assert_eq(1); 902 | assert_eq!(slice.len(), 18); 903 | for (c, i) in slice.into_iter().zip(1..) { 904 | c.assert_eq(i); 905 | } 906 | } 907 | counter.assert_eq(19); 908 | } 909 | 910 | #[cfg(feature = "std")] 911 | #[test] 912 | fn test_iter_properties() { 913 | let slice = vec![1i8, 4, 9, 16, 25].into_iter().collect::>(); 914 | let mut iter = slice.into_iter(); 915 | assert_eq!(iter.size_hint(), (5, Some(5))); 916 | assert_eq!(iter.len(), 5); 917 | assert_eq!(iter.next(), Some(1)); 918 | assert_eq!(iter.next_back(), Some(25)); 919 | assert_eq!(iter.size_hint(), (3, Some(3))); 920 | assert_eq!(iter.len(), 3); 921 | assert_eq!(iter.collect::>(), vec![4, 9, 16]); 922 | } 923 | 924 | #[cfg(not(windows))] 925 | #[test] 926 | fn test_iter_drop() { 927 | let counter = DropCounter::default(); 928 | { 929 | let slice = repeat(counter.clone()).take(18).collect::>(); 930 | counter.assert_eq(1); 931 | assert_eq!(slice.len(), 18); 932 | 933 | let mut iter = slice.into_iter(); 934 | counter.assert_eq(1); 935 | { 936 | iter.next().unwrap().assert_eq(1) 937 | }; 938 | { 939 | iter.next().unwrap().assert_eq(2) 940 | }; 941 | { 942 | iter.next_back().unwrap().assert_eq(3) 943 | }; 944 | counter.assert_eq(4); 945 | } 946 | counter.assert_eq(19); 947 | } 948 | 949 | #[test] 950 | fn test_zst_slice() { 951 | let slice = repeat(()).take(7).collect::>(); 952 | let _ = slice.clone(); 953 | slice.into_iter(); 954 | } 955 | 956 | #[test] 957 | #[should_panic(expected = "panic on clone")] 958 | fn test_panic_during_clone() { 959 | let mbox = MBox::::default(); 960 | let _ = mbox.clone(); 961 | } 962 | 963 | #[test] 964 | #[should_panic(expected = "panic on clone")] 965 | fn test_panic_during_clone_from() { 966 | let mut mbox = MBox::::default(); 967 | let other = MBox::default(); 968 | mbox.clone_from(&other); 969 | } 970 | 971 | //}}} 972 | 973 | //{{{ UTF-8 String -------------------------------------------------------------------------------- 974 | 975 | impl MBox { 976 | /// Constructs a new malloc-backed string from the pointer and the length (number of UTF-8 code 977 | /// units). 978 | /// 979 | /// # Safety 980 | /// 981 | /// The `malloc`ed size of the pointer must be at least `len`. The content must already been 982 | /// initialized and be valid UTF-8. 983 | pub unsafe fn from_raw_utf8_parts_unchecked(value: *mut u8, len: usize) -> MBox { 984 | Self::from_utf8_unchecked(MBox::from_raw_parts(value, len)) 985 | } 986 | 987 | /// Constructs a new malloc-backed string from the pointer and the length (number of UTF-8 code 988 | /// units). If the content does not contain valid UTF-8, this method returns an `Err`. 989 | /// 990 | /// # Safety 991 | /// 992 | /// The `malloc`ed size of the pointer must be at least `len`. 993 | /// The content must already been initialized. 994 | pub unsafe fn from_raw_utf8_parts(value: *mut u8, len: usize) -> Result, Utf8Error> { 995 | Self::from_utf8(MBox::from_raw_parts(value, len)) 996 | } 997 | 998 | /// Converts the string into raw bytes. 999 | pub fn into_bytes(self) -> MBox<[u8]> { 1000 | unsafe { MBox::from_raw(Self::into_raw(self) as *mut [u8]) } 1001 | } 1002 | 1003 | /// Creates a string from raw bytes. 1004 | /// 1005 | /// # Safety 1006 | /// 1007 | /// The raw bytes must be valid UTF-8. 1008 | pub unsafe fn from_utf8_unchecked(bytes: MBox<[u8]>) -> MBox { 1009 | Self::from_raw(MBox::into_raw(bytes) as *mut str) 1010 | } 1011 | 1012 | /// Creates a string from raw bytes. If the content does not contain valid UTF-8, this method 1013 | /// returns an `Err`. 1014 | pub fn from_utf8(bytes: MBox<[u8]>) -> Result, Utf8Error> { 1015 | from_utf8(&bytes)?; 1016 | unsafe { Ok(Self::from_utf8_unchecked(bytes)) } 1017 | } 1018 | } 1019 | 1020 | impl Default for MBox { 1021 | fn default() -> Self { 1022 | unsafe { Self::from_raw_utf8_parts_unchecked(gen_malloc(0).as_ptr(), 0) } 1023 | } 1024 | } 1025 | 1026 | impl Clone for MBox { 1027 | fn clone(&self) -> Self { 1028 | Self::from(&**self) 1029 | } 1030 | } 1031 | 1032 | impl From<&str> for MBox { 1033 | /// Creates a new `malloc`-boxed string by cloning the content of an existing string slice. 1034 | fn from(string: &str) -> Self { 1035 | let len = string.len(); 1036 | let new_slice = gen_malloc(len).as_ptr(); 1037 | // SAFETY: `new_slice` is not null, allocated with size fitting `string`, 1038 | // and also `string` is guaranteed to be UTF-8. 1039 | unsafe { 1040 | copy_nonoverlapping(string.as_ptr(), new_slice, len); 1041 | Self::from_raw_utf8_parts_unchecked(new_slice, len) 1042 | } 1043 | } 1044 | } 1045 | 1046 | #[test] 1047 | fn test_string_from_bytes() { 1048 | let bytes = MBox::from_slice(b"abcdef\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89"); 1049 | let string = MBox::from_utf8(bytes).unwrap(); 1050 | assert_eq!(&*string, "abcdef一二三"); 1051 | assert_eq!(string, MBox::::from("abcdef一二三")); 1052 | let bytes = string.into_bytes(); 1053 | assert_eq!(&*bytes, b"abcdef\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89"); 1054 | } 1055 | 1056 | #[test] 1057 | fn test_string_with_internal_nul() { 1058 | let string = MBox::::from("ab\0c"); 1059 | assert_eq!(&*string, "ab\0c"); 1060 | } 1061 | 1062 | #[test] 1063 | fn test_non_utf8() { 1064 | let bytes = MBox::from_slice(b"\x88\x88\x88\x88"); 1065 | let string = MBox::from_utf8(bytes); 1066 | assert!(string.is_err()); 1067 | } 1068 | 1069 | #[test] 1070 | fn test_default_str() { 1071 | assert_eq!(MBox::::default(), MBox::::from("")); 1072 | } 1073 | 1074 | #[test] 1075 | #[should_panic(expected = "panic on clone")] 1076 | fn test_panic_on_clone_slice() { 1077 | let mbox: MBox<[PanicOnClone]> = once(PanicOnClone::default()).collect(); 1078 | let _ = mbox.clone(); 1079 | } 1080 | 1081 | //}}} 1082 | --------------------------------------------------------------------------------