1use crate::sys;
11use core::{
12 ffi,
13 marker::PhantomData,
14 mem::{self, MaybeUninit},
15 ptr, slice,
16};
17use windows::Win32::Foundation::E_OUTOFMEMORY;
18use windows_core::{Error, HRESULT};
19
20#[derive(Debug)]
22pub enum MAPIAllocError {
23 SizeOverflow(usize),
27
28 OutOfBoundsAccess,
34
35 AllocationFailed(Error),
40}
41
42enum Buffer<T>
43where
44 T: Sized,
45{
46 Uninit(*mut MaybeUninit<T>),
47 Ready(*mut T),
48}
49
50enum Allocation<'a, T>
51where
52 T: Sized,
53{
54 Root {
55 buffer: Buffer<T>,
56 byte_count: usize,
57 },
58 More {
59 buffer: Buffer<T>,
60 byte_count: usize,
61 root: *mut ffi::c_void,
62 phantom: PhantomData<&'a T>,
63 },
64}
65
66impl<'a, T> Allocation<'a, T>
67where
68 T: Sized,
69{
70 fn new(count: usize) -> Result<Self, MAPIAllocError> {
71 let byte_count = count * mem::size_of::<T>();
72 Ok(Self::Root {
73 buffer: unsafe {
74 let mut alloc = ptr::null_mut();
75 HRESULT::from_win32(sys::MAPIAllocateBuffer(
76 u32::try_from(byte_count)
77 .map_err(|_| MAPIAllocError::SizeOverflow(byte_count))?,
78 &mut alloc,
79 ) as u32)
80 .ok()
81 .map_err(MAPIAllocError::AllocationFailed)?;
82 if alloc.is_null() {
83 return Err(MAPIAllocError::AllocationFailed(Error::from_hresult(
84 E_OUTOFMEMORY,
85 )));
86 }
87 Buffer::Uninit(alloc as *mut _)
88 },
89 byte_count,
90 })
91 }
92
93 fn chain<P>(&self, count: usize) -> Result<Allocation<'a, P>, MAPIAllocError>
94 where
95 P: Sized,
96 {
97 let root = match self {
98 Self::Root { buffer, .. } => match buffer {
99 Buffer::Uninit(alloc) => *alloc as *mut _,
100 Buffer::Ready(alloc) => *alloc as *mut _,
101 },
102 Self::More { root, .. } => *root,
103 };
104 let byte_count = count * mem::size_of::<T>();
105 Ok(Allocation::More {
106 buffer: unsafe {
107 let mut alloc = ptr::null_mut();
108 HRESULT::from_win32(sys::MAPIAllocateMore(
109 u32::try_from(byte_count)
110 .map_err(|_| MAPIAllocError::SizeOverflow(byte_count))?,
111 root,
112 &mut alloc,
113 ) as u32)
114 .ok()
115 .map_err(MAPIAllocError::AllocationFailed)?;
116 if alloc.is_null() {
117 return Err(MAPIAllocError::AllocationFailed(Error::from_hresult(
118 E_OUTOFMEMORY,
119 )));
120 }
121 Buffer::Uninit(alloc as *mut _)
122 },
123 byte_count,
124 root,
125 phantom: PhantomData,
126 })
127 }
128
129 fn into<P>(self) -> Result<Allocation<'a, P>, MAPIAllocError> {
130 let result = match self {
131 Self::Root {
132 buffer: Buffer::Ready(_),
133 ..
134 }
135 | Self::More {
136 buffer: Buffer::Ready(_),
137 ..
138 } => unreachable!(),
139 Self::Root {
140 buffer: Buffer::Uninit(alloc),
141 byte_count,
142 } if byte_count >= mem::size_of::<T>() => Ok(Allocation::Root {
143 buffer: Buffer::Uninit(alloc as *mut _),
144 byte_count,
145 }),
146 Self::More {
147 buffer: Buffer::Uninit(alloc),
148 byte_count,
149 root,
150 ..
151 } if byte_count >= mem::size_of::<T>() => Ok(Allocation::More {
152 buffer: Buffer::Uninit(alloc as *mut _),
153 byte_count,
154 root,
155 phantom: PhantomData,
156 }),
157 _ => Err(MAPIAllocError::OutOfBoundsAccess),
158 };
159 if result.is_ok() {
160 mem::forget(self);
161 }
162 result
163 }
164
165 fn iter(&self) -> AllocationIter<'a, T> {
166 match self {
167 Self::Root {
168 buffer: Buffer::Uninit(alloc),
169 byte_count,
170 } => AllocationIter {
171 alloc: *alloc,
172 byte_count: *byte_count,
173 element_size: mem::size_of::<T>(),
174 root: *alloc as *mut _,
175 phantom: PhantomData,
176 },
177 Self::More {
178 buffer: Buffer::Uninit(alloc),
179 byte_count,
180 root,
181 ..
182 } => AllocationIter {
183 alloc: *alloc,
184 byte_count: *byte_count,
185 element_size: mem::size_of::<T>(),
186 root: *root,
187 phantom: PhantomData,
188 },
189 _ => unreachable!(),
190 }
191 }
192
193 fn uninit(&mut self) -> Result<&mut MaybeUninit<T>, MAPIAllocError> {
194 match self {
195 Self::Root {
196 buffer: Buffer::Ready(_),
197 ..
198 }
199 | Self::More {
200 buffer: Buffer::Ready(_),
201 ..
202 } => unreachable!(),
203 Self::Root {
204 buffer: Buffer::Uninit(alloc),
205 byte_count,
206 } if mem::size_of::<T>() <= *byte_count => Ok(unsafe { &mut *(*alloc) }),
207 Self::More {
208 buffer: Buffer::Uninit(alloc),
209 byte_count,
210 ..
211 } if mem::size_of::<T>() <= *byte_count => Ok(unsafe { &mut *(*alloc) }),
212 _ => Err(MAPIAllocError::OutOfBoundsAccess),
213 }
214 }
215
216 unsafe fn assume_init(self) -> Self {
217 let result = match self {
218 Self::Root {
219 buffer: Buffer::Uninit(alloc),
220 byte_count,
221 } => Self::Root {
222 buffer: Buffer::Ready(alloc as *mut _),
223 byte_count,
224 },
225 Self::More {
226 buffer: Buffer::Uninit(alloc),
227 byte_count,
228 root,
229 ..
230 } => Self::More {
231 buffer: Buffer::Ready(alloc as *mut _),
232 byte_count,
233 root,
234 phantom: PhantomData,
235 },
236 _ => unreachable!(),
237 };
238 mem::forget(self);
239 result
240 }
241
242 fn as_mut(&mut self) -> Result<&mut T, MAPIAllocError> {
243 match self {
244 Self::Root {
245 buffer: Buffer::Uninit(_),
246 ..
247 }
248 | Self::More {
249 buffer: Buffer::Uninit(_),
250 ..
251 } => unreachable!(),
252 Self::Root {
253 buffer: Buffer::Ready(alloc),
254 byte_count,
255 } if mem::size_of::<T>() <= *byte_count => Ok(unsafe { &mut *(*alloc) }),
256 Self::More {
257 buffer: Buffer::Ready(alloc),
258 byte_count,
259 ..
260 } if mem::size_of::<T>() <= *byte_count => Ok(unsafe { &mut *(*alloc) }),
261 _ => Err(MAPIAllocError::OutOfBoundsAccess),
262 }
263 }
264}
265
266impl<T> Drop for Allocation<'_, T> {
267 fn drop(&mut self) {
268 if let Self::Root { buffer, .. } = self {
269 let alloc = match mem::replace(buffer, Buffer::Uninit(ptr::null_mut())) {
270 Buffer::Uninit(alloc) => alloc as *mut T,
271 Buffer::Ready(alloc) => alloc,
272 };
273 if !alloc.is_null() {
274 #[cfg(test)]
275 unreachable!();
276 #[cfg(not(test))]
277 unsafe {
278 sys::MAPIFreeBuffer(alloc as *mut _);
279 }
280 }
281 }
282 }
283}
284
285struct AllocationIter<'a, T>
286where
287 T: Sized,
288{
289 alloc: *mut MaybeUninit<T>,
290 byte_count: usize,
291 root: *mut ffi::c_void,
292 element_size: usize,
293 phantom: PhantomData<&'a T>,
294}
295
296impl<'a, T> Iterator for AllocationIter<'a, T>
297where
298 T: Sized,
299{
300 type Item = Allocation<'a, T>;
301
302 fn next(&mut self) -> Option<Self::Item> {
303 if self.byte_count < self.element_size {
304 return None;
305 }
306
307 let item = Allocation::More {
308 buffer: Buffer::Uninit(self.alloc),
309 byte_count: self.element_size,
310 root: self.root,
311 phantom: PhantomData,
312 };
313
314 self.byte_count -= self.element_size;
315 self.alloc = unsafe { self.alloc.add(1) };
316
317 Some(item)
318 }
319}
320
321pub struct MAPIUninit<'a, T>(Allocation<'a, T>)
324where
325 T: Sized;
326
327impl<'a, T> MAPIUninit<'a, T> {
328 pub fn new(count: usize) -> Result<Self, MAPIAllocError> {
336 Ok(Self(Allocation::new(count)?))
337 }
338
339 pub fn chain<P>(&self, count: usize) -> Result<MAPIUninit<'a, P>, MAPIAllocError> {
346 Ok(MAPIUninit::<'a, P>(self.0.chain::<P>(count)?))
347 }
348
349 pub fn into<P>(self) -> Result<MAPIUninit<'a, P>, MAPIAllocError> {
353 Ok(MAPIUninit::<'a, P>(self.0.into::<P>()?))
354 }
355
356 pub fn iter(&self) -> MAPIUninitIter<'a, T> {
358 MAPIUninitIter(self.0.iter())
359 }
360
361 pub fn uninit(&mut self) -> Result<&mut MaybeUninit<T>, MAPIAllocError> {
363 self.0.uninit()
364 }
365
366 pub unsafe fn assume_init(self) -> MAPIBuffer<'a, T> {
375 MAPIBuffer(unsafe { self.0.assume_init() })
376 }
377}
378
379pub struct MAPIUninitIter<'a, T>(AllocationIter<'a, T>)
381where
382 T: Sized;
383
384impl<'a, T> Iterator for MAPIUninitIter<'a, T>
385where
386 T: Sized,
387{
388 type Item = MAPIUninit<'a, T>;
389
390 fn next(&mut self) -> Option<Self::Item> {
391 self.0.next().map(MAPIUninit)
392 }
393}
394
395pub struct MAPIBuffer<'a, T>(Allocation<'a, T>)
397where
398 T: Sized;
399
400impl<'a, T> MAPIBuffer<'a, T> {
401 pub fn chain<P>(&self, count: usize) -> Result<MAPIUninit<'a, P>, MAPIAllocError> {
408 Ok(MAPIUninit::<'a, P>(self.0.chain::<P>(count)?))
409 }
410
411 pub fn as_mut(&mut self) -> Result<&mut T, MAPIAllocError> {
414 self.0.as_mut()
415 }
416}
417
418pub struct MAPIOutParam<T>(*mut T)
421where
422 T: Sized;
423
424impl<T> MAPIOutParam<T>
425where
426 T: Sized,
427{
428 pub fn as_mut_ptr(&mut self) -> *mut *mut T {
431 &mut self.0
432 }
433
434 pub unsafe fn as_mut(&mut self) -> Option<&mut T> {
441 unsafe { self.0.as_mut() }
442 }
443
444 pub unsafe fn as_mut_slice(&mut self, count: usize) -> Option<&mut [T]> {
451 if self.0.is_null() {
452 None
453 } else {
454 Some(unsafe { slice::from_raw_parts_mut(self.0, count) })
455 }
456 }
457}
458
459impl<T> Default for MAPIOutParam<T>
460where
461 T: Sized,
462{
463 fn default() -> Self {
464 Self(ptr::null_mut())
465 }
466}
467
468impl<T> Drop for MAPIOutParam<T>
469where
470 T: Sized,
471{
472 fn drop(&mut self) {
473 if !self.0.is_null() {
474 #[cfg(test)]
475 unreachable!();
476 #[cfg(not(test))]
477 unsafe {
478 sys::MAPIFreeBuffer(self.0 as *mut _);
479 }
480 }
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487 use crate::*;
488
489 use mem::ManuallyDrop;
490
491 SizedSPropTagArray! { TestTags[2] }
492
493 const TEST_TAGS: TestTags = TestTags {
494 cValues: 2,
495 aulPropTag: [sys::PR_INSTANCE_KEY, sys::PR_SUBJECT_W],
496 };
497
498 #[test]
499 fn buffer_uninit() {
500 let mut buffer: MaybeUninit<TestTags> = MaybeUninit::uninit();
501 let mut mapi_buffer = ManuallyDrop::new(MAPIUninit(Allocation::Root {
502 buffer: Buffer::Uninit(&mut buffer),
503 byte_count: mem::size_of_val(&buffer),
504 }));
505 assert!(mapi_buffer.uninit().is_ok());
506 }
507
508 #[test]
509 fn buffer_into() {
510 let mut buffer: [MaybeUninit<u8>; mem::size_of::<TestTags>()] =
511 [MaybeUninit::uninit(); CbNewSPropTagArray(2)];
512 let mut mapi_buffer = ManuallyDrop::new(MAPIUninit(Allocation::Root {
513 buffer: Buffer::Uninit(buffer.as_mut_ptr()),
514 byte_count: buffer.len(),
515 }));
516 assert!(mapi_buffer.uninit().is_ok());
517 let mut mapi_buffer = ManuallyDrop::new(
518 ManuallyDrop::into_inner(mapi_buffer)
519 .into::<TestTags>()
520 .expect("into failed"),
521 );
522 assert!(mapi_buffer.uninit().is_ok());
523 }
524
525 #[test]
526 fn buffer_iter() {
527 let mut buffer: [MaybeUninit<u32>; 2] = [MaybeUninit::uninit(); 2];
528 let mapi_buffer = ManuallyDrop::new(MAPIUninit(Allocation::Root {
529 buffer: Buffer::Uninit(buffer.as_mut_ptr()),
530 byte_count: buffer.len() * mem::size_of::<u32>(),
531 }));
532 let mut next = mapi_buffer.iter();
533 assert!(match next.next() {
534 Some(MAPIUninit(Allocation::More {
535 buffer: Buffer::Uninit(alloc),
536 byte_count,
537 root,
538 ..
539 })) => {
540 assert_eq!(alloc, buffer.as_mut_ptr() as *mut _);
541 assert_eq!(root, buffer.as_mut_ptr() as *mut _);
542 assert_eq!(byte_count, mem::size_of::<u32>());
543 true
544 }
545 _ => false,
546 });
547 assert!(match next.next() {
548 Some(MAPIUninit(Allocation::More {
549 buffer: Buffer::Uninit(alloc),
550 byte_count,
551 root,
552 ..
553 })) => {
554 assert_eq!(alloc, unsafe { buffer.as_mut_ptr().add(1) } as *mut _);
555 assert_eq!(root, buffer.as_mut_ptr() as *mut _);
556 assert_eq!(byte_count, mem::size_of::<u32>());
557 true
558 }
559 _ => false,
560 });
561 assert!(next.next().is_none());
562 }
563
564 #[test]
565 fn buffer_assume_init() {
566 let mut buffer = MaybeUninit::uninit();
567 let mapi_buffer = ManuallyDrop::new(MAPIUninit(Allocation::Root {
568 buffer: Buffer::Uninit(&mut buffer),
569 byte_count: mem::size_of_val(&buffer),
570 }));
571 buffer.write(TEST_TAGS);
572 let mut mapi_buffer =
573 ManuallyDrop::new(unsafe { ManuallyDrop::into_inner(mapi_buffer).assume_init() });
574 let test_tags = mapi_buffer.as_mut().expect("as_mut failed");
575 assert_eq!(TEST_TAGS.cValues, test_tags.cValues);
576 assert_eq!(TEST_TAGS.aulPropTag, test_tags.aulPropTag);
577 }
578}