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 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
// Copyright (c) 2015 const-cstr developers // Copyright (c) 2017 const-cstr-fork developers // Licensed under the Apache License, Version 2.0 // <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! Create static C-compatible strings from Rust string literals. //! //! Example //! ------- //! //! ```rust //! #[macro_use] extern crate const_cstr_fork; //! // Just for the `libc::c_char` type alias. //! extern crate libc; //! //! use std::ffi::CStr; //! //! const_cstr! { //! HELLO_CSTR = "Hello, world!"; //! //! // Multiple declarations can be made with one invocation. //! // GOODNIGHT_CSTR = "Goodnight, sun!"; //! //! // But only with the same visibility: //! // pub GOODNIGHT_CSTR = "Goodnight, sun!"; //! // ^~~ Error: expected identifier, found `pub` //! } //! //! // Imagine this is an `extern "C"` function linked from some other lib. //! unsafe fn print_c_string(cstr: *const libc::c_char) { //! println!("{}", CStr::from_ptr(cstr).to_str().unwrap()); //! } //! //! fn main() { //! // When just passed a literal, returns an rvalue instead. //! let goodnight_cstr = const_cstr!("Goodnight, sun!"); //! //! unsafe { //! print_c_string(HELLO_CSTR.as_ptr()); //! print_c_string(goodnight_cstr.as_ptr()); //! } //! } //! ``` //! //! Prints: //! //! ```notest //! Hello, world! //! Goodnight, sun! //! ``` #![allow(non_snake_case)] extern crate libc; use std::ffi::CStr; pub trait ToPointerCString { #[inline(always)] fn to_ptr(&self) -> *const ::libc::c_char; } /// A type representing a static C-compatible string. /// /// Note /// ---- /// Use ONLY the `const_cstr!` macro to create an instance of this struct. /// The macro will include the NUL byte for you. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ConstCStr { /// The wrapped string value. Not intended to be used for manual initialization. /// Public only to allow initialization by the `const_cstr!` macro. /// /// Includes the NUL terminating byte. Use `to_str()` to get an `&'static str` /// without the NUL terminating byte. pub cValue: &'static str, /// Excludes the terminating byte pub rustValue: &'static str, } impl ToPointerCString for ConstCStr { #[inline(always)] fn to_ptr(&self) -> *const ::libc::c_char { self.as_ptr() } } impl ToPointerCString for CStr { #[inline(always)] fn to_ptr(&self) -> *const ::libc::c_char { self.as_ptr() as *const ::libc::c_char } } impl ConstCStr { /// Returns the wrapped string, without the NUL terminating byte. /// /// Compare to `CStr::to_str()` which checks that the string is valid UTF-8 first, /// since it starts from an arbitrary pointer instead of a Rust string slice. #[inline(always)] pub fn to_str(&self) -> &'static str { self.rustValue } /// Returns the wrapped string as a byte slice, **without** the NUL terminating byte. #[inline(always)] pub fn to_bytes(&self) -> &'static [u8] { self.rustValue.as_bytes() } /// Returns the wrapped string as a byte slice, *with** the NUL terminating byte. #[inline(always)] pub fn to_bytes_with_nul(&self) -> &'static [u8] { self.cValue.as_bytes() } /// Returns a pointer to the beginning of the wrapped string. /// /// Suitable for passing to any function that expects a C-compatible string. /// Since the underlying string is guaranteed to be `'static`, /// the pointer should always be valid. #[inline(always)] pub fn as_ptr(&self) -> *const libc::c_char { self.cValue.as_ptr() as *const libc::c_char } /// Returns the wrapped string as an `&'static CStr`, skipping the length check that /// `CStr::from_ptr()` performs (since we know the length already). #[inline(always)] pub fn as_cstr(&self) -> &'static CStr { unsafe { CStr::from_ptr(self.cValue.as_ptr() as *const i8) } } } /// Create a C-compatible string as an rvalue or a `const` binding. /// Appends a NUL byte to the passed string. /// /// Multiple `const` declarations can be created with one invocation, but only with the same /// visibility (`pub` or not). /// /// See crate root documentation for example usage. /// /// Note /// ---- /// For logical consistency, the passed string(s) should not contain any NUL bytes. /// Remember that functions consuming a C-string will only see up to the first NUL byte. #[macro_export] macro_rules! const_cstr { ($strval:expr) => ( $crate::ConstCStr { rustValue: $strval, cValue: concat!($strval, "\0") } ); ($($strname:ident = $strval:expr);+;) => ( $( const $strname: $crate::ConstCStr = const_cstr!($strval); )+ ); ($(pub $strname:ident = $strval:expr);+;) => ( $( pub const $strname: $crate::ConstCStr = const_cstr!($strval); )+ ); } #[test] fn test_creates_valid_str() { const_cstr! { HELLO_CSTR = "Hello, world!"; } let cstr = unsafe { CStr::from_ptr(HELLO_CSTR.as_ptr()) }; assert_eq!(HELLO_CSTR.to_str(), cstr.to_str().unwrap()); }