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