xref: /aosp_15_r20/external/pigweed/pw_bytes/rust/pw_bytes.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 //! pw_bytes is a collection of utilities for manipulating binary data.
16 //!
17 //! # Features
18 //! pw_bytes contains the follow features:
19 //! * macros for concatenating `const [u8]`s and `&'static str`s.
20 //!
21 //! # Examples
22 //! ```
23 //! use pw_bytes::concat_const_u8_slices;
24 //!
25 //! // Concatenate two slices.
26 //! const SLICE_A: &[u8] = b"abc";
27 //! const SLICE_B: &[u8] = b"def";
28 //! const SLICE_AB: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B);
29 //! assert_eq!(SLICE_AB, b"abcdef");
30 //! ```
31 //!
32 //! ```
33 //! use pw_bytes::concat_static_strs;
34 //!
35 //! // Concatenate two strings.
36 //! const STR_A: &'static str = "abc";
37 //! const STR_B: &'static str = "def";
38 //! const STR_AB: &'static str = concat_static_strs!(STR_A, STR_B);
39 //! assert_eq!(STR_AB, "abcdef");
40 //!
41 //! ```
42 #![no_std]
43 #![deny(missing_docs)]
44 
45 /// Concatenates multiple `const [u8]`s into one.
46 ///
47 /// Returns a `const [u8]`
48 #[macro_export]
49 macro_rules! concat_const_u8_slices {
50   ($($slice:expr),+) => {{
51       // Calculate the length of the resulting array.  Because `+` is not a
52       // valid `MacroRepSep` (see https://doc.rust-lang.org/reference/macros-by-example.html#macros-by-example),
53       // we must precede the variadic expansion with a 0 and embed the + in
54       // the expansion itself.
55       const TOTAL_LEN: usize = 0 $(+ $slice.len())+;
56       const ARRAY: [u8; TOTAL_LEN] = {
57           let mut array = [0u8; TOTAL_LEN];
58           let mut array_index = 0;
59 
60           // For each input slice, copy its contents into `array`.
61           $({
62               // Using while loop as for loops are not allowed in `const` expressions
63               let mut slice_index = 0;
64               while slice_index < $slice.len() {
65                   array[array_index] = $slice[slice_index];
66                   array_index += 1;
67                   slice_index += 1;
68               }
69           })+;
70 
71           array
72       };
73       &ARRAY
74   }}
75 }
76 
77 /// Concatenates multiple `const &'static str`s into one.
78 ///
79 /// Returns a `const &'static str`
80 #[macro_export]
81 macro_rules! concat_static_strs {
82   ($($string:expr),+) => {{
83     // Safety: we're building a byte array of known valid utf8 strings so the
84     // resulting string is guaranteed to be valid utf8.
85     unsafe{
86       core::str::from_utf8_unchecked($crate::concat_const_u8_slices!($($string.as_bytes()),+))
87     }
88   }}
89 }
90 
91 #[cfg(test)]
92 mod tests {
93     #[test]
one_const_slice_concatenates_correctly()94     fn one_const_slice_concatenates_correctly() {
95         const SLICE_A: &[u8] = b"abc";
96         const SLICE_A_PRIME: &[u8] = concat_const_u8_slices!(SLICE_A);
97         assert_eq!(SLICE_A_PRIME, b"abc");
98     }
99 
100     #[test]
two_const_slices_concatenates_correctly()101     fn two_const_slices_concatenates_correctly() {
102         const SLICE_A: &[u8] = b"abc";
103         const SLICE_B: &[u8] = b"def";
104         const SLICE_AB: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B);
105         assert_eq!(SLICE_AB, b"abcdef");
106     }
107 
108     #[test]
three_const_slices_concatenates_correctly()109     fn three_const_slices_concatenates_correctly() {
110         const SLICE_A: &[u8] = b"abc";
111         const SLICE_B: &[u8] = b"def";
112         const SLICE_C: &[u8] = b"ghi";
113         const SLICE_ABC: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B, SLICE_C);
114         assert_eq!(SLICE_ABC, b"abcdefghi");
115     }
116 
117     #[test]
empty_first_const_slice_concatenates_correctly()118     fn empty_first_const_slice_concatenates_correctly() {
119         const SLICE_A: &[u8] = b"";
120         const SLICE_B: &[u8] = b"def";
121         const SLICE_C: &[u8] = b"ghi";
122         const SLICE_ABC: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B, SLICE_C);
123         assert_eq!(SLICE_ABC, b"defghi");
124     }
125 
126     #[test]
empty_middle_const_slice_concatenates_correctly()127     fn empty_middle_const_slice_concatenates_correctly() {
128         const SLICE_A: &[u8] = b"abc";
129         const SLICE_B: &[u8] = b"";
130         const SLICE_C: &[u8] = b"ghi";
131         const SLICE_ABC: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B, SLICE_C);
132         assert_eq!(SLICE_ABC, b"abcghi");
133     }
134 
135     #[test]
empty_last_const_slice_concatenates_correctly()136     fn empty_last_const_slice_concatenates_correctly() {
137         const SLICE_A: &[u8] = b"abc";
138         const SLICE_B: &[u8] = b"def";
139         const SLICE_C: &[u8] = b"";
140         const SLICE_ABC: &[u8] = concat_const_u8_slices!(SLICE_A, SLICE_B, SLICE_C);
141         assert_eq!(SLICE_ABC, b"abcdef");
142     }
143 
144     #[test]
145     // Since `concat_static_strs!` uses `concat_const_u8_slices!`, we rely on
146     // the exhaustive tests above for testing edge conditions.
strings_concatenates_correctly()147     fn strings_concatenates_correctly() {
148         const STR_A: &str = "abc";
149         const STR_B: &str = "def";
150         const STR_AB: &str = concat_static_strs!(STR_A, STR_B);
151         assert_eq!(STR_AB, "abcdef");
152     }
153 }
154