1use super::{
4 AllocError,
5 granularity::{Granularity, ResourceClass},
6 host::HostMemory,
7 linear::Linear,
8 tlsf::Tlsf,
9 virtual_block::{PlacementStrategy, VirtualAllocationInfo, VirtualBlockStats},
10};
11
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub enum AllocationAlgorithm {
21 #[default]
22 Tlsf,
23 Linear,
24}
25
26#[derive(Clone, Copy)]
27pub(super) enum Placement {
28 Tlsf(usize, u64),
29 Linear { offset: u64, serial: u64 },
30}
31
32#[expect(
35 clippy::large_enum_variant,
36 reason = "inline metadata preserves fallible block acquisition"
37)]
38pub(super) enum Metadata<'callbacks> {
39 Tlsf(Tlsf<'callbacks>),
40 Linear(Linear<'callbacks>),
41}
42
43impl<'callbacks> Metadata<'callbacks> {
44 pub fn free_bytes(&self) -> u64 {
46 match self {
47 Self::Tlsf(metadata) => metadata.free_bytes(),
48 Self::Linear(metadata) => metadata.free_bytes(),
49 }
50 }
51
52 pub fn is_empty(&self) -> bool {
53 match self {
54 Self::Tlsf(metadata) => metadata.is_empty(),
55 Self::Linear(metadata) => metadata.is_empty(),
56 }
57 }
58
59 pub fn new_native(
60 size: u64,
61 algorithm: AllocationAlgorithm,
62 allow_ring: bool,
63 granularity: Granularity,
64 host: HostMemory<'callbacks>,
65 ) -> Result<Self, AllocError> {
66 let mut metadata = Self::new_with_host(size, algorithm, allow_ring, host)?;
67 match &mut metadata {
68 Self::Tlsf(metadata) => metadata.granularity = granularity,
69 Self::Linear(metadata) => metadata.granularity = granularity,
70 }
71 Ok(metadata)
72 }
73
74 pub fn new(
75 size: u64,
76 algorithm: AllocationAlgorithm,
77 allow_ring: bool,
78 ) -> Result<Self, AllocError> {
79 match algorithm {
80 AllocationAlgorithm::Tlsf => Tlsf::new(size).map(Self::Tlsf),
81 AllocationAlgorithm::Linear => Linear::new(size, allow_ring).map(Self::Linear),
82 }
83 }
84
85 pub fn host_memory(&self) -> HostMemory<'callbacks> {
86 match self {
87 Self::Tlsf(metadata) => metadata.host_memory(),
88 Self::Linear(metadata) => metadata.host_memory(),
89 }
90 }
91
92 pub fn new_with_host(
93 size: u64,
94 algorithm: AllocationAlgorithm,
95 allow_ring: bool,
96 host: HostMemory<'callbacks>,
97 ) -> Result<Self, AllocError> {
98 match algorithm {
99 AllocationAlgorithm::Tlsf => Tlsf::new_with_host(size, host).map(Self::Tlsf),
100 AllocationAlgorithm::Linear => {
101 Linear::new_with_host(size, allow_ring, host).map(Self::Linear)
102 }
103 }
104 }
105
106 #[inline]
107 pub fn allocate(
108 &mut self,
109 size: u64,
110 alignment: u64,
111 strategy: PlacementStrategy,
112 upper: bool,
113 ) -> Result<Placement, AllocError> {
114 match self {
115 Self::Tlsf(metadata) => {
116 if upper {
117 return Err(AllocError::UnsupportedCapability(
118 "upper address requires linear placement",
119 ));
120 }
121 metadata
122 .allocate(size, alignment, strategy)
123 .map(|(index, serial)| Placement::Tlsf(index, serial))
124 }
125 Self::Linear(metadata) => metadata.allocate(size, alignment, upper),
126 }
127 }
128
129 #[inline]
132 pub fn allocate_resource(
133 &mut self,
134 size: u64,
135 alignment: u64,
136 strategy: PlacementStrategy,
137 upper: bool,
138 class: ResourceClass,
139 ) -> Result<(Placement, u64), AllocError> {
140 match self {
141 Self::Tlsf(metadata) => {
142 if upper {
143 return Err(AllocError::UnsupportedCapability(
144 "upper address requires linear placement",
145 ));
146 }
147 metadata
148 .allocate_resource(size, alignment, strategy, class)
149 .map(|(index, serial)| (Placement::Tlsf(index, serial), metadata.offset(index)))
150 }
151 Self::Linear(metadata) => metadata.allocate_resource(size, alignment, upper, class),
152 }
153 }
154
155 pub fn info(&self, placement: Placement) -> Result<VirtualAllocationInfo, AllocError> {
156 match (self, placement) {
157 (Self::Tlsf(metadata), Placement::Tlsf(index, serial)) => metadata.info(index, serial),
158 (Self::Linear(metadata), Placement::Linear { offset, serial }) => {
159 metadata.info(offset, serial)
160 }
161 _ => Err(AllocError::UnknownAllocation),
162 }
163 }
164
165 #[inline]
166 pub fn free(&mut self, placement: Placement) -> Result<(), AllocError> {
167 match (self, placement) {
168 (Self::Tlsf(metadata), Placement::Tlsf(index, serial)) => metadata.free(index, serial),
169 (Self::Linear(metadata), Placement::Linear { offset, serial }) => {
170 metadata.free(offset, serial)
171 }
172 _ => Err(AllocError::UnknownAllocation),
173 }
174 }
175
176 pub fn clear(&mut self) {
177 match self {
178 Self::Tlsf(metadata) => metadata.clear(),
179 Self::Linear(metadata) => metadata.clear(),
180 }
181 }
182
183 pub fn set_user_data(&mut self, placement: Placement, data: usize) -> Result<(), AllocError> {
184 match (self, placement) {
185 (Self::Tlsf(metadata), Placement::Tlsf(index, serial)) => {
186 metadata.set_user_data(index, serial, data)
187 }
188 (Self::Linear(metadata), Placement::Linear { offset, serial }) => {
189 metadata.set_user_data(offset, serial, data)
190 }
191 _ => Err(AllocError::UnknownAllocation),
192 }
193 }
194
195 pub fn for_each_allocation(&self, visit: impl FnMut(u64, u64, usize)) {
196 match self {
197 Self::Tlsf(metadata) => metadata.for_each_allocation(visit),
198 Self::Linear(metadata) => metadata.for_each_allocation(visit),
199 }
200 }
201
202 pub fn for_each_free(&self, visit: impl FnMut(u64, u64)) {
203 match self {
204 Self::Tlsf(metadata) => metadata.for_each_free(visit),
205 Self::Linear(metadata) => metadata.for_each_free(visit),
206 }
207 }
208
209 pub fn statistics(&self) -> VirtualBlockStats {
210 match self {
211 Self::Tlsf(metadata) => metadata.statistics(),
212 Self::Linear(metadata) => metadata.statistics(),
213 }
214 }
215}
216
217#[cfg(test)]
218#[path = "../../../test/rs/mem/test_granularity.rs"]
219mod tests;