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})}