byte_strings/
const.rs

1/*! `const`-friendly equivalents of the top-level crates.
2
3<details><summary><code>const</code>-friendly?</summary>
4
5The default / naïve implementation of the macros of this crate (ab)used the
6proc-macro capabilities to inspect the contents / values of a given (byte)
7string literal. But besides that capability, (procedural) macros can't do any
8other form of semantic evaluation.
9
10This means that when fed a `const`ant expression evaluating to a valid (byte)
11string literal,
12
13  - such as a `const`:
14
15    ```rust ,compile_fail
16    #[macro_use]
17    extern crate byte_strings;
18
19    fn main ()
20    {
21        const FOO: &str = "foo";
22
23        // FAILS with something along the lines of "expected a literal"
24        const FOO_BYTES: &[u8] = as_bytes!(FOO);
25    }
26    ```
27
28  - or some macro <sub><sup>(besides `concat!`, `stringify!`, which are
29    *syntactically* detected and thus get to feature ad-hoc polyfilled
30    support)</sup></sub>:
31
32    ```rust ,compile_fail
33    #[macro_use]
34    extern crate byte_strings;
35
36    # mod third_party_lib { pub(in super) use stringify as their_macro; }
37    #
38    fn main ()
39    {
40        // FAILS with something along the lines of "expected a literal"
41        let _ = as_bytes!(third_party_lib::their_macro!(...));
42    }
43    ```
44
45</details>
46
47The macros of this module have been written to use `const fn`s as much as
48possible, so as to use the **semantic evaluation built within the compiler**
49rather than the limited syntactical evaluation of classic macros. This allows
50the macros in this module to be able to support any kind of `const`-evaluatable
51(byte) string:
52
53```rust
54use ::byte_strings::const_::c_str;
55use ::core::ffi::CStr;
56
57const MESSAGE: &str = "Hello, World!";
58const C_MESSAGE: &CStr = c_str!(MESSAGE); // OK!
59```
60*/
61
62#[doc(hidden)] /** Not part of the public API **/ pub
63mod __ {
64    #![allow(nonstandard_style)]
65    pub use ::core;
66
67    pub
68    struct const_<T> /* = */ (
69        pub T,
70    );
71
72    impl<'lt> const_<&'lt str> {
73        pub
74        const
75        fn as_bytes (self)
76          -> &'lt [u8]
77        {
78            self.0.as_bytes()
79        }
80    }
81
82    impl<'lt> const_<&'lt [u8]> {
83        pub
84        const
85        fn as_bytes (self)
86          -> &'lt [u8]
87        {
88            self.0
89        }
90    }
91
92    impl<'lt, const N: usize> const_<&'lt [u8; N]> {
93        pub
94        const
95        fn as_bytes (self)
96          -> &'lt [u8]
97        {
98            self.0
99        }
100    }
101
102    #[repr(C)]
103    pub
104    struct Contiguous<_0, _1> /* = */ (
105        pub _0,
106        pub _1,
107    );
108
109    pub
110    const
111    fn c_strlen (bytes: &[u8])
112      -> usize
113    {
114        let mut i = 0;
115        while i < bytes.len() {
116            if bytes[i] == b'\0' {
117                break;
118            }
119            i += 1;
120        }
121        i
122    }
123
124    pub
125    struct c_strlen<const N: usize> {}
126}
127
128/// [`const`-friendly][crate::const_] version of [`as_bytes!`][crate::as_bytes].
129///
130/// ```rust
131/// # fn main () {}
132/// #[macro_use]
133/// extern crate byte_strings;
134///
135/// const MESSAGE: &str = "Hello, World!";
136/// const MESSAGE_BYTES: &[u8] = const_as_bytes!(MESSAGE);
137/// ```
138#[macro_export]
139macro_rules! const_as_bytes {( $s:expr $(,)? ) => ({
140    const __BYTES: &'static [u8] = $crate::const_::__::const_($s).as_bytes();
141    unsafe {
142        $crate::__::core::mem::transmute::<
143            *const u8,
144            &'static [u8; __BYTES.len()],
145        >(
146            __BYTES.as_ptr()
147        )
148    }
149})}
150#[doc(inline)] pub use const_as_bytes as as_bytes;
151
152/// [`const`-friendly][crate::const_] version of [`concat!`][::core::concat].
153///
154/// ```rust
155/// # fn main () {}
156/// #[macro_use]
157/// extern crate byte_strings;
158///
159/// const GREETING: &str = "Hello";
160/// const MESSAGE: &str = const_concat!(GREETING, ", World!");
161/// ```
162#[macro_export]
163macro_rules! const_concat {(
164    $($s:expr),* $(,)?
165) => (
166    unsafe {
167        $crate::__::core::str::from_utf8_unchecked(
168            $crate::const_concat_bytes!(
169                $(
170                    <$crate::__::core::primitive::str>::as_bytes($s)
171                ),*
172            )
173        )
174    }
175)}
176#[doc(inline)] pub use const_concat as concat;
177
178/// [`const`-friendly][crate::const_] version of
179/// [`c_str!`][crate::c_str].
180///
181/// ```rust
182/// use ::byte_strings::const_;
183/// use ::core::ffi::CStr;
184///
185/// const MESSAGE: &str = "Hello, World!";
186/// const C_MESSAGE: &CStr = const_::c_str!(MESSAGE);
187/// ```
188///
189/// Inner null bytes are still rejected at compile time:
190///
191/// ```rust ,compile_fail
192/// use ::byte_strings::const_;
193/// use ::core::ffi::CStr;
194///
195/// const MESSAGE: &str = "Hell\0, World!";
196/// const C_MESSAGE: &CStr = const_::c_str!(MESSAGE); // Error.
197/// ```
198#[macro_export]
199macro_rules! const_cstr {() => ( $crate::cstr!() ); (
200    $($s:expr),* $(,)?
201) => ({
202    const BYTES: &[$crate::__::core::primitive::u8] = {
203        $crate::const_concat_bytes!($($s ,)*)
204    };
205    /// Assert lack of inner null bytes.
206    const _: $crate::const_::__::c_strlen::<{
207        BYTES.len() - if BYTES[BYTES.len() - 1] == b'\0' { 1 } else { 0 }
208    }> = $crate::const_::__::c_strlen::<{
209        $crate::const_::__::c_strlen(BYTES)
210    }> {};
211    unsafe {
212        $crate::__::core::ffi::CStr::from_bytes_with_nul_unchecked(
213            // Append a null terminator if needed.
214            if BYTES[BYTES.len() - 1] == b'\0' {
215                BYTES
216            } else {
217                $crate::const_concat_bytes!(BYTES, b"\0")
218            }
219        )
220    }
221})}
222#[doc(inline)] pub use const_cstr as c_str;
223
224/// [`const`-friendly][crate::const_] version of
225/// [`concat_bytes!`][crate::concat_bytes].
226///
227/// ```rust
228/// # fn main () {}
229/// #[macro_use]
230/// extern crate byte_strings;
231///
232/// const GREETING: &str = "Hello";
233/// const MESSAGE: &[u8; 13] = const_concat_bytes!(GREETING, ", World!");
234/// ```
235#[macro_export]
236macro_rules! const_concat_bytes {
237    () => (b"");
238    (
239        $single:expr $(,)?
240    ) => (
241        $crate::const_as_bytes!($single)
242    );
243
244    (
245        $first:expr $(,
246        $rest:expr)+ $(,)?
247    ) => (
248        $crate::__concat_bytes_two!(
249            $crate::const_as_bytes!($first),
250            $crate::const_concat_bytes!($($rest),+),
251        )
252    );
253}
254#[doc(inline)] pub use const_concat_bytes as concat_bytes;
255
256#[doc(hidden)] /** Not part of the public API */ #[macro_export]
257macro_rules! __concat_bytes_two {(
258    $left:expr,
259    $right:expr $(,)?
260) => ({
261    const LEFT: &'static [$crate::__::core::primitive::u8] = $left;
262    const RIGHT: &'static [$crate::__::core::primitive::u8] = $right;
263    unsafe {
264        use $crate::const_::__::{Contiguous, core::{self, primitive::*, mem}};
265        const LEFT_LEN: usize = LEFT.len();
266        const LEFT_BYTES: &'static [u8; LEFT_LEN] = unsafe {
267            mem::transmute(LEFT.as_ptr())
268        };
269        const RIGHT_LEN: usize = RIGHT.len();
270        const RIGHT_BYTES: &'static [u8; RIGHT_LEN] = unsafe {
271            mem::transmute(RIGHT.as_ptr())
272        };
273        const CONCAT_CONTIGUOUS: (
274            &'static Contiguous<
275                [u8; LEFT_LEN],
276                [u8; RIGHT_LEN],
277            >
278        ) = &Contiguous(
279            *LEFT_BYTES,
280            *RIGHT_BYTES,
281        );
282        const CONCAT_BYTES: &'static [u8; LEFT_LEN + RIGHT_LEN] = unsafe {
283            mem::transmute(CONCAT_CONTIGUOUS)
284        };
285        CONCAT_BYTES
286    }
287})}