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}