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