const_str/__ctfe/
concat_bytes.rs

1pub struct ConcatBytesPart<T>(pub T);
2
3impl ConcatBytesPart<u8> {
4    pub const fn output_len(&self) -> usize {
5        1
6    }
7
8    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
9        crate::bytes::clone(&[self.0])
10    }
11}
12
13impl<const L: usize> ConcatBytesPart<&[u8; L]> {
14    pub const fn output_len(&self) -> usize {
15        L
16    }
17
18    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
19        crate::bytes::clone(self.0)
20    }
21}
22
23impl ConcatBytesPart<&[u8]> {
24    pub const fn output_len(&self) -> usize {
25        self.0.len()
26    }
27
28    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
29        crate::bytes::clone(self.0)
30    }
31}
32
33impl<const L: usize> ConcatBytesPart<[u8; L]> {
34    pub const fn output_len(&self) -> usize {
35        L
36    }
37
38    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
39        crate::bytes::clone(&self.0)
40    }
41}
42
43impl ConcatBytesPart<&str> {
44    pub const fn output_len(&self) -> usize {
45        self.0.len()
46    }
47
48    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
49        crate::bytes::clone(self.0.as_bytes())
50    }
51}
52
53pub struct ConcatBytes<'a>(pub &'a [&'a [u8]]);
54
55impl ConcatBytes<'_> {
56    pub const fn output_len(&self) -> usize {
57        let parts = self.0;
58        let mut sum = 0;
59        let mut i = 0;
60        while i < parts.len() {
61            sum += parts[i].len();
62            i += 1;
63        }
64        sum
65    }
66
67    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
68        let mut buf = [0; N];
69        let mut pos = 0;
70
71        macro_rules! push {
72            ($x:expr) => {
73                buf[pos] = $x;
74                pos += 1;
75            };
76        }
77
78        let parts = self.0;
79        let mut i = 0;
80        while i < parts.len() {
81            let part = parts[i];
82            let mut j = 0;
83            while j < part.len() {
84                push!(part[j]);
85                j += 1;
86            }
87            i += 1;
88        }
89
90        assert!(pos == N);
91        buf
92    }
93}
94
95#[doc(hidden)]
96#[macro_export]
97macro_rules! __concat_bytes_part {
98    ($x: expr) => {{
99        const OUTPUT_LEN: usize = $crate::__ctfe::ConcatBytesPart($x).output_len();
100        const OUTPUT_BUF: [u8; OUTPUT_LEN] = $crate::__ctfe::ConcatBytesPart($x).const_eval();
101        const OUTPUT: &[u8] = &OUTPUT_BUF;
102        OUTPUT
103    }};
104}
105
106/// Concatenates values into a byte slice.
107///
108/// The input type must be one of
109/// + [`u8`]
110/// + [`&[u8]`](slice)
111/// + [`[u8; N]`](array), [`&[u8; N]`](array)
112/// + [`&str`](str)
113///
114/// The output type is [`&[u8; _]`](array).
115///
116/// This macro is [const-context only](./index.html#const-context-only).
117///
118/// # Examples
119///
120/// ```rust
121/// const S1: &[u8; 7] = const_str::concat_bytes!(b'A', b"BC", [68, b'E', 70], "G");
122/// const S2: &[u8] = const_str::concat_bytes!(S1, "/123", 0u8);
123/// assert_eq!(S1, b"ABCDEFG");
124/// assert_eq!(S2, b"ABCDEFG/123\x00");
125/// ```
126///
127#[macro_export]
128macro_rules! concat_bytes {
129    ($($x: expr),+ $(,)?) => {{
130        const PARTS: &[&[u8]] = &[$( $crate::__concat_bytes_part!($x) ),+];
131        const OUTPUT_LEN: usize = $crate::__ctfe::ConcatBytes(PARTS).output_len();
132        const OUTPUT_BUF: [u8; OUTPUT_LEN] = $crate::__ctfe::ConcatBytes(PARTS).const_eval();
133        &OUTPUT_BUF
134    }};
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_concat_bytes() {
143        const S1: &[u8; 7] = concat_bytes!(b'A', b"BC", [68, b'E', 70], "G");
144        const S2: &[u8] = concat_bytes!(S1, "/123", 0u8);
145        assert_eq!(S1, b"ABCDEFG");
146        assert_eq!(S2, b"ABCDEFG/123\x00");
147
148        const S3: &[u8] = concat_bytes!(b"hello", b" ", b"world");
149        assert_eq!(S3, b"hello world");
150
151        const S4: &[u8] = concat_bytes!(b'x');
152        assert_eq!(S4, b"x");
153
154        const S5: &[u8] = concat_bytes!("test", b"123");
155        assert_eq!(S5, b"test123");
156
157        const ARR: [u8; 3] = [1, 2, 3];
158        const S6: &[u8] = concat_bytes!(ARR, [4, 5]);
159        assert_eq!(S6, &[1, 2, 3, 4, 5]);
160    }
161
162    #[test]
163    fn test_concat_bytes_runtime() {
164        // Runtime tests for ConcatBytesPart
165        let part_u8 = ConcatBytesPart(42u8);
166        assert_eq!(part_u8.output_len(), 1);
167        let buf: [u8; 1] = part_u8.const_eval();
168        assert_eq!(buf, [42]);
169
170        let arr: &[u8; 3] = b"abc";
171        let part_arr = ConcatBytesPart(arr);
172        assert_eq!(part_arr.output_len(), 3);
173        let buf_arr: [u8; 3] = part_arr.const_eval();
174        assert_eq!(buf_arr, [b'a', b'b', b'c']);
175
176        let slice: &[u8] = b"hello";
177        let part_slice = ConcatBytesPart(slice);
178        assert_eq!(part_slice.output_len(), 5);
179        let buf_slice: [u8; 5] = part_slice.const_eval();
180        assert_eq!(buf_slice, *b"hello");
181
182        let owned_arr: [u8; 2] = [1, 2];
183        let part_owned = ConcatBytesPart(owned_arr);
184        assert_eq!(part_owned.output_len(), 2);
185        let buf_owned: [u8; 2] = part_owned.const_eval();
186        assert_eq!(buf_owned, [1, 2]);
187
188        let str_part = ConcatBytesPart("test");
189        assert_eq!(str_part.output_len(), 4);
190        let buf_str: [u8; 4] = str_part.const_eval();
191        assert_eq!(buf_str, *b"test");
192
193        // Runtime tests for ConcatBytes
194        let parts: &[&[u8]] = &[b"hello", b"world"];
195        let concat = ConcatBytes(parts);
196        assert_eq!(concat.output_len(), 10);
197        let buf: [u8; 10] = concat.const_eval();
198        assert_eq!(&buf, b"helloworld");
199
200        let empty_parts: &[&[u8]] = &[];
201        let concat_empty = ConcatBytes(empty_parts);
202        assert_eq!(concat_empty.output_len(), 0);
203        let buf_empty: [u8; 0] = concat_empty.const_eval();
204        assert_eq!(&buf_empty, b"");
205
206        // Test multiple parts
207        let multi_parts: &[&[u8]] = &[b"a", b"b", b"c"];
208        let concat_multi = ConcatBytes(multi_parts);
209        assert_eq!(concat_multi.output_len(), 3);
210        let buf_multi: [u8; 3] = concat_multi.const_eval();
211        assert_eq!(&buf_multi, b"abc");
212    }
213}