1*5225e6b1SAndroid Build Coastguard Worker // Copyright (C) 2024 The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker
15*5225e6b1SAndroid Build Coastguard Worker //! Low-level utilities shared across multiple GBL libraries.
16*5225e6b1SAndroid Build Coastguard Worker
17*5225e6b1SAndroid Build Coastguard Worker #![cfg_attr(not(test), no_std)]
18*5225e6b1SAndroid Build Coastguard Worker
19*5225e6b1SAndroid Build Coastguard Worker use liberror::{Error, Result};
20*5225e6b1SAndroid Build Coastguard Worker use safemath::SafeNum;
21*5225e6b1SAndroid Build Coastguard Worker
22*5225e6b1SAndroid Build Coastguard Worker /// Returns the largest aligned subslice.
23*5225e6b1SAndroid Build Coastguard Worker ///
24*5225e6b1SAndroid Build Coastguard Worker /// This function drops as many bytes as needed from the front of the given slice to ensure the
25*5225e6b1SAndroid Build Coastguard Worker /// result is properly-aligned. It does not truncate bytes from the end, so the resulting size may
26*5225e6b1SAndroid Build Coastguard Worker /// not be a multiple of `alignment`.
27*5225e6b1SAndroid Build Coastguard Worker ///
28*5225e6b1SAndroid Build Coastguard Worker /// If the next `alignment` boundary would be directly following the last byte, this returns the
29*5225e6b1SAndroid Build Coastguard Worker /// 0-length slice at that alignment rather than an error, to match standard slicing behavior.
30*5225e6b1SAndroid Build Coastguard Worker ///
31*5225e6b1SAndroid Build Coastguard Worker /// # Arguments
32*5225e6b1SAndroid Build Coastguard Worker /// * `bytes`: the byte slice to align
33*5225e6b1SAndroid Build Coastguard Worker /// * `alignment`: the desired starting alignment
34*5225e6b1SAndroid Build Coastguard Worker ///
35*5225e6b1SAndroid Build Coastguard Worker /// # Returns
36*5225e6b1SAndroid Build Coastguard Worker /// * The subslice on success
37*5225e6b1SAndroid Build Coastguard Worker /// * [Error::ArithmeticOverflow] if `bytes` overflows when finding the next `alignment`
38*5225e6b1SAndroid Build Coastguard Worker /// * [Error::BufferTooSmall] if `bytes` is not large enough to reach the next `alignment`. The
39*5225e6b1SAndroid Build Coastguard Worker /// 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>,40*5225e6b1SAndroid Build Coastguard Worker pub fn aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]>
41*5225e6b1SAndroid Build Coastguard Worker where
42*5225e6b1SAndroid Build Coastguard Worker T: Copy + Into<SafeNum>,
43*5225e6b1SAndroid Build Coastguard Worker {
44*5225e6b1SAndroid Build Coastguard Worker let addr = bytes.as_ptr() as usize;
45*5225e6b1SAndroid Build Coastguard Worker let aligned_offset = (SafeNum::from(addr).round_up(alignment) - addr).try_into()?;
46*5225e6b1SAndroid Build Coastguard Worker Ok(bytes.get_mut(aligned_offset..).ok_or(Error::BufferTooSmall(Some(aligned_offset)))?)
47*5225e6b1SAndroid Build Coastguard Worker }
48*5225e6b1SAndroid Build Coastguard Worker
49*5225e6b1SAndroid Build Coastguard Worker /// A helper for getting the offset of the first byte with and aligned address.
50*5225e6b1SAndroid Build Coastguard Worker ///
51*5225e6b1SAndroid Build Coastguard Worker /// # Arguments
52*5225e6b1SAndroid Build Coastguard Worker /// * `bytes`: the byte slice
53*5225e6b1SAndroid Build Coastguard Worker /// * `alignment`: the desired starting alignment.
54*5225e6b1SAndroid Build Coastguard Worker ///
55*5225e6b1SAndroid Build Coastguard Worker /// # Returns
56*5225e6b1SAndroid Build Coastguard Worker ///
57*5225e6b1SAndroid Build Coastguard Worker /// * Returns Ok(offset) on success, Err() on integer overflow.
aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize> where T: Copy + Into<SafeNum>,58*5225e6b1SAndroid Build Coastguard Worker pub fn aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize>
59*5225e6b1SAndroid Build Coastguard Worker where
60*5225e6b1SAndroid Build Coastguard Worker T: Copy + Into<SafeNum>,
61*5225e6b1SAndroid Build Coastguard Worker {
62*5225e6b1SAndroid Build Coastguard Worker let addr = SafeNum::from(buffer.as_ptr() as usize);
63*5225e6b1SAndroid Build Coastguard Worker (addr.round_up(alignment) - addr).try_into().map_err(From::from)
64*5225e6b1SAndroid Build Coastguard Worker }
65*5225e6b1SAndroid Build Coastguard Worker
66*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
67*5225e6b1SAndroid Build Coastguard Worker mod test {
68*5225e6b1SAndroid Build Coastguard Worker use super::*;
69*5225e6b1SAndroid Build Coastguard Worker
70*5225e6b1SAndroid Build Coastguard Worker // A byte array that's always at least 8-byte aligned for testing.
71*5225e6b1SAndroid Build Coastguard Worker #[repr(align(8))]
72*5225e6b1SAndroid Build Coastguard Worker struct AlignedBytes<const N: usize>([u8; N]);
73*5225e6b1SAndroid Build Coastguard Worker
74*5225e6b1SAndroid Build Coastguard Worker #[test]
aligned_subslice_already_aligned()75*5225e6b1SAndroid Build Coastguard Worker fn aligned_subslice_already_aligned() {
76*5225e6b1SAndroid Build Coastguard Worker let mut bytes = AlignedBytes([0u8; 16]);
77*5225e6b1SAndroid Build Coastguard Worker let bytes = &mut bytes.0;
78*5225e6b1SAndroid Build Coastguard Worker
79*5225e6b1SAndroid Build Coastguard Worker // AlignedBytes is `align(8)`, so must be 1/2/4/8-aligned.
80*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(bytes, 1).unwrap().as_ptr_range(), bytes.as_ptr_range());
81*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(bytes, 2).unwrap().as_ptr_range(), bytes.as_ptr_range());
82*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(bytes, 4).unwrap().as_ptr_range(), bytes.as_ptr_range());
83*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(bytes, 8).unwrap().as_ptr_range(), bytes.as_ptr_range());
84*5225e6b1SAndroid Build Coastguard Worker }
85*5225e6b1SAndroid Build Coastguard Worker
86*5225e6b1SAndroid Build Coastguard Worker #[test]
aligned_subslice_unaligned()87*5225e6b1SAndroid Build Coastguard Worker fn aligned_subslice_unaligned() {
88*5225e6b1SAndroid Build Coastguard Worker let mut bytes = AlignedBytes([0u8; 16]);
89*5225e6b1SAndroid Build Coastguard Worker let bytes = &mut bytes.0;
90*5225e6b1SAndroid Build Coastguard Worker
91*5225e6b1SAndroid Build Coastguard Worker // AlignedBytes is 8-aligned, so offsetting by <8 should snap to the next 8-alignment.
92*5225e6b1SAndroid Build Coastguard Worker assert_eq!(
93*5225e6b1SAndroid Build Coastguard Worker aligned_subslice(&mut bytes[1..], 8).unwrap().as_ptr_range(),
94*5225e6b1SAndroid Build Coastguard Worker bytes[8..].as_ptr_range()
95*5225e6b1SAndroid Build Coastguard Worker );
96*5225e6b1SAndroid Build Coastguard Worker assert_eq!(
97*5225e6b1SAndroid Build Coastguard Worker aligned_subslice(&mut bytes[4..], 8).unwrap().as_ptr_range(),
98*5225e6b1SAndroid Build Coastguard Worker bytes[8..].as_ptr_range()
99*5225e6b1SAndroid Build Coastguard Worker );
100*5225e6b1SAndroid Build Coastguard Worker assert_eq!(
101*5225e6b1SAndroid Build Coastguard Worker aligned_subslice(&mut bytes[7..], 8).unwrap().as_ptr_range(),
102*5225e6b1SAndroid Build Coastguard Worker bytes[8..].as_ptr_range()
103*5225e6b1SAndroid Build Coastguard Worker );
104*5225e6b1SAndroid Build Coastguard Worker }
105*5225e6b1SAndroid Build Coastguard Worker
106*5225e6b1SAndroid Build Coastguard Worker #[test]
aligned_subslice_empty_slice()107*5225e6b1SAndroid Build Coastguard Worker fn aligned_subslice_empty_slice() {
108*5225e6b1SAndroid Build Coastguard Worker let mut bytes = AlignedBytes([0u8; 16]);
109*5225e6b1SAndroid Build Coastguard Worker let bytes = &mut bytes.0;
110*5225e6b1SAndroid Build Coastguard Worker
111*5225e6b1SAndroid Build Coastguard Worker // If the next alignment is just past the input, return the empty slice.
112*5225e6b1SAndroid Build Coastguard Worker assert_eq!(
113*5225e6b1SAndroid Build Coastguard Worker aligned_subslice(&mut bytes[9..], 8).unwrap().as_ptr_range(),
114*5225e6b1SAndroid Build Coastguard Worker bytes[16..].as_ptr_range()
115*5225e6b1SAndroid Build Coastguard Worker );
116*5225e6b1SAndroid Build Coastguard Worker }
117*5225e6b1SAndroid Build Coastguard Worker
118*5225e6b1SAndroid Build Coastguard Worker #[test]
aligned_subslice_buffer_overflow()119*5225e6b1SAndroid Build Coastguard Worker fn aligned_subslice_buffer_overflow() {
120*5225e6b1SAndroid Build Coastguard Worker let mut bytes = AlignedBytes([0u8; 7]); // 7 bytes; can't reach the next 8-alignment.
121*5225e6b1SAndroid Build Coastguard Worker let bytes = &mut bytes.0;
122*5225e6b1SAndroid Build Coastguard Worker
123*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(&mut bytes[1..], 8), Err(Error::BufferTooSmall(Some(7))));
124*5225e6b1SAndroid Build Coastguard Worker assert_eq!(aligned_subslice(&mut bytes[6..], 8), Err(Error::BufferTooSmall(Some(2))));
125*5225e6b1SAndroid Build Coastguard Worker }
126*5225e6b1SAndroid Build Coastguard Worker
127*5225e6b1SAndroid Build Coastguard Worker #[test]
aligned_subslice_alignment_overflow()128*5225e6b1SAndroid Build Coastguard Worker fn aligned_subslice_alignment_overflow() {
129*5225e6b1SAndroid Build Coastguard Worker let mut bytes = AlignedBytes([0u8; 16]);
130*5225e6b1SAndroid Build Coastguard Worker let bytes = &mut bytes.0;
131*5225e6b1SAndroid Build Coastguard Worker
132*5225e6b1SAndroid Build Coastguard Worker assert!(matches!(aligned_subslice(bytes, SafeNum::MAX), Err(Error::ArithmeticOverflow(_))));
133*5225e6b1SAndroid Build Coastguard Worker }
134*5225e6b1SAndroid Build Coastguard Worker }
135