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