1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! Operations on [UTF-8]-encoded [C strings][c_str]. //! //! The [`CUtf8`] and [`CUtf8Buf`] types are guaranteed to be: //! //! - [Nul (Ø) terminated C strings][c_str] in order to more safely ensure that //! C APIs only access memory that is properly owned. //! //! - Encoded as valid [UTF-8], allowing for passing around native Rust [`str`] //! strings with ease. //! //! # Usage //! //! This crate is available [on crates.io](https://crates.io/crates/c_utf8) and //! can be used by adding the following to your project's //! [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html): //! //! ```toml //! [dependencies] //! c_utf8 = "0.1" //! ``` //! //! and this to your crate root (`lib.rs` or `main.rs`): //! //! ``` //! #[macro_use] // enables c_utf8! macro //! extern crate c_utf8; //! # fn main() {} //! ``` //! //! # Examples //! //! A [`CUtf8`] slice can be created via the [`c_utf8!`](macro.c_utf8.html) //! macro, which ensures it will _always_ end with a trailing 0 byte: //! //! ``` //! # #[macro_use] extern crate c_utf8; //! use c_utf8::CUtf8; //! //! static MESSAGE: &CUtf8 = c_utf8!("Heyo!"); //! //! fn main() { //! let bytes = [72, 101, 121, 111, 33, 0]; //! assert_eq!(MESSAGE.as_bytes_with_nul(), &bytes); //! } //! ``` //! //! [UTF-8]: https://en.wikipedia.org/wiki/UTF-8 //! [c_str]: https://en.wikipedia.org/wiki/Null-terminated_string //! [`str`]: https://doc.rust-lang.org/std/primitive.str.html //! [`CUtf8`]: struct.CUtf8.html //! [`CUtf8Buf`]: struct.CUtf8Buf.html #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "try_from", feature(try_from))] #![cfg_attr(all(test, nightly), feature(test))] #[cfg(all(test, nightly))] extern crate test; #[cfg(feature = "std")] use std as core; /// Creates a [`&'static CUtf8`](struct.CUtf8.html) from a native Rust [`str`] /// string literal, making it much easier to work with C APIs that are strict /// about encoding input as UTF-8. /// /// # Usage /// /// Although the input string can have a 0 byte, it is **highly recommended** to /// not have one. This is because C APIs will only work with the memory up to /// the first 0 byte. In the future, it will be very likely be a **hard error** /// to have a 0 byte within the string literal. /// /// # Examples /// /// The resulting string will _always_ end with a 0 byte: /// /// ``` /// #[macro_use] /// extern crate c_utf8; /// /// fn main() { /// let string = c_utf8!("Hello!"); /// let bytes = [72, 101, 108, 108, 111, 33, 0]; /// /// assert_eq!(string.as_bytes_with_nul(), &bytes); /// } /// ``` /// /// The macro can even be evaluated within a constant expression. This allows /// for having instances of types with `&'static CUtf8` fields. /// /// ``` /// # #[macro_use] extern crate c_utf8; use c_utf8::CUtf8; fn main() { /// static APP_NAME: &CUtf8 = c_utf8!(env!("CARGO_PKG_NAME")); /// /// assert_eq!(APP_NAME.as_str_with_nul(), "c_utf8\0"); /// # } /// ``` /// /// [`str`]: https://doc.rust-lang.org/std/primitive.str.html #[macro_export] macro_rules! c_utf8 { ($s:expr) => { unsafe { // An internal type that allows for converting static Rust string // slices into static CUtf8 slices within a constant expression union _Ref<'a> { s: &'a str, c: &'a $crate::CUtf8 } _Ref { s: concat!($s, "\0") }.c } } } #[cfg(feature = "std")] mod c_utf8_buf; mod c_utf8; mod error; #[cfg(feature = "std")] pub use self::c_utf8_buf::*; pub use self::c_utf8::*; pub use self::error::*; /// Equivalent to C's `char` type. #[allow(non_camel_case_types)] #[cfg(not(feature = "std"))] #[cfg(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x")), all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all(target_os = "openbsd", target_arch = "aarch64"), all(target_os = "fuchsia", target_arch = "aarch64")))] pub type c_char = u8; /// Equivalent to C's `char` type. #[allow(non_camel_case_types)] #[cfg(not(feature = "std"))] #[cfg(not(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x")), all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all(target_os = "openbsd", target_arch = "aarch64"), all(target_os = "fuchsia", target_arch = "aarch64"))))] pub type c_char = i8; #[cfg(feature = "std")] pub use std::os::raw::c_char; #[inline] fn is_nul_terminated(s: &str) -> bool { s.as_bytes().last().cloned() == Some(0) }