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
54use std::os::raw::c_char;
55use 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)]
64pub 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
73impl 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]
143macro_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]
160fn 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)]
171mod test_creates_pub_str_mod {
172 const_cstr! {
173 pub FIRST = "first";
174 pub SECOND = "second";
175 }
176}
177
178#[test]
179fn 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