mbox-0.6.0/.cargo_vcs_info.json0000644000000001120000000000000117750ustar { "git": { "sha1": "bf49a1c2959639e16b9afb4900942a757324746b" } } mbox-0.6.0/Cargo.toml0000644000000022130000000000000077770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "mbox" version = "0.6.0" authors = ["kennytm "] build = "build.rs" exclude = [".gitignore", ".github"] description = "malloc-based box.\n\nSupports wrapping pointers or null-terminated strings returned from malloc as a Rust type, which\nwill be free'd on drop.\n" readme = "README.md" keywords = ["malloc", "free", "ffi", "box", "cstr"] license = "MIT" repository = "https://github.com/kennytm/mbox" [dependencies.libc] version = "0.2" [dependencies.stable_deref_trait] version = "1.0" default-features = false [build-dependencies.rustc_version] version = "0.3" [features] default = ["std"] std = [] mbox-0.6.0/Cargo.toml.orig000064400000000000000000000011500000000000000134350ustar 00000000000000[package] name = "mbox" version = "0.6.0" authors = ["kennytm "] license = "MIT" keywords = ["malloc", "free", "ffi", "box", "cstr"] repository = "https://github.com/kennytm/mbox" readme = "README.md" description = """malloc-based box. Supports wrapping pointers or null-terminated strings returned from malloc as a Rust type, which will be free'd on drop. """ build = "build.rs" exclude = [".gitignore", ".github"] [dependencies] libc = "0.2" stable_deref_trait = { version = "1.0", default-features = false } [build-dependencies] rustc_version = "0.3" [features] default = ["std"] std = [] mbox-0.6.0/LICENSE.txt000064400000000000000000000021120000000000000123700ustar 00000000000000The MIT License (MIT) Copyright (c) 2016 Kenny Chan 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. mbox-0.6.0/README.md000064400000000000000000000052160000000000000120340ustar 00000000000000`mbox`: `malloc`-based box ========================== [![Crates.io](https://img.shields.io/crates/v/mbox.svg)](https://crates.io/crates/mbox) [![docs.rs](https://docs.rs/mbox/badge.svg)](https://docs.rs/mbox) [![Build status](https://github.com/kennytm/mbox/workflows/Rust/badge.svg)](https://github.com/kennytm/mbox/actions?query=workflow%3ARust) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.txt) This crate provides structures that wrap pointers returned from `malloc` as a Box, and automatically `free` them on drop. These types allow you to interact with pointers and null-terminated strings and arrays in a Rusty style. ## Examples ```rust extern crate libc; extern crate mbox; use libc::{c_char, malloc, strcpy}; use mbox::MString; // Assume we have a C function that returns a malloc'ed string. unsafe extern "C" fn create_str() -> *mut c_char { let ptr = malloc(12) as *mut c_char; strcpy(ptr, b"Hello world\0".as_ptr() as *const c_char); ptr } fn main() { // we wrap the null-terminated string into an MString. let string = unsafe { MString::from_raw_unchecked(create_str()) }; // check the content is expected. assert_eq!(&*string, "Hello world"); // the string will be dropped by `free` after the code is done. } ``` ## Installation Add this to your Cargo.toml: ```toml [dependencies] mbox = "0.6" ``` ## Usage This crate provides three main types, all of which uses the system's `malloc`/`free` as the allocator. * `MBox` — Similar to `Box`. * `MString` — Similar to `std::ffi::CString`. * `MArray` — A null-terminated array, which can be used to represent e.g. array of C strings terminated by a null pointer. ## `#![no_std]` You may compile `mbox` and disable the `std` feature to not link to `std` (it will still link to `core`. ```toml [dependencies] mbox = { version = "0.6", default-features = false } ``` When `#![no_std]` is activated, you cannot convert an `MString` into a `std::ffi::CStr`, as the type simply does not exist 🙂. ## Migrating from other crates Note that `MBox` does not support custom allocator. If the type requires custom allocation, `MBox` cannot serve you. * [`malloc_buf`](https://crates.io/crates/malloc_buf) — `MallocBuffer` is equivalent to `MBox<[T]>`. Note however we will not check for null pointers. * [`cbox`](https://crates.io/crates/cbox) — When not using a custom `DisposeRef`, the `CSemiBox<'static, T>` type is equivalent to `MBox`, and `CBox` is equivalent to `&'static T`. * [`c_vec`](https://crates.io/crates/c_vec) — When using `free` as the destructor, `CVec` is equivalent to `MBox<[T]>` and `CSlice` as `[T]`. mbox-0.6.0/build.rs000064400000000000000000000005230000000000000122160ustar 00000000000000extern crate rustc_version; use rustc_version::{version_meta, Channel}; pub fn main() { let meta = version_meta().unwrap(); let channel = match meta.channel { Channel::Dev | Channel::Nightly => "nightly", Channel::Beta | Channel::Stable => "stable", }; println!("cargo:rustc-cfg={}_channel", channel); } mbox-0.6.0/src/free.rs000064400000000000000000000026200000000000000126270ustar 00000000000000//! Trait to instruct how to properly drop and free pointers. use std::ptr::{drop_in_place, NonNull}; use internal::gen_free; /// Implemented for pointers which can be freed. pub trait Free { /// Drops the content pointed by this pointer and frees it. /// /// # Safety /// /// The `ptr` must be allocated through `malloc()`. /// /// Do not call this method if the pointer has been freed. Users of this trait should maintain a /// flag to track if the pointer has been freed or not (the Rust compiler will automatically do /// this with a `Drop` type). unsafe fn free(ptr: NonNull); } /// Drops the content of `*ptr`, then frees the `ptr` itself. unsafe fn free_ptr_ref(ptr: NonNull) { drop_in_place(ptr.as_ptr()); gen_free(ptr); } impl Free for T { #[cfg(nightly_channel)] default unsafe fn free(ptr_ref: NonNull) { free_ptr_ref(ptr_ref); } #[cfg(stable_channel)] unsafe fn free(ptr_ref: NonNull) { free_ptr_ref(ptr_ref); } } impl Free for [T] { unsafe fn free(mut fat_ptr: NonNull) { let thin_ptr = NonNull::new_unchecked(fat_ptr.as_mut().as_mut_ptr()); drop_in_place(fat_ptr.as_ptr()); gen_free(thin_ptr); } } impl Free for str { unsafe fn free(fat_ptr: NonNull) { Free::free(NonNull::new_unchecked(fat_ptr.as_ptr() as *mut [u8])); } } mbox-0.6.0/src/internal.rs000064400000000000000000000145410000000000000135270ustar 00000000000000#[cfg(not(feature = "std"))] extern crate alloc; use libc::c_void; use std::alloc::Layout; use std::marker::PhantomData; use std::mem::{align_of, size_of}; use std::ptr::NonNull; #[cfg(not(feature = "std"))] use self::alloc::alloc::handle_alloc_error; #[cfg(feature = "std")] use std::alloc::handle_alloc_error; #[cfg(nightly_channel)] use std::marker::Unsize; #[cfg(nightly_channel)] use std::ops::CoerceUnsized; //{{{ Unique -------------------------------------------------------------------------------------- /// Same as `std::ptr::Unique`, but provides a close-enough representation on stable channel. pub struct Unique { pointer: NonNull, marker: PhantomData, } unsafe impl Send for Unique {} unsafe impl Sync for Unique {} impl Unique { /// Creates a new raw unique pointer. /// /// # Safety /// /// The `pointer`'s ownership must be transferred into the result. That is, /// it is no longer valid to touch `pointer` and its copies directly after /// calling this function. pub const unsafe fn new(pointer: NonNull) -> Self { Self { pointer, marker: PhantomData, } } } impl Unique { pub fn as_non_null_ptr(&self) -> NonNull { self.pointer } } #[cfg(nightly_channel)] impl, U: ?Sized> CoerceUnsized> for Unique {} //}}} //{{{ gen_malloc ---------------------------------------------------------------------------------- #[cfg(windows)] unsafe fn malloc_aligned(size: usize, _align: usize) -> *mut c_void { libc::malloc(size) } #[cfg(all(not(windows), target_os = "android"))] unsafe fn malloc_aligned(size: usize, align: usize) -> *mut c_void { libc::memalign(align, size) } #[cfg(all(not(windows), not(target_os = "android")))] unsafe fn malloc_aligned(size: usize, align: usize) -> *mut c_void { let mut result = std::ptr::null_mut(); let align = align.max(size_of::<*mut ()>()); libc::posix_memalign(&mut result, align, size); result } /// Generic malloc function. pub fn gen_malloc(count: usize) -> NonNull { if size_of::() == 0 || count == 0 { NonNull::dangling() } else { let requested_size = count.checked_mul(size_of::()).expect("memory overflow"); // SAFETY: // - allocating should be safe, duh. // - in the rare case allocation failed, we throw an allocation error, so when we reach // NonNull::new_unchecked we can be sure the result is not null. unsafe { let res = malloc_aligned(requested_size, align_of::()) as *mut T; if res.is_null() { handle_alloc_error(Layout::new::()); } NonNull::new_unchecked(res) } } } /// Generic free function. /// /// # Safety /// /// The `ptr` must be obtained from `malloc()` or similar C functions. pub unsafe fn gen_free(ptr: NonNull) { if ptr != NonNull::dangling() { libc::free(ptr.as_ptr() as *mut c_void); } } /// Generic realloc function. /// /// # Safety /// /// The `ptr` must be obtained from `malloc()` or similar C functions. pub unsafe fn gen_realloc(ptr: NonNull, new_count: usize) -> NonNull { if size_of::() == 0 { ptr } else if new_count == 0 { gen_free(ptr); NonNull::dangling() } else if ptr == NonNull::dangling() { gen_malloc(new_count) } else { if let Some(requested_size) = new_count.checked_mul(size_of::()) { let res = libc::realloc(ptr.as_ptr() as *mut c_void, requested_size); if !res.is_null() { return NonNull::new_unchecked(res as *mut T); } } handle_alloc_error(Layout::new::()); } } //}}} //{{{ Drop counter -------------------------------------------------------------------------------- #[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Default))] pub(crate) struct SharedCounter { #[cfg(feature = "std")] counter: std::rc::Rc>, /// A shared, mutable counter on heap. #[cfg(not(feature = "std"))] counter: NonNull, } #[cfg(all(test, not(feature = "std")))] impl Default for SharedCounter { fn default() -> Self { // SAFETY: malloc() returns an uninitialized integer which is then filled in. unsafe { let counter = gen_malloc(1); std::ptr::write(counter.as_ptr(), 0); Self { counter } } } } #[cfg(test)] impl SharedCounter { /// Gets the counter value. pub(crate) fn get(&self) -> usize { #[cfg(feature = "std")] { self.counter.get() } // SAFETY: `self.counter` is malloc()'ed, initialized and never freed. #[cfg(not(feature = "std"))] unsafe { *self.counter.as_ref() } } /// Asserts the counter value equals to the input. Panics when different. pub(crate) fn assert_eq(&self, value: usize) { assert_eq!(self.get(), value); } /// Increases the counter by 1. fn inc(&self) { #[cfg(feature = "std")] { self.counter.set(self.counter.get() + 1); } // SAFETY: `self.counter` is malloc()'ed, initialized and never freed. // Since `SharedCounter` is not Sync nor Send, we are sure the // modification happens in the single thread, so we don't worry about // the interior mutation. #[cfg(not(feature = "std"))] unsafe { *self.counter.as_ptr() += 1; } } } /// A test structure to count how many times the value has been dropped. #[cfg(test)] #[derive(Clone, Debug, PartialEq, Eq, Default)] pub(crate) struct DropCounter(SharedCounter); #[cfg(test)] impl std::ops::Deref for DropCounter { type Target = SharedCounter; fn deref(&self) -> &Self::Target { &self.0 } } #[cfg(test)] impl Drop for DropCounter { fn drop(&mut self) { self.0.inc(); } } //}}} //{{{ Panic-on-clone ------------------------------------------------------------------------------ /// A test structure which panics when it is cloned. #[cfg(test)] #[derive(Default)] pub struct PanicOnClone(u8); #[cfg(test)] impl Clone for PanicOnClone { fn clone(&self) -> Self { panic!("panic on clone"); } } //}}} mbox-0.6.0/src/lib.rs000064400000000000000000000055340000000000000124630ustar 00000000000000//! `malloc`-based box. //! //! This crate provides structures that wrap pointers returned from `malloc` as a Box, and //! automatically `free` them on drop. These types allow you to interact with pointers and //! null-terminated strings and arrays in a Rusty style. //! //! # Examples //! //! ```rust //! extern crate libc; //! extern crate mbox; //! //! use libc::{c_char, malloc, strcpy}; //! use mbox::MString; //! //! // Assume we have a C function that returns a malloc'ed string. //! unsafe extern "C" fn create_str() -> *mut c_char { //! let ptr = malloc(12) as *mut c_char; //! strcpy(ptr, b"Hello world\0".as_ptr() as *const c_char); //! ptr //! } //! //! fn main() { //! // we wrap the null-terminated string into an MString. //! let string = unsafe { MString::from_raw_unchecked(create_str()) }; //! //! // check the content is expected. //! assert_eq!(&*string, "Hello world"); //! //! // the string will be dropped by `free` after the code is done. //! } //! ``` //! //! # Usage //! //! This crate provides three main types, all of which uses the system's `malloc`/`free` as the //! allocator. //! //! * [`MBox`](mbox/struct.MBox.html) — Similar to `Box`. //! * [`MString`](sentinel/struct.MString.html) — Similar to `std::ffi::CString`. //! * [`MArray`](sentinel/struct.MArray.html) — A null-terminated array, which can be used to //! represent e.g. array of C strings terminated by a null pointer. //! //! # `#![no_std]` //! //! You may compile `mbox` and disable the `std` feature to not link to `std` (it will still link to //! `core`. //! //! ```toml //! [dependencies] //! mbox = { version = "0.3", default-features = false } //! ``` //! //! When `#![no_std]` is activated, you cannot convert an `MString` into a `std::ffi::CStr`, as the //! type simply does not exist 🙂. //! //! # Migrating from other crates //! //! Note that `MBox` does not support custom allocator. If the type requires custom allocation, //! `MBox` cannot serve you. //! //! * [`malloc_buf`](https://crates.io/crates/malloc_buf) — `MallocBuffer` is equivalent to //! `MBox<[T]>`. Note however we will not check for null pointers. //! //! * [`cbox`](https://crates.io/crates/cbox) — When not using a custom `DisposeRef`, the //! `CSemiBox<'static, T>` type is equivalent to `MBox`, and `CBox` is equivalent to //! `&'static T`. //! //! * [`c_vec`](https://crates.io/crates/c_vec) — When using `free` as the destructor, `CVec` is //! equivalent to `MBox<[T]>` and `CSlice` as `[T]`. #![cfg_attr(nightly_channel, feature(min_specialization, unsize, coerce_unsized))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate core as std; extern crate libc; extern crate stable_deref_trait; pub mod free; mod internal; pub mod mbox; pub mod sentinel; pub use mbox::MBox; pub use sentinel::{MArray, MString}; mbox-0.6.0/src/mbox.rs000064400000000000000000000673750000000000000126750ustar 00000000000000//! `malloc`-based Box. use stable_deref_trait::StableDeref; use std::cmp::Ordering; use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Display, Formatter, Pointer, Result as FormatResult}; use std::hash::{Hash, Hasher}; use std::iter::{DoubleEndedIterator, FromIterator, IntoIterator}; use std::marker::Unpin; use std::mem::{forget, MaybeUninit}; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::ptr::{copy_nonoverlapping, drop_in_place, read, write}; use std::slice::{from_raw_parts, from_raw_parts_mut, Iter, IterMut}; use std::str::{from_utf8, from_utf8_unchecked, Utf8Error}; use std::{ borrow::{Borrow, BorrowMut}, ptr::NonNull, }; use internal::{gen_free, gen_malloc, gen_realloc, Unique}; #[cfg(test)] use internal::{DropCounter, PanicOnClone}; #[cfg(test)] use std::iter::{once, repeat}; #[cfg(test)] use std::mem::size_of; #[cfg(nightly_channel)] use std::marker::Unsize; #[cfg(nightly_channel)] use std::ops::CoerceUnsized; use free::Free; //{{{ Basic structure ----------------------------------------------------------------------------- /// A malloc-backed box. This structure allows Rust to exchange objects with C without cloning. pub struct MBox(Unique); impl MBox { /// Constructs a new malloc-backed box from a pointer allocated by `malloc`. /// /// # Safety /// /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is /// expected to be deallocated using `free()`. It must not be null. The content of the pointer /// must be already initialized. The pointer's ownership is passed into the box, and thus should /// not be used after this function returns. pub unsafe fn from_raw(ptr: *mut T) -> Self { Self::from_non_null_raw(NonNull::new_unchecked(ptr)) } /// Constructs a new malloc-backed box from a non-null pointer allocated by `malloc`. /// /// # Safety /// /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is /// expected to be deallocated using `free()`. The content of the pointer must be already /// initialized. The pointer's ownership is passed into the box, and thus should not be used /// after this function returns. pub unsafe fn from_non_null_raw(ptr: NonNull) -> Self { Self(Unique::new(ptr)) } /// Obtains the pointer owned by the box. pub fn as_ptr(boxed: &Self) -> *const T { boxed.0.as_non_null_ptr().as_ptr() } /// Obtains the mutable pointer owned by the box. pub fn as_mut_ptr(boxed: &mut Self) -> *mut T { boxed.0.as_non_null_ptr().as_ptr() } /// Consumes the box and returns the original pointer. /// /// The caller is responsible for `free`ing the pointer after this. pub fn into_raw(boxed: Self) -> *mut T { Self::into_non_null_raw(boxed).as_ptr() } /// Consumes the box and returns the original non-null pointer. /// /// The caller is responsible for `free`ing the pointer after this. pub fn into_non_null_raw(boxed: Self) -> NonNull { let ptr = boxed.0.as_non_null_ptr(); forget(boxed); ptr } } impl Drop for MBox { fn drop(&mut self) { // SAFETY: the pointer is assumed to be obtained from `malloc()`. unsafe { T::free(self.0.as_non_null_ptr()) }; } } impl Deref for MBox { type Target = T; fn deref(&self) -> &T { unsafe { &*Self::as_ptr(self) } } } unsafe impl StableDeref for MBox {} impl Unpin for MBox {} impl DerefMut for MBox { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *Self::as_mut_ptr(self) } } } impl AsRef for MBox { fn as_ref(&self) -> &T { self } } impl AsMut for MBox { fn as_mut(&mut self) -> &mut T { self } } impl Borrow for MBox { fn borrow(&self) -> &T { self } } impl BorrowMut for MBox { fn borrow_mut(&mut self) -> &mut T { self } } #[cfg(nightly_channel)] impl, U: ?Sized + Free> CoerceUnsized> for MBox {} impl Pointer for MBox { fn fmt(&self, formatter: &mut Formatter) -> FormatResult { Pointer::fmt(&Self::as_ptr(self), formatter) } } impl Debug for MBox { fn fmt(&self, formatter: &mut Formatter) -> FormatResult { self.deref().fmt(formatter) } } impl Display for MBox { fn fmt(&self, formatter: &mut Formatter) -> FormatResult { self.deref().fmt(formatter) } } impl Hash for MBox { fn hash(&self, state: &mut H) { self.deref().hash(state) } } impl> PartialEq> for MBox { fn eq(&self, other: &MBox) -> bool { self.deref().eq(other.deref()) } } impl Eq for MBox {} impl> PartialOrd> for MBox { fn partial_cmp(&self, other: &MBox) -> Option { self.deref().partial_cmp(other.deref()) } } impl Ord for MBox { fn cmp(&self, other: &Self) -> Ordering { self.deref().cmp(other.deref()) } } //}}} //{{{ Single object ------------------------------------------------------------------------------- impl MBox { /// Constructs a new malloc-backed box, and move an initialized value into it. pub fn new(value: T) -> Self { let storage = gen_malloc(1); // SAFETY: the `storage` is uninitialized and enough to store T. // this pointer is obtained via `malloc` and thus good for `from_raw`. unsafe { write(storage.as_ptr(), value); Self::from_non_null_raw(storage) } } /// Constructs a new malloc-backed box with uninitialized content. pub fn new_uninit() -> MBox> { let storage = gen_malloc(1); // SAFETY: The storage is allowed to be uninitialized. unsafe { MBox::from_non_null_raw(storage) } } /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then `value` will be /// pinned in memory and cannot be moved. pub fn pin(value: T) -> Pin { Self::into_pin(Self::new(value)) } /// Converts an `MBox` into a single-item `MBox<[T]>`. /// /// This conversion does not allocate on the heap and happens in place. pub fn into_boxed_slice(boxed: Self) -> MBox<[T]> { // SAFETY: free() only cares about the allocated size, and `T` and // `[T; 1]` are equivalent in terms of drop() and free(). unsafe { MBox::from_raw_parts(Self::into_raw(boxed), 1) } } /// Consumes the `MBox`, returning the wrapped value. pub fn into_inner(boxed: Self) -> T { let mut dst = MaybeUninit::uninit(); let src = Self::into_non_null_raw(boxed); // SAFETY: after calling `into_raw` above, we have the entire ownership of the malloc'ed // pointer `src`. The content is moved into the destination. After that, we can free `src` // without touching the content. So there is a single copy of the content fully initialized // into `dst` which is safe to assume_init. unsafe { copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), 1); gen_free(src); dst.assume_init() } } /// Converts an `MBox` into a `Pin>`. /// /// This conversion does not allocate on the heap and happens in place. pub fn into_pin(boxed: Self) -> Pin { // SAFETY: Same reason as why `Box::into_pin` is safe. unsafe { Pin::new_unchecked(boxed) } } /// Consumes and leaks the `MBox`, returning a mutable reference, `&'a mut T`. pub fn leak<'a>(boxed: Self) -> &'a mut T where T: 'a, { // SAFETY: into_raw takes the ownership of the box, which is then immediately leaked. Thus, // no one is able to call `gen_free` on this pointer and thus safe to be used in the rest of // its lifetime. unsafe { &mut *Self::into_non_null_raw(boxed).as_ptr() } } } impl MBox> { /// Converts into an initialized box. /// /// # Safety /// /// The caller should guarantee `*self` is indeed initialized. pub unsafe fn assume_init(self) -> MBox { MBox::from_non_null_raw(Self::into_non_null_raw(self).cast()) } } impl From for MBox { fn from(value: T) -> MBox { MBox::new(value) } } impl Clone for MBox { fn clone(&self) -> MBox { Self::new(self.deref().clone()) } fn clone_from(&mut self, source: &Self) { self.deref_mut().clone_from(source); } } impl Default for MBox { fn default() -> MBox { MBox::new(T::default()) } } #[test] fn test_single_object() { let counter = DropCounter::default(); { let mbox = MBox::new(counter.clone()); counter.assert_eq(0); drop(mbox); } counter.assert_eq(1); } #[test] fn test_into_raw() { let mbox = MBox::new(66u8); let raw = MBox::into_raw(mbox); unsafe { assert_eq!(*raw, 66u8); gen_free(NonNull::new(raw).unwrap()); } } #[test] fn test_clone() { let counter = DropCounter::default(); { let first_mbox = MBox::new(counter.clone()); { let second_mbox = first_mbox.clone(); counter.assert_eq(0); drop(second_mbox); } counter.assert_eq(1); } counter.assert_eq(2); } #[test] fn test_clone_from() { let counter = DropCounter::default(); { let first_mbox = MBox::new(counter.clone()); { let mut second_mbox = MBox::new(counter.clone()); counter.assert_eq(0); second_mbox.clone_from(&first_mbox); counter.assert_eq(1); } counter.assert_eq(2); } counter.assert_eq(3); } #[test] fn test_no_drop_flag() { fn do_test_for_drop_flag(branch: bool, expected: usize) { let counter = DropCounter::default(); let inner_counter = counter.deref().clone(); { let mbox; if branch { mbox = MBox::new(counter.clone()); let _ = &mbox; } inner_counter.assert_eq(0); } inner_counter.assert_eq(expected); } do_test_for_drop_flag(true, 1); do_test_for_drop_flag(false, 0); assert_eq!( size_of::>(), size_of::<*mut DropCounter>() ); } #[cfg(feature = "std")] #[test] fn test_format() { let a = MBox::new(3u64); assert_eq!(format!("{:p}", a), format!("{:p}", MBox::as_ptr(&a))); assert_eq!(format!("{}", a), "3"); assert_eq!(format!("{:?}", a), "3"); } #[test] fn test_standard_traits() { let mut a = MBox::new(0u64); assert_eq!(*a, 0); *a = 3; assert_eq!(*a, 3); assert_eq!(*a.as_ref(), 3); assert_eq!(*a.as_mut(), 3); assert_eq!(*(a.borrow() as &u64), 3); assert_eq!(*(a.borrow_mut() as &mut u64), 3); assert!(a == MBox::new(3u64)); assert!(a != MBox::new(0u64)); assert!(a < MBox::new(4u64)); assert!(a > MBox::new(2u64)); assert!(a <= MBox::new(4u64)); assert!(a >= MBox::new(2u64)); assert_eq!(a.cmp(&MBox::new(7u64)), Ordering::Less); assert_eq!(MBox::::default(), MBox::new(0u64)); } #[test] fn test_zero_sized_type() { let a = MBox::new(()); assert!(!MBox::as_ptr(&a).is_null()); } #[test] fn test_non_zero() { let b = 0u64; assert!(!Some(MBox::new(0u64)).is_none()); assert!(!Some(MBox::new(())).is_none()); assert!(!Some(MBox::new(&b)).is_none()); assert_eq!(size_of::>>(), size_of::>()); assert_eq!(size_of::>>(), size_of::>()); assert_eq!( size_of::>>(), size_of::>() ); } //}}} //{{{ Slice helpers ------------------------------------------------------------------------------- mod slice_helper { use super::*; /// A `Vec`-like structure backed by `malloc()`. pub struct MSliceBuilder { ptr: NonNull, cap: usize, len: usize, } impl MSliceBuilder { /// Creates a new slice builder with an initial capacity. pub fn with_capacity(cap: usize) -> MSliceBuilder { MSliceBuilder { ptr: gen_malloc(cap), cap, len: 0, } } pub fn push(&mut self, obj: T) { // SAFETY: // - self.ptr is initialized from gen_malloc() so it can be placed into gen_realloc() // - we guarantee that `self.ptr `points to an array of nonzero length `self.cap`, and // the `if` condition ensures the invariant `self.len < self.cap`, so // `self.ptr.add(self.len)` is always a valid (but uninitialized) object. // - since `self.ptr[self.len]` is not yet initialized, we can `write()` into it safely. unsafe { if self.len >= self.cap { if self.cap == 0 { self.cap = 1; } else { self.cap *= 2; } self.ptr = gen_realloc(self.ptr, self.cap); } write(self.ptr.as_ptr().add(self.len), obj); } self.len += 1; } pub fn into_mboxed_slice(self) -> MBox<[T]> { // SAFETY: `self.ptr` has been allocated by malloc(), and its length is self.cap // (>= self.len). let slice = unsafe { MBox::from_raw_parts(self.ptr.as_ptr(), self.len) }; forget(self); slice } } impl MSliceBuilder> { /// Sets the length of the builder to the same as the capacity. The elements in the /// uninitialized tail remains uninitialized. pub fn set_len_to_cap(&mut self) { self.len = self.cap; } } impl Drop for MSliceBuilder { fn drop(&mut self) { unsafe { gen_free(self.ptr); } } } } use self::slice_helper::MSliceBuilder; /// The iterator returned from `MBox<[T]>::into_iter()`. pub struct MSliceIntoIter { ptr: NonNull, begin: usize, end: usize, } impl Iterator for MSliceIntoIter { type Item = T; fn next(&mut self) -> Option { if self.begin == self.end { None } else { unsafe { let ptr = self.ptr.as_ptr().add(self.begin); self.begin += 1; Some(read(ptr)) } } } fn size_hint(&self) -> (usize, Option) { let len = self.end - self.begin; (len, Some(len)) } } impl DoubleEndedIterator for MSliceIntoIter { fn next_back(&mut self) -> Option { if self.begin == self.end { None } else { unsafe { self.end -= 1; let ptr = self.ptr.as_ptr().add(self.end); Some(read(ptr)) } } } } unsafe impl Send for MSliceIntoIter {} unsafe impl Sync for MSliceIntoIter {} impl ExactSizeIterator for MSliceIntoIter {} impl Drop for MSliceIntoIter { fn drop(&mut self) { unsafe { let base = self.ptr.as_ptr().add(self.begin); let len = self.end - self.begin; let slice = from_raw_parts_mut(base, len) as *mut [T]; drop_in_place(slice); gen_free(self.ptr); } } } //}}} //{{{ Slice --------------------------------------------------------------------------------------- impl MBox<[T]> { /// Constructs a new malloc-backed slice from the pointer and the length (number of items). /// /// # Safety /// /// `ptr` must be allocated via `malloc()` or similar C functions. It must not be null. /// /// The `malloc`ed size of the pointer must be at least `len * size_of::()`. The content /// must already been initialized. pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { let ptr = from_raw_parts_mut(ptr, len) as *mut [T]; Self::from_raw(ptr) } /// Constructs a new boxed slice with uninitialized contents. pub fn new_uninit_slice(len: usize) -> MBox<[MaybeUninit]> { let mut builder = MSliceBuilder::with_capacity(len); builder.set_len_to_cap(); builder.into_mboxed_slice() } /// Decomposes the boxed slice into a pointer to the first element and the slice length. pub fn into_raw_parts(mut self) -> (*mut T, usize) { let len = self.len(); let ptr = self.as_mut_ptr(); forget(self); (ptr, len) } } impl MBox<[MaybeUninit]> { /// Converts into an initialized boxed slice. /// /// # Safety /// /// The caller should guarantee `*self` is indeed initialized. pub unsafe fn assume_init(self) -> MBox<[T]> { MBox::from_raw(Self::into_raw(self) as *mut [T]) } } impl Default for MBox<[T]> { fn default() -> Self { unsafe { Self::from_raw_parts(gen_malloc(0).as_ptr(), 0) } } } impl Clone for MBox<[T]> { fn clone(&self) -> Self { Self::from_slice(self) } } impl MBox<[T]> { /// Creates a new `malloc`-boxed slice by cloning the content of an existing slice. pub fn from_slice(slice: &[T]) -> MBox<[T]> { let mut builder = MSliceBuilder::with_capacity(slice.len()); for item in slice { builder.push(item.clone()); } builder.into_mboxed_slice() } } impl FromIterator for MBox<[T]> { fn from_iter>(iter: I) -> Self { let iter = iter.into_iter(); let (lower_size, upper_size) = iter.size_hint(); let initial_capacity = upper_size.unwrap_or(lower_size).max(1); let mut builder = MSliceBuilder::with_capacity(initial_capacity); for item in iter { builder.push(item); } builder.into_mboxed_slice() } } impl IntoIterator for MBox<[T]> { type Item = T; type IntoIter = MSliceIntoIter; fn into_iter(self) -> MSliceIntoIter { let (ptr, len) = self.into_raw_parts(); MSliceIntoIter { ptr: unsafe { NonNull::new_unchecked(ptr) }, begin: 0, end: len, } } } impl<'a, T> IntoIterator for &'a MBox<[T]> { type Item = &'a T; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Iter<'a, T> { self.iter() } } impl<'a, T> IntoIterator for &'a mut MBox<[T]> { type Item = &'a mut T; type IntoIter = IterMut<'a, T>; fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } } #[test] fn test_slice() { unsafe { let slice_content = gen_malloc::(5).as_ptr(); *slice_content.offset(0) = 16458340076686561191; *slice_content.offset(1) = 15635007859502065083; *slice_content.offset(2) = 4845947824042606450; *slice_content.offset(3) = 8907026173756975745; *slice_content.offset(4) = 7378932587879886134; let mbox = MBox::from_raw_parts(slice_content, 5); assert_eq!( &mbox as &[u64], &[ 16458340076686561191, 15635007859502065083, 4845947824042606450, 8907026173756975745, 7378932587879886134 ] ); } } #[test] fn test_slice_with_drops() { let counter = DropCounter::default(); unsafe { let slice_content = gen_malloc::(3).as_ptr(); { write(slice_content.offset(0), counter.clone()); write(slice_content.offset(1), counter.clone()); write(slice_content.offset(2), counter.clone()); } counter.assert_eq(0); let mbox = MBox::from_raw_parts(slice_content, 3); mbox[0].assert_eq(0); mbox[1].assert_eq(0); mbox[2].assert_eq(0); assert_eq!(mbox.len(), 3); } counter.assert_eq(3); } #[cfg(nightly_channel)] #[test] fn test_coerce_unsized() { let counter = DropCounter::default(); { let pre_box = MBox::new([counter.clone(), counter.clone()]); counter.assert_eq(0); pre_box[0].assert_eq(0); pre_box[1].assert_eq(0); assert_eq!(pre_box.len(), 2); let post_box: MBox<[DropCounter]> = pre_box; counter.assert_eq(0); post_box[0].assert_eq(0); post_box[1].assert_eq(0); assert_eq!(post_box.len(), 2); } counter.assert_eq(2); } #[test] fn test_empty_slice() { let mbox = MBox::<[DropCounter]>::default(); let sl: &[DropCounter] = &mbox; assert_eq!(sl.len(), 0); assert!(!sl.as_ptr().is_null()); } #[cfg(nightly_channel)] #[test] fn test_coerce_from_empty_slice() { let pre_box = MBox::<[DropCounter; 0]>::new([]); assert_eq!(pre_box.len(), 0); assert!(!pre_box.as_ptr().is_null()); let post_box: MBox<[DropCounter]> = pre_box; let sl: &[DropCounter] = &post_box; assert_eq!(sl.len(), 0); assert!(!sl.as_ptr().is_null()); } #[test] fn test_clone_slice() { let counter = DropCounter::default(); unsafe { let slice_content = gen_malloc::(3).as_ptr(); { write(slice_content.offset(0), counter.clone()); write(slice_content.offset(1), counter.clone()); write(slice_content.offset(2), counter.clone()); } let mbox = MBox::from_raw_parts(slice_content, 3); assert_eq!(mbox.len(), 3); { let cloned_mbox = mbox.clone(); counter.assert_eq(0); assert_eq!(cloned_mbox.len(), 3); cloned_mbox[0].assert_eq(0); cloned_mbox[1].assert_eq(0); cloned_mbox[2].assert_eq(0); } counter.assert_eq(3); mbox[0].assert_eq(3); mbox[1].assert_eq(3); mbox[2].assert_eq(3); } counter.assert_eq(6); } #[test] fn test_from_iterator() { let counter = DropCounter::default(); { let slice = repeat(counter.clone()).take(18).collect::>(); counter.assert_eq(1); assert_eq!(slice.len(), 18); for c in &slice { c.assert_eq(1); } } counter.assert_eq(19); } #[test] fn test_into_iterator() { let counter = DropCounter::default(); { let slice = repeat(counter.clone()).take(18).collect::>(); counter.assert_eq(1); assert_eq!(slice.len(), 18); for (i, c) in slice.into_iter().enumerate() { c.assert_eq(1 + i); } } counter.assert_eq(19); } #[cfg(feature = "std")] #[test] fn test_iter_properties() { let slice = vec![1, 4, 9, 16, 25].into_iter().collect::>(); let mut iter = slice.into_iter(); assert_eq!(iter.size_hint(), (5, Some(5))); assert_eq!(iter.len(), 5); assert_eq!(iter.next(), Some(1)); assert_eq!(iter.next_back(), Some(25)); assert_eq!(iter.size_hint(), (3, Some(3))); assert_eq!(iter.len(), 3); assert_eq!(iter.collect::>(), vec![4, 9, 16]); } #[test] fn test_iter_drop() { let counter = DropCounter::default(); { let slice = repeat(counter.clone()).take(18).collect::>(); counter.assert_eq(1); assert_eq!(slice.len(), 18); let mut iter = slice.into_iter(); counter.assert_eq(1); { iter.next().unwrap().assert_eq(1) }; { iter.next().unwrap().assert_eq(2) }; { iter.next_back().unwrap().assert_eq(3) }; counter.assert_eq(4); } counter.assert_eq(19); } #[test] fn test_zst_slice() { let slice = repeat(()).take(7).collect::>(); let _ = slice.clone(); slice.into_iter(); } #[test] #[should_panic(expected = "panic on clone")] fn test_panic_during_clone() { let mbox = MBox::::default(); let _ = mbox.clone(); } #[test] #[should_panic(expected = "panic on clone")] fn test_panic_during_clone_from() { let mut mbox = MBox::::default(); let other = MBox::default(); mbox.clone_from(&other); } //}}} //{{{ UTF-8 String -------------------------------------------------------------------------------- impl MBox { /// Constructs a new malloc-backed string from the pointer and the length (number of UTF-8 code /// units). /// /// # Safety /// /// The `malloc`ed size of the pointer must be at least `len`. The content must already been /// initialized and be valid UTF-8. pub unsafe fn from_raw_utf8_parts_unchecked(value: *mut u8, len: usize) -> MBox { let bytes = from_raw_parts(value, len); let string = from_utf8_unchecked(bytes) as *const str as *mut str; Self::from_raw(string) } /// Constructs a new malloc-backed string from the pointer and the length (number of UTF-8 code /// units). If the content does not contain valid UTF-8, this method returns an `Err`. /// /// # Safety /// /// The `malloc`ed size of the pointer must be at least `len`. pub unsafe fn from_raw_utf8_parts(value: *mut u8, len: usize) -> Result, Utf8Error> { let bytes = from_raw_parts(value, len); let string = from_utf8(bytes)? as *const str as *mut str; Ok(Self::from_raw(string)) } /// Converts the string into raw bytes. pub fn into_bytes(self) -> MBox<[u8]> { unsafe { MBox::from_raw(Self::into_raw(self) as *mut [u8]) } } /// Creates a string from raw bytes. /// /// # Safety /// /// The raw bytes must be valid UTF-8. pub unsafe fn from_utf8_unchecked(bytes: MBox<[u8]>) -> MBox { Self::from_raw(MBox::into_raw(bytes) as *mut str) } /// Creates a string from raw bytes. If the content does not contain valid UTF-8, this method /// returns an `Err`. pub fn from_utf8(bytes: MBox<[u8]>) -> Result, Utf8Error> { unsafe { let (ptr, len) = bytes.into_raw_parts(); Self::from_raw_utf8_parts(ptr, len) } } } impl Default for MBox { fn default() -> Self { unsafe { Self::from_raw_utf8_parts_unchecked(gen_malloc(0).as_ptr(), 0) } } } impl Clone for MBox { fn clone(&self) -> Self { Self::from(&**self) } } impl From<&str> for MBox { /// Creates a new `malloc`-boxed string by cloning the content of an existing string slice. fn from(string: &str) -> Self { let len = string.len(); unsafe { let new_slice = gen_malloc(len).as_ptr(); copy_nonoverlapping(string.as_ptr(), new_slice, len); Self::from_raw_utf8_parts_unchecked(new_slice, len) } } } #[test] fn test_string_from_bytes() { let bytes = MBox::from_slice(b"abcdef\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89"); let string = MBox::from_utf8(bytes).unwrap(); assert_eq!(&*string, "abcdef一二三"); assert_eq!(string, MBox::::from("abcdef一二三")); let bytes = string.into_bytes(); assert_eq!(&*bytes, b"abcdef\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89"); } #[test] fn test_non_utf8() { let bytes = MBox::from_slice(b"\x88\x88\x88\x88"); let string = MBox::from_utf8(bytes); assert!(string.is_err()); } #[test] fn test_default_str() { assert_eq!(MBox::::default(), MBox::::from("")); } #[test] #[should_panic(expected = "panic on clone")] fn test_panic_on_clone_slice() { let mbox: MBox<[PanicOnClone]> = once(PanicOnClone::default()).collect(); let _ = mbox.clone(); } //}}} mbox-0.6.0/src/sentinel.rs000064400000000000000000000273710000000000000135410ustar 00000000000000//! Sentinel-terminated types. use libc::{c_char, strlen}; use stable_deref_trait::StableDeref; use std::borrow::{Borrow, BorrowMut}; use std::convert::{AsMut, AsRef}; use std::default::Default; #[cfg(feature = "std")] use std::ffi::CStr; use std::hash::{Hash, Hasher}; use std::iter::once; use std::ops::{Deref, DerefMut}; use std::ptr::{copy_nonoverlapping, null, null_mut, write}; use std::str::Utf8Error; use internal::gen_malloc; use mbox::MBox; #[cfg(test)] use internal::DropCounter; /// Implemented for types which has a sentinel value. pub trait Sentinel: Eq { /// Obtains the sentinel value. const SENTINEL: Self; } impl Sentinel for *const T { const SENTINEL: Self = null(); } impl Sentinel for *mut T { const SENTINEL: Self = null_mut(); } impl Sentinel for Option { const SENTINEL: Self = None; } macro_rules! impl_zero_for_sentinel { ($($ty:ty)+) => { $(impl Sentinel for $ty { const SENTINEL: Self = 0; })+ } } impl_zero_for_sentinel!(u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize); /// A `malloc`-backed array with an explicit sentinel at the end. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct MArray(MBox<[T]>); /// A `malloc`-backed null-terminated string (similar to `CString`). #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct MString(MBox); impl MArray { /// Constructs a new malloc-backed slice from a pointer to the null-terminated array. /// /// # Safety /// /// The `ptr` must be allocated via `malloc()`, `calloc()` or similar C functions that is /// expected to be deallocated using `free()`. It must not be null. The content of the pointer /// must be already initialized, and terminated by `T::SENTINEL`. The array's ownership is /// passed into the result, and thus should not be used after this function returns. pub unsafe fn from_raw(base: *mut T) -> MArray { let mut len = 0; while *base.add(len) != T::SENTINEL { len += 1; } MArray(MBox::from_raw_parts(base, len + 1)) } /// Converts into an `MBox` including the sentinel. pub fn into_mbox_with_sentinel(self) -> MBox<[T]> { self.0 } /// Converts into an `MBox` excluding the sentinel. pub fn into_mbox(self) -> MBox<[T]> { let (ptr, len) = self.0.into_raw_parts(); unsafe { MBox::from_raw_parts(ptr, len - 1) } } } impl MArray { /// Creates a null-terminated array from the clone of a slice. pub fn from_slice(slice: &[T]) -> MArray { MArray(slice.iter().cloned().chain(once(T::SENTINEL)).collect()) } } impl MString { /// Constructs a new malloc-backed string from a null-terminated C string. /// /// # Safety /// /// The `base` must be allocated via `malloc()`, `calloc()` or similar C functions that is /// expected to be deallocated using `free()`. It must not be null. The content of the string /// must be already initialized, and terminated by `'\0'`. The string's ownership is passed into /// the result, and thus should not be used after this function returns. /// /// The string must be valid UTF-8. pub unsafe fn from_raw_unchecked(base: *mut c_char) -> MString { let len = strlen(base); MString(MBox::from_raw_utf8_parts_unchecked( base as *mut u8, len + 1, )) } /// Constructs a new malloc-backed string from a null-terminated C string. Errors with /// `Utf8Error` if the string is not in valid UTF-8. /// /// # Safety /// /// The `base` must be allocated via `malloc()`, `calloc()` or similar C functions that is /// expected to be deallocated using `free()`. It must not be null. The content of the string /// must be already initialized, and terminated by `'\0'`. The string's ownership is passed into /// the result, and thus should not be used after this function returns. pub unsafe fn from_raw(base: *mut c_char) -> Result { let len = strlen(base); let mbox = MBox::from_raw_utf8_parts(base as *mut u8, len + 1)?; Ok(MString(mbox)) } pub fn into_bytes(self) -> MArray { MArray(self.0.into_bytes()) } /// Converts into an `MBox` including the sentinel. pub fn into_mbox_with_sentinel(self) -> MBox { self.0 } /// Converts into an `MBox` excluding the sentinel. pub fn into_mbox(self) -> MBox { unsafe { MBox::from_utf8_unchecked(self.into_bytes().into_mbox()) } } /// Converts to a C string. This allows users to borrow an MString in FFI code. #[cfg(all(feature = "std"))] pub fn as_c_str(&self) -> &CStr { unsafe { CStr::from_bytes_with_nul_unchecked(self.0.as_bytes()) } } /// Obtains the raw bytes including the sentinel. pub fn as_bytes_with_sentinel(&self) -> &[u8] { self.0.as_bytes() } } impl From<&str> for MString { /// Creates a null-terminated string from the clone of a string. fn from(string: &str) -> MString { unsafe { let len = string.len(); let ptr = gen_malloc(len + 1).as_ptr(); copy_nonoverlapping(string.as_ptr(), ptr, len); write(ptr.add(len), 0); MString(MBox::from_raw_utf8_parts_unchecked(ptr, len + 1)) } } } impl Deref for MArray { type Target = [T]; fn deref(&self) -> &[T] { let actual_len = self.0.len() - 1; &self.0[..actual_len] } } impl Deref for MString { type Target = str; fn deref(&self) -> &str { let actual_len = self.0.len() - 1; &self.0[..actual_len] } } unsafe impl StableDeref for MArray {} unsafe impl StableDeref for MString {} impl DerefMut for MArray { fn deref_mut(&mut self) -> &mut [T] { let actual_len = self.0.len() - 1; &mut self.0[..actual_len] } } #[allow(clippy::derive_hash_xor_eq)] impl Hash for MString { fn hash(&self, state: &mut H) { self.deref().hash(state); } } #[allow(clippy::derive_hash_xor_eq)] impl Hash for MArray { fn hash(&self, state: &mut H) { self.deref().hash(state); } } impl DerefMut for MString { fn deref_mut(&mut self) -> &mut str { let actual_len = self.0.len() - 1; &mut self.0[..actual_len] } } impl Default for MArray { fn default() -> Self { unsafe { let arr = gen_malloc(1).as_ptr(); write(arr, T::SENTINEL); MArray(MBox::from_raw_parts(arr, 1)) } } } impl Default for MString { fn default() -> Self { unsafe { let arr = gen_malloc(1).as_ptr(); write(arr, 0); MString(MBox::from_raw_utf8_parts_unchecked(arr, 1)) } } } impl AsRef<[T]> for MArray { fn as_ref(&self) -> &[T] { self } } impl AsMut<[T]> for MArray { fn as_mut(&mut self) -> &mut [T] { self } } impl Borrow<[T]> for MArray { fn borrow(&self) -> &[T] { self } } impl BorrowMut<[T]> for MArray { fn borrow_mut(&mut self) -> &mut [T] { self } } impl AsRef for MString { fn as_ref(&self) -> &str { self } } impl AsMut for MString { fn as_mut(&mut self) -> &mut str { self } } impl Borrow for MString { fn borrow(&self) -> &str { self } } impl BorrowMut for MString { fn borrow_mut(&mut self) -> &mut str { self } } #[cfg(feature = "std")] impl AsRef for MString { fn as_ref(&self) -> &CStr { self.as_c_str() } } #[test] fn test_array() { unsafe { let src = gen_malloc::(6).as_ptr(); *src.offset(0) = 56; *src.offset(1) = 18; *src.offset(2) = 200; *src.offset(3) = 0; *src.offset(4) = 105; *src.offset(5) = 0; let mut array = MArray::from_raw(src); assert_eq!(&*array, &[56u8, 18, 200]); array[1] = 19; assert_eq!(*src.offset(1), 19); } } #[test] fn test_array_with_drop() { let counter = DropCounter::default(); unsafe { let src = gen_malloc::>(3).as_ptr(); write(src.offset(0), Some(counter.clone())); write(src.offset(1), Some(counter.clone())); write(src.offset(2), None); counter.assert_eq(0); let array = MArray::from_raw(src); assert_eq!(array.len(), 2); array[0].as_ref().unwrap().assert_eq(0); array[1].as_ref().unwrap().assert_eq(0); } counter.assert_eq(2); } #[test] fn test_string() { unsafe { let src = gen_malloc::(5).as_ptr(); *src.offset(0) = 0x61; *src.offset(1) = -0x19; *src.offset(2) = -0x6c; *src.offset(3) = -0x4e; *src.offset(4) = 0; let string = MString::from_raw_unchecked(src); assert_eq!(&*string, "a甲"); } } #[test] fn test_non_utf8_string() { unsafe { let src = gen_malloc::(2).as_ptr(); *src.offset(0) = -1; *src.offset(1) = 0; let string = MString::from_raw(src); assert!(string.is_err()); let src2 = gen_malloc::(2).as_ptr(); *src2.offset(0) = 1; *src2.offset(1) = 0; let string2 = MString::from_raw(src2); assert_eq!(string2.unwrap().deref(), "\u{1}"); } } #[cfg(feature = "std")] #[test] fn test_c_str() { unsafe { let src = gen_malloc::(2).as_ptr(); *src.offset(0) = 1; *src.offset(1) = 0; let string = MString::from_raw_unchecked(src); let c_str = string.as_c_str(); assert_eq!(c_str, CStr::from_ptr(b"\x01\x00".as_ptr() as *const c_char)); } } #[test] fn test_array_into_mbox() { let first = MArray::from_slice(&[123, 456, 789]); let second = first.clone(); assert_eq!(&*first.into_mbox(), &[123, 456, 789]); assert_eq!(&*second.into_mbox_with_sentinel(), &[123, 456, 789, 0]); } #[test] fn test_string_into_mbox() { let first = MString::from("abcde"); let second = first.clone(); assert_eq!(first.as_bytes(), b"abcde"); assert_eq!(&*first.into_mbox(), "abcde"); assert_eq!(second.as_bytes_with_sentinel(), b"abcde\0"); assert_eq!(&*second.into_mbox_with_sentinel(), "abcde\0"); } #[test] fn test_default_array() { let arr = MArray::::default(); assert_eq!(arr.into_mbox_with_sentinel(), MBox::from_slice(&[0u64])); } #[test] fn test_default_string() { let string = MString::default(); assert_eq!(string.into_mbox_with_sentinel(), MBox::::from("\0")); } #[cfg(feature = "std")] #[test] fn test_hash_string() { use std::collections::HashSet; let mut hs: HashSet = HashSet::new(); hs.insert(MString::from("a")); hs.insert(MString::from("bcd")); let hs = hs; assert!(hs.contains("bcd")); assert!(!hs.contains("ef")); assert!(!hs.contains("bcd\0")); assert!(hs.contains("a")); assert!(hs.contains(&MString::from("bcd"))); assert!(!hs.contains(&MString::from("ef"))); assert!(hs.contains(&MString::from("a"))); } #[cfg(feature = "std")] #[test] fn test_hash_array() { use std::collections::HashSet; let mut hs: HashSet> = HashSet::new(); hs.insert(MArray::from_slice(b"a")); hs.insert(MArray::from_slice(b"bcd")); let hs = hs; assert!(hs.contains(&b"bcd"[..])); assert!(!hs.contains(&b"ef"[..])); assert!(!hs.contains(&b"bcd\0"[..])); assert!(hs.contains(&b"a"[..])); assert!(hs.contains(&MArray::from_slice(b"bcd"))); assert!(!hs.contains(&MArray::from_slice(b"ef"))); assert!(hs.contains(&MArray::from_slice(b"a"))); }