• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Low-level utilities shared across multiple GBL libraries.
16 
17 #![cfg_attr(not(test), no_std)]
18 
19 use core::{cmp::min, str::from_utf8};
20 use liberror::{Error, Result};
21 use safemath::SafeNum;
22 
23 /// Returns the largest aligned subslice.
24 ///
25 /// This function drops as many bytes as needed from the front of the given slice to ensure the
26 /// result is properly-aligned. It does not truncate bytes from the end, so the resulting size may
27 /// not be a multiple of `alignment`.
28 ///
29 /// If the next `alignment` boundary would be directly following the last byte, this returns the
30 /// 0-length slice at that alignment rather than an error, to match standard slicing behavior.
31 ///
32 /// # Arguments
33 /// * `bytes`: the byte slice to align
34 /// * `alignment`: the desired starting alignment
35 ///
36 /// # Returns
37 /// * The subslice on success
38 /// * [Error::ArithmeticOverflow] if `bytes` overflows when finding the next `alignment`
39 /// * [Error::BufferTooSmall] if `bytes` is not large enough to reach the next `alignment`. The
40 ///   error will contain the size that would have been needed to reach `alignment`.
aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]> where T: Copy + Into<SafeNum>,41 pub fn aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]>
42 where
43     T: Copy + Into<SafeNum>,
44 {
45     let addr = bytes.as_ptr() as usize;
46     let aligned_offset = (SafeNum::from(addr).round_up(alignment) - addr).try_into()?;
47     Ok(bytes.get_mut(aligned_offset..).ok_or(Error::BufferTooSmall(Some(aligned_offset)))?)
48 }
49 
50 /// A helper for getting the offset of the first byte with and aligned address.
51 ///
52 /// # Arguments
53 /// * `bytes`: the byte slice
54 /// * `alignment`: the desired starting alignment.
55 ///
56 /// # Returns
57 ///
58 /// * Returns Ok(offset) on success, Err() on integer overflow.
aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize> where T: Copy + Into<SafeNum>,59 pub fn aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize>
60 where
61     T: Copy + Into<SafeNum>,
62 {
63     let addr = SafeNum::from(buffer.as_ptr() as usize);
64     (addr.round_up(alignment) - addr).try_into().map_err(From::from)
65 }
66 
67 /// A helper data structure for writing formatted string to fixed size bytes array.
68 #[derive(Debug)]
69 pub struct FormattedBytes<T>(T, usize);
70 
71 impl<T: AsMut<[u8]> + AsRef<[u8]>> FormattedBytes<T> {
72     /// Create an instance.
new(buf: T) -> Self73     pub fn new(buf: T) -> Self {
74         Self(buf, 0)
75     }
76 
77     /// Get the size of content.
size(&self) -> usize78     pub fn size(&self) -> usize {
79         self.1
80     }
81 
82     /// Appends the given `bytes` to the contents.
83     ///
84     /// If `bytes` exceeds the remaining buffer space, any excess bytes are discarded.
85     ///
86     /// Returns the resulting contents.
append(&mut self, bytes: &[u8]) -> &mut [u8]87     pub fn append(&mut self, bytes: &[u8]) -> &mut [u8] {
88         let buf = &mut self.0.as_mut()[self.1..];
89         // Only write as much as the size of the bytes buffer. Additional write is silently
90         // ignored.
91         let to_write = min(buf.len(), bytes.len());
92         buf[..to_write].clone_from_slice(&bytes[..to_write]);
93         self.1 += to_write;
94         &mut self.0.as_mut()[..self.1]
95     }
96 
97     /// Converts to string.
to_str(&self) -> &str98     pub fn to_str(&self) -> &str {
99         from_utf8(&self.0.as_ref()[..self.1]).unwrap_or("")
100     }
101 }
102 
103 impl<T: AsMut<[u8]> + AsRef<[u8]>> core::fmt::Write for FormattedBytes<T> {
write_str(&mut self, s: &str) -> core::fmt::Result104     fn write_str(&mut self, s: &str) -> core::fmt::Result {
105         self.append(s.as_bytes());
106         Ok(())
107     }
108 }
109 
110 /// A convenient macro that behaves similar to snprintf in C.
111 ///
112 /// Panics if the written string is not UTF-8.
113 #[macro_export]
114 macro_rules! snprintf {
115     ( $arr:expr, $( $x:expr ),* ) => {
116         {
117             let mut bytes = $crate::FormattedBytes::new(&mut $arr[..]);
118             core::fmt::Write::write_fmt(&mut bytes, core::format_args!($($x,)*)).unwrap();
119             let size = bytes.size();
120             core::str::from_utf8(&$arr[..size]).unwrap()
121         }
122     };
123 }
124 
125 #[cfg(test)]
126 mod test {
127     use super::*;
128 
129     // A byte array that's always at least 8-byte aligned for testing.
130     #[repr(align(8))]
131     struct AlignedBytes<const N: usize>([u8; N]);
132 
133     #[test]
aligned_subslice_already_aligned()134     fn aligned_subslice_already_aligned() {
135         let mut bytes = AlignedBytes([0u8; 16]);
136         let bytes = &mut bytes.0;
137 
138         // AlignedBytes is `align(8)`, so must be 1/2/4/8-aligned.
139         assert_eq!(aligned_subslice(bytes, 1).unwrap().as_ptr_range(), bytes.as_ptr_range());
140         assert_eq!(aligned_subslice(bytes, 2).unwrap().as_ptr_range(), bytes.as_ptr_range());
141         assert_eq!(aligned_subslice(bytes, 4).unwrap().as_ptr_range(), bytes.as_ptr_range());
142         assert_eq!(aligned_subslice(bytes, 8).unwrap().as_ptr_range(), bytes.as_ptr_range());
143     }
144 
145     #[test]
aligned_subslice_unaligned()146     fn aligned_subslice_unaligned() {
147         let mut bytes = AlignedBytes([0u8; 16]);
148         let bytes = &mut bytes.0;
149 
150         // AlignedBytes is 8-aligned, so offsetting by <8 should snap to the next 8-alignment.
151         assert_eq!(
152             aligned_subslice(&mut bytes[1..], 8).unwrap().as_ptr_range(),
153             bytes[8..].as_ptr_range()
154         );
155         assert_eq!(
156             aligned_subslice(&mut bytes[4..], 8).unwrap().as_ptr_range(),
157             bytes[8..].as_ptr_range()
158         );
159         assert_eq!(
160             aligned_subslice(&mut bytes[7..], 8).unwrap().as_ptr_range(),
161             bytes[8..].as_ptr_range()
162         );
163     }
164 
165     #[test]
aligned_subslice_empty_slice()166     fn aligned_subslice_empty_slice() {
167         let mut bytes = AlignedBytes([0u8; 16]);
168         let bytes = &mut bytes.0;
169 
170         // If the next alignment is just past the input, return the empty slice.
171         assert_eq!(
172             aligned_subslice(&mut bytes[9..], 8).unwrap().as_ptr_range(),
173             bytes[16..].as_ptr_range()
174         );
175     }
176 
177     #[test]
aligned_subslice_buffer_overflow()178     fn aligned_subslice_buffer_overflow() {
179         let mut bytes = AlignedBytes([0u8; 7]); // 7 bytes; can't reach the next 8-alignment.
180         let bytes = &mut bytes.0;
181 
182         assert_eq!(aligned_subslice(&mut bytes[1..], 8), Err(Error::BufferTooSmall(Some(7))));
183         assert_eq!(aligned_subslice(&mut bytes[6..], 8), Err(Error::BufferTooSmall(Some(2))));
184     }
185 
186     #[test]
aligned_subslice_alignment_overflow()187     fn aligned_subslice_alignment_overflow() {
188         let mut bytes = AlignedBytes([0u8; 16]);
189         let bytes = &mut bytes.0;
190 
191         assert!(matches!(aligned_subslice(bytes, SafeNum::MAX), Err(Error::ArithmeticOverflow(_))));
192     }
193 
194     #[test]
test_formatted_bytes()195     fn test_formatted_bytes() {
196         let mut bytes = [0u8; 4];
197         assert_eq!(snprintf!(bytes, "abcde"), "abcd");
198         assert_eq!(&bytes, b"abcd");
199     }
200 }
201