xref: /aosp_15_r20/external/pigweed/pw_bytes/rust/pw_bytes.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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