1 | // Copyright (c) 2015 const-cstr developers
|
2 | // Licensed under the Apache License, Version 2.0
|
3 | // <LICENSE-APACHE or
|
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
5 | // license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
6 | // at your option. All files in the project carrying such
|
7 | // notice may not be copied, modified, or distributed except
|
8 | // according to those terms.
|
9 | //! Create static C-compatible strings from Rust string literals.
|
10 | //!
|
11 | //! Example
|
12 | //! -------
|
13 | //!
|
14 | //! ```rust
|
15 | //! #[macro_use] extern crate const_cstr;
|
16 | //!
|
17 | //! use std::os::raw::c_char;
|
18 | //! use std::ffi::CStr;
|
19 | //!
|
20 | //! const_cstr! {
|
21 | //! HELLO_CSTR = "Hello, world!" ;
|
22 | //!
|
23 | //! // Multiple declarations can be made with one invocation.
|
24 | //! // GOODNIGHT_CSTR = "Goodnight, sun!";
|
25 | //!
|
26 | //! // But only with the same visibility:
|
27 | //! // pub GOODNIGHT_CSTR = "Goodnight, sun!";
|
28 | //! // ^~~ Error: expected identifier, found `pub`
|
29 | //! }
|
30 | //!
|
31 | //! // Imagine this is an `extern "C"` function linked from some other lib.
|
32 | //! unsafe fn print_c_string(cstr: *const c_char) {
|
33 | //! println!("{}" , CStr::from_ptr(cstr).to_str().unwrap());
|
34 | //! }
|
35 | //!
|
36 | //! fn main() {
|
37 | //! // When just passed a literal, returns an rvalue instead.
|
38 | //! let goodnight_cstr = const_cstr!("Goodnight, sun!" );
|
39 | //!
|
40 | //! unsafe {
|
41 | //! print_c_string(HELLO_CSTR.as_ptr());
|
42 | //! print_c_string(goodnight_cstr.as_ptr());
|
43 | //! }
|
44 | //! }
|
45 | //! ```
|
46 | //!
|
47 | //! Prints:
|
48 | //!
|
49 | //! ```notest
|
50 | //! Hello, world!
|
51 | //! Goodnight, sun!
|
52 | //! ```
|
53 |
|
54 | use std::os::raw::c_char;
|
55 | use std::ffi::CStr;
|
56 |
|
57 | /// A type representing a static C-compatible string, wrapping `&'static str`.
|
58 | ///
|
59 | /// Note
|
60 | /// ----
|
61 | /// Prefer the `const_cstr!` macro to create an instance of this struct
|
62 | /// over manual initialization. The macro will include the NUL byte for you.
|
63 | #[derive (Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
64 | pub struct ConstCStr {
|
65 | /// The wrapped string value. Not intended to be used for manual initialization.
|
66 | /// Public only to allow initialization by the `const_cstr!` macro.
|
67 | ///
|
68 | /// Includes the NUL terminating byte. Use `to_str()` to get an `&'static str`
|
69 | /// without the NUL terminating byte.
|
70 | pub val: &'static str,
|
71 | }
|
72 |
|
73 | impl ConstCStr {
|
74 | /// Returns the wrapped string, without the NUL terminating byte.
|
75 | ///
|
76 | /// Compare to `CStr::to_str()` which checks that the string is valid UTF-8 first,
|
77 | /// since it starts from an arbitrary pointer instead of a Rust string slice.
|
78 | pub fn to_str(&self) -> &'static str {
|
79 | &self.val[..self.val.len() - 1]
|
80 | }
|
81 |
|
82 | /// Returns the wrapped string as a byte slice, **without** the NUL terminating byte.
|
83 | pub fn to_bytes(&self) -> &'static [u8] {
|
84 | self.to_str().as_bytes()
|
85 | }
|
86 |
|
87 | /// Returns the wrapped string as a byte slice, *with** the NUL terminating byte.
|
88 | pub fn to_bytes_with_nul(&self) -> &'static [u8] {
|
89 | self.val.as_bytes()
|
90 | }
|
91 |
|
92 | /// Returns a pointer to the beginning of the wrapped string.
|
93 | ///
|
94 | /// Suitable for passing to any function that expects a C-compatible string.
|
95 | /// Since the underlying string is guaranteed to be `'static`,
|
96 | /// the pointer should always be valid.
|
97 | ///
|
98 | /// Panics
|
99 | /// ------
|
100 | /// If the wrapped string is not NUL-terminated.
|
101 | /// (Unlikely if you used the `const_cstr!` macro. This is just a sanity check.)
|
102 | pub fn as_ptr(&self) -> *const c_char {
|
103 | let bytes = self.val.as_bytes();
|
104 |
|
105 | assert_eq!(bytes[bytes.len() - 1], b' \0' );
|
106 |
|
107 | self.val.as_bytes().as_ptr() as *const c_char
|
108 | }
|
109 |
|
110 | /// Returns the wrapped string as an `&'static CStr`, skipping the length check that
|
111 | /// `CStr::from_ptr()` performs (since we know the length already).
|
112 | ///
|
113 | /// Panics
|
114 | /// ------
|
115 | /// If the wrapped string is not NUL-terminated.
|
116 | /// (Unlikely if you used the `const_cstr!` macro. This is just a sanity check.)
|
117 | pub fn as_cstr(&self) -> &'static CStr {
|
118 | let bytes = self.val.as_bytes();
|
119 |
|
120 | assert_eq!(bytes[bytes.len() - 1], b' \0' );
|
121 |
|
122 | // This check is safe because of the above assert.
|
123 | // Interior nuls are more of a logic error than a memory saftey issue.
|
124 | unsafe {
|
125 | CStr::from_bytes_with_nul_unchecked(bytes)
|
126 | }
|
127 | }
|
128 | }
|
129 |
|
130 | /// Create a C-compatible string as an rvalue or a `const` binding.
|
131 | /// Appends a NUL byte to the passed string.
|
132 | ///
|
133 | /// Multiple `const` declarations can be created with one invocation, but only with the same
|
134 | /// visibility (`pub` or not).
|
135 | ///
|
136 | /// See crate root documentation for example usage.
|
137 | ///
|
138 | /// Note
|
139 | /// ----
|
140 | /// For logical consistency, the passed string(s) should not contain any NUL bytes.
|
141 | /// Remember that functions consuming a C-string will only see up to the first NUL byte.
|
142 | #[macro_export ]
|
143 | macro_rules! const_cstr {
|
144 | ($(pub $strname:ident = $strval:expr);+;) => (
|
145 | $(
|
146 | pub const $strname: $crate::ConstCStr = const_cstr!($strval);
|
147 | )+
|
148 | );
|
149 | ($strval:expr) => (
|
150 | $crate::ConstCStr { val: concat!($strval, " \0" ) }
|
151 | );
|
152 | ($($strname:ident = $strval:expr);+;) => (
|
153 | $(
|
154 | const $strname: $crate::ConstCStr = const_cstr!($strval);
|
155 | )+
|
156 | );
|
157 | }
|
158 |
|
159 | #[test ]
|
160 | fn test_creates_valid_str() {
|
161 | const_cstr! {
|
162 | HELLO_CSTR = "Hello, world!" ;
|
163 | }
|
164 |
|
165 | let cstr: &CStr = unsafe { CStr::from_ptr(HELLO_CSTR.as_ptr()) };
|
166 |
|
167 | assert_eq!(HELLO_CSTR.to_str(), cstr.to_str().unwrap());
|
168 | }
|
169 |
|
170 | #[cfg (test)]
|
171 | mod test_creates_pub_str_mod {
|
172 | const_cstr! {
|
173 | pub FIRST = "first" ;
|
174 | pub SECOND = "second" ;
|
175 | }
|
176 | }
|
177 |
|
178 | #[test ]
|
179 | fn test_creates_pub_str() {
|
180 | assert_eq!(test_creates_pub_str_mod::FIRST.to_str(), "first" );
|
181 | assert_eq!(test_creates_pub_str_mod::SECOND.to_str(), "second" );
|
182 | }
|
183 | |