Skip to main content

acpi_lite/
binary_reader.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use crate::structures::VariableSized;
8
9// Light-weight class for decoding structs in a safe manner.
10//
11// Each operation returns a pointer to a valid struct or None indicating
12// that the read would return an invalid structure, such as a structure out of
13// bounds of the original input buffer.
14//
15// BinaryReader supports a common requirement in ACPI of variable-length
16// structures, where a struct consists of a header followed by a payload.
17// To support such structures, we require a |size| method returning the
18// size of the header + payload.
19//
20// Successful reads consume bytes from the buffer, while failed reads don't
21// modify internal state.
22pub struct BinaryReader<'a> {
23    buffer: &'a [u8],
24}
25
26impl<'a> BinaryReader<'a> {
27    // Construct a BinaryReader from the given slice.
28    pub fn new(buffer: &'a [u8]) -> Self {
29        Self { buffer }
30    }
31
32    /// # Safety
33    /// The caller must ensure that `ptr` points to a valid block of memory
34    /// of at least `size` bytes, and that the memory remains valid for the
35    /// lifetime `'a`.
36    pub unsafe fn from_ptr(ptr: *const u8, size: usize) -> Self {
37        // SAFETY: The caller guarantees `ptr` and `size` are valid.
38        Self { buffer: unsafe { core::slice::from_raw_parts(ptr, size) } }
39    }
40
41    // Construct a BinaryReader from a valid structure with a size() method.
42    pub fn from_variable_sized<T>(header: &'a T) -> Self
43    where
44        T: VariableSized,
45    {
46        let size = header.size();
47        let ptr = header as *const T as *const u8;
48        // SAFETY: `header` is a valid reference, and `T::size()` is assumed
49        // to return the size of the memory backing it.
50        unsafe { Self::from_ptr(ptr, size) }
51    }
52
53    // Construct a BinaryReader from a class with a size() method, skipping the header T.
54    pub fn from_payload_of_struct<T>(header: &'a T) -> Self
55    where
56        T: VariableSized,
57    {
58        let size = header.size();
59        let struct_size = core::mem::size_of::<T>();
60        if size < struct_size {
61            return Self { buffer: &[] };
62        }
63        let ptr = header as *const T as *const u8;
64        // SAFETY: `header` is a valid reference, and `T::size()` is assumed
65        // to return the size of the memory backing it. We offset by `struct_size`
66        // which is safe because `size >= struct_size`.
67        unsafe { Self::from_ptr(ptr.add(struct_size), size - struct_size) }
68    }
69
70    // Read a fixed-length structure.
71    pub fn read_fixed_length<T>(&mut self) -> Option<&'a T>
72    where
73        T: zerocopy::FromBytes + zerocopy::Unaligned + zerocopy::Immutable + zerocopy::KnownLayout,
74    {
75        let size = core::mem::size_of::<T>();
76        if self.buffer.len() < size {
77            return None;
78        }
79        let (prefix, suffix) = self.buffer.split_at(size);
80        let r = zerocopy::Ref::<_, T>::from_bytes(prefix).ok()?;
81        self.buffer = suffix;
82        Some(zerocopy::Ref::into_ref(r))
83    }
84
85    // Read a variable length structure, where the size is determined by T::size().
86    pub fn read<T>(&mut self) -> Option<&'a T>
87    where
88        T: VariableSized
89            + zerocopy::FromBytes
90            + zerocopy::Unaligned
91            + zerocopy::Immutable
92            + zerocopy::KnownLayout,
93    {
94        let size = core::mem::size_of::<T>();
95        if self.buffer.len() < size {
96            return None;
97        }
98        let prefix = &self.buffer[..size];
99        let r = zerocopy::Ref::<_, T>::from_bytes(prefix).ok()?;
100        let val = zerocopy::Ref::into_ref(r);
101        let desired_size = val.size();
102        if desired_size < size || desired_size > self.buffer.len() {
103            return None;
104        }
105        self.buffer = &self.buffer[desired_size..];
106        Some(val)
107    }
108
109    // Discard the given number of bytes.
110    //
111    // Return true if the bytes could be discarded, or false if there are insufficient bytes.
112    pub fn skip_bytes(&mut self, bytes: usize) -> bool {
113        if self.buffer.len() < bytes {
114            return false;
115        }
116        self.buffer = &self.buffer[bytes..];
117        true
118    }
119
120    // Return true if all the bytes of the reader have been consumed.
121    pub fn is_empty(&self) -> bool {
122        self.buffer.is_empty()
123    }
124}
125
126// Convert a pointer to type |Src| to a pointer of type |Dest|, ensuring that the size of |Src|
127// is valid.
128//
129// We require that the type |Dest| has a field |header| at offset 0 of type |Src|.
130pub trait DowncastFrom<Src> {
131    /// # Safety
132    ///
133    /// The caller must ensure that `src` actually points to a structure of type `Self`.
134    unsafe fn downcast_from(src: &Src) -> Option<&Self>;
135}
136
137#[derive(Copy, Clone, zerocopy::FromBytes, zerocopy::Immutable, zerocopy::KnownLayout)]
138#[repr(C, packed)]
139// A "packed" type wraps a plain type, but instructs the compiler to treat it as unaligned data.
140pub struct Unaligned<T>(pub T);
141
142unsafe impl<T> zerocopy::Unaligned for Unaligned<T> {
143    fn only_derive_is_allowed_to_implement_this_trait() {}
144}
145
146macro_rules! impl_downcast_from {
147    ($Src:ty => $($Target:ty),+ $(,)?) => {
148        $(
149            impl $crate::binary_reader::DowncastFrom<$Src> for $Target {
150                unsafe fn downcast_from(src: &$Src) -> Option<&Self> {
151                    if $crate::structures::VariableSized::size(src) < core::mem::size_of::<Self>() {
152                        return None;
153                    }
154                    // SAFETY: The caller must ensure that `src` points to a block of memory
155                    // of at least `src.size()` bytes.
156                    let bytes = unsafe {
157                        core::slice::from_raw_parts(
158                            src as *const $Src as *const u8,
159                            $crate::structures::VariableSized::size(src),
160                        )
161                    };
162                    let bytes = &bytes[..core::mem::size_of::<Self>()];
163                    let r = zerocopy::Ref::<_, Self>::from_bytes(bytes).ok()?;
164                    Some(zerocopy::Ref::into_ref(r))
165                }
166            }
167        )+
168    };
169}