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