Skip to main content

secure_types/
array.rs

1// No_std: we only need `Layout` for computing allocation sizes.
2// We call `alloc::alloc::dealloc` via fully-qualified path to avoid
3// shadowing the crate-level `alloc::<T>()` helper.
4#[cfg(not(feature = "use_os"))]
5use alloc::alloc::Layout;
6
7use super::{Error, SecureVec, alloc};
8use core::{marker::PhantomData, mem, ptr::NonNull};
9use zeroize::Zeroize;
10
11#[cfg(feature = "use_os")]
12use super::free;
13#[cfg(feature = "use_os")]
14use memsec::Prot;
15
16/// Unlocks the array's memory on construction and re-locks it on drop —
17/// including when the drop happens because the fn closure panicked.
18struct UnlockGuard<'a, T: Zeroize, const LENGTH: usize> {
19   array: &'a SecureArray<T, LENGTH>,
20}
21
22impl<'a, T: Zeroize, const LENGTH: usize> UnlockGuard<'a, T, LENGTH> {
23   fn new(array: &'a SecureArray<T, LENGTH>) -> Self {
24      let ok = array.unlock_memory();
25      debug_assert!(ok, "UnlockGuard::new: unlock_memory failed");
26      UnlockGuard { array }
27   }
28}
29
30impl<'a, T: Zeroize, const LENGTH: usize> Drop for UnlockGuard<'a, T, LENGTH> {
31   fn drop(&mut self) {
32      let ok = self.array.lock_memory();
33      debug_assert!(ok, "UnlockGuard::drop: lock_memory failed");
34   }
35}
36
37/// A fixed-size array allocated in a secure memory region.
38///
39/// ## Security Model
40///
41/// When compiled with the `use_os` feature (the default), it provides several layers of protection:
42/// - **Zeroization on Drop**: The memory is zeroized when the array is dropped.
43/// - **Memory Locking**: The underlying memory pages are locked using `mlock` & `madvise` for (Unix) or
44///   `VirtualLock` & `VirtualProtect` for (Windows) to prevent the OS from memory-dump/swap to disk or other processes accessing the memory.
45///
46/// In a `no_std` environment, it falls back to providing only the **zeroization-on-drop** guarantee.
47///
48/// # Security Note
49///
50/// We intentionally do **not** implement `Index` or `IndexMut`.
51/// `array[0]` is a compile error.
52///
53/// Always use `.unlock()` / `.unlock_mut()` (or the slice variants) to access data.
54///
55/// # Notes
56///
57/// If you return a new allocated `[T; LENGTH]` from one of the unlock methods you are responsible for zeroizing the memory.
58///
59/// # Example
60///
61/// ```
62/// use secure_types::{SecureArray, Zeroize};
63///
64/// let exposed_key: &mut [u8; 32] = &mut [1u8; 32];
65/// let secure_key: SecureArray<u8, 32> = SecureArray::from_slice_mut(exposed_key).unwrap();
66///
67/// secure_key.unlock(|unlocked_slice| {
68///     assert_eq!(unlocked_slice.len(), 32);
69///     assert_eq!(unlocked_slice[0], 1);
70/// });
71///
72/// // Not recommended but if you allocate a new [u8; LENGTH] make sure to zeroize it
73/// let mut exposed = secure_key.unlock(|unlocked_slice| {
74///     [unlocked_slice[0], unlocked_slice[1], unlocked_slice[2]]
75/// });
76///
77/// // Do what you need to to do with the new array
78/// // When you are done with it, zeroize it
79/// exposed.zeroize();
80/// ```
81pub struct SecureArray<T, const LENGTH: usize>
82where
83   T: Zeroize,
84{
85   ptr: NonNull<T>,
86   _marker: PhantomData<T>,
87}
88
89unsafe impl<T: Zeroize + Send, const LENGTH: usize> Send for SecureArray<T, LENGTH> {}
90unsafe impl<T: Zeroize + Send + Sync, const LENGTH: usize> Sync for SecureArray<T, LENGTH> {}
91
92impl<T, const LENGTH: usize> SecureArray<T, LENGTH>
93where
94   T: Zeroize,
95{
96   /// Creates an empty (but allocated) SecureArray.
97   ///
98   /// The memory is allocated but not initialized, and it's the caller's responsibility to fill it.
99   pub fn empty() -> Result<Self, Error> {
100      let size = LENGTH * mem::size_of::<T>();
101      if size == 0 {
102         // Cannot create a zero-sized secure array
103         return Err(Error::LengthCannotBeZero);
104      }
105
106      let ptr = unsafe { alloc::<T>(size)? };
107
108      let secure_array = SecureArray {
109         ptr,
110         _marker: PhantomData,
111      };
112
113      let _locked = secure_array.lock_memory();
114
115      #[cfg(feature = "use_os")]
116      if !_locked {
117         return Err(Error::LockFailed);
118      }
119
120      Ok(secure_array)
121   }
122
123   /// Creates a new SecureArray from a `&mut [T; LENGTH]`.
124   ///
125   /// The passed slice is zeroized afterwards
126   pub fn from_slice_mut(content: &mut [T; LENGTH]) -> Result<Self, Error>
127   where
128      T: Clone,
129   {
130      let secure_array = match Self::empty() {
131         Ok(secure_array) => secure_array,
132         Err(e) => {
133            content.zeroize();
134            return Err(e);
135         }
136      };
137
138      let unlocked = secure_array.unlock_memory();
139
140      if !unlocked {
141         content.zeroize();
142         return Err(Error::UnlockFailed);
143      }
144
145      unsafe {
146         let dst = secure_array.ptr.as_ptr();
147         for (i, item) in content.iter().enumerate() {
148            core::ptr::write(dst.add(i), item.clone());
149         }
150      }
151
152      content.zeroize();
153
154      let _locked = secure_array.lock_memory();
155
156      #[cfg(feature = "use_os")]
157      if !_locked {
158         return Err(Error::LockFailed);
159      }
160
161      Ok(secure_array)
162   }
163
164   /// Creates a new SecureArray from a `&[T; LENGTH]`.
165   ///
166   /// The array is not zeroized, you are responsible for zeroizing it
167   pub fn from_slice(content: &[T; LENGTH]) -> Result<Self, Error>
168   where
169      T: Clone,
170   {
171      let secure_array = Self::empty()?;
172
173      let unlocked = secure_array.unlock_memory();
174
175      if !unlocked {
176         return Err(Error::UnlockFailed);
177      }
178
179      unsafe {
180         let dst = secure_array.ptr.as_ptr();
181         for (i, item) in content.iter().enumerate() {
182            core::ptr::write(dst.add(i), item.clone());
183         }
184      }
185
186      let _locked = secure_array.lock_memory();
187
188      #[cfg(feature = "use_os")]
189      if !_locked {
190         return Err(Error::LockFailed);
191      }
192
193      Ok(secure_array)
194   }
195
196   pub fn len(&self) -> usize {
197      LENGTH
198   }
199
200   pub fn is_empty(&self) -> bool {
201      self.len() == 0
202   }
203
204   /// Returns the pointer to the locked memory region
205   ///
206   /// # DANGER
207   ///
208   /// This is a low-level API, which should be used only for
209   /// testing purposes. If you need to access the locked memory
210   /// region, use [`unlock`](Self::unlock) or [`unlock_mut`](Self::unlock_mut).
211   #[cfg(feature = "expose-ptr")]
212   #[deprecated(
213      since = "0.3.0",
214      note = "This method is intended only for testing/crash reproduction. Use unlock() or unlock_mut() instead."
215   )]
216   pub fn ptr(&self) -> NonNull<T> {
217      self.ptr
218   }
219
220   pub(crate) fn lock_memory(&self) -> bool {
221      #[cfg(feature = "use_os")]
222      {
223         #[cfg(windows)]
224         {
225            super::mprotect(self.ptr, Prot::NoAccess)
226         }
227         #[cfg(unix)]
228         {
229            super::mprotect(self.ptr, Prot::NoAccess)
230         }
231      }
232      #[cfg(not(feature = "use_os"))]
233      {
234         true // No-op: always "succeeds"
235      }
236   }
237
238   pub(crate) fn unlock_memory(&self) -> bool {
239      #[cfg(feature = "use_os")]
240      {
241         #[cfg(windows)]
242         {
243            super::mprotect(self.ptr, Prot::ReadWrite)
244         }
245         #[cfg(unix)]
246         {
247            super::mprotect(self.ptr, Prot::ReadWrite)
248         }
249      }
250
251      #[cfg(not(feature = "use_os"))]
252      {
253         true // No-op: always "succeeds"
254      }
255   }
256
257   /// Immutable access to the array's data as a `&[T]`
258   pub fn unlock<F, R>(&self, f: F) -> R
259   where
260      F: FnOnce(&[T]) -> R,
261   {
262      let _guard = UnlockGuard::new(self);
263      let slice = unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), LENGTH) };
264      let result = f(slice);
265      result
266   }
267
268   /// Mutable access to the array's data as a `&mut [T]`
269   pub fn unlock_mut<F, R>(&mut self, f: F) -> R
270   where
271      F: FnOnce(&mut [T]) -> R,
272   {
273      let _guard = UnlockGuard::new(self);
274      let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), LENGTH) };
275      let result = f(slice);
276      result
277   }
278
279   /// Securely erases the contents of the array by zeroizing the memory.
280   pub fn erase(&mut self) {
281      self.unlock_mut(|slice| {
282         for element in slice.iter_mut() {
283            element.zeroize();
284         }
285      });
286   }
287
288   /// Same as `SecureVec::init_from_clone`, for the fixed-size buffer.
289   /// `src.len()` must equal `LENGTH`.
290   pub(crate) fn init_from_clone(&mut self, src: &[T])
291   where
292      T: Clone,
293   {
294      debug_assert_eq!(src.len(), LENGTH);
295
296      let ok = self.unlock_memory();
297      debug_assert!(
298         ok,
299         "SecureArray::init_from_clone: unlock_memory failed"
300      );
301
302      unsafe {
303         let dst = self.ptr.as_ptr();
304         for (i, item) in src.iter().enumerate() {
305            core::ptr::write(dst.add(i), item.clone());
306         }
307      }
308      let ok = self.lock_memory();
309      debug_assert!(
310         ok,
311         "SecureArray::init_from_clone: lock_memory failed"
312      );
313   }
314}
315
316impl<T: Zeroize, const LENGTH: usize> Drop for SecureArray<T, LENGTH> {
317   fn drop(&mut self) {
318      let ok = self.unlock_memory();
319      debug_assert!(ok, "SecureArray::drop: unlock_memory failed");
320
321      let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), LENGTH) };
322      for element in slice.iter_mut() {
323         element.zeroize();
324      }
325
326      let size = LENGTH * mem::size_of::<T>();
327      if size == 0 {
328         return;
329      }
330
331      #[cfg(feature = "use_os")]
332      free(self.ptr);
333
334      #[cfg(not(feature = "use_os"))]
335      unsafe {
336         let layout = Layout::from_size_align_unchecked(size, mem::align_of::<T>());
337         alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
338      }
339   }
340}
341
342impl<T: Clone + Zeroize, const LENGTH: usize> Clone for SecureArray<T, LENGTH> {
343   fn clone(&self) -> Self {
344      let mut new_array = Self::empty().unwrap();
345      self.unlock(|src_slice| {
346         new_array.init_from_clone(src_slice);
347      });
348      new_array
349   }
350}
351
352impl<const LENGTH: usize> TryFrom<SecureVec<u8>> for SecureArray<u8, LENGTH> {
353   type Error = Error;
354
355   /// Tries to convert a `SecureVec<u8>` into a `SecureArray<u8, LENGTH>`.
356   ///
357   /// This operation will only succeed if `vec.len() == LENGTH`.
358   ///
359   /// The `SecureVec` is consumed.
360   fn try_from(vec: SecureVec<u8>) -> Result<Self, Self::Error> {
361      if vec.len() != LENGTH {
362         return Err(Error::LengthMismatch);
363      }
364
365      let mut new_array = Self::empty()?;
366
367      vec.unlock_slice(|vec_slice| {
368         new_array.init_from_clone(vec_slice);
369      });
370
371      Ok(new_array)
372   }
373}
374
375#[cfg(feature = "serde")]
376impl<const LENGTH: usize> serde::Serialize for SecureArray<u8, LENGTH> {
377   fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
378   where
379      S: serde::Serializer,
380   {
381      self.unlock(|slice| serializer.collect_seq(slice.iter()))
382   }
383}
384
385#[cfg(feature = "serde")]
386impl<'de, const LENGTH: usize> serde::Deserialize<'de> for SecureArray<u8, LENGTH> {
387   fn deserialize<D>(deserializer: D) -> Result<SecureArray<u8, LENGTH>, D::Error>
388   where
389      D: serde::Deserializer<'de>,
390   {
391      struct SecureArrayVisitor<const L: usize>;
392
393      impl<'de, const L: usize> serde::de::Visitor<'de> for SecureArrayVisitor<L> {
394         type Value = SecureArray<u8, L>;
395
396         fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
397            write!(formatter, "a byte array of length {}", L)
398         }
399
400         fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
401         where
402            A: serde::de::SeqAccess<'de>,
403         {
404            let mut data: SecureVec<u8> =
405               SecureVec::new_with_capacity(L).map_err(serde::de::Error::custom)?;
406            while let Some(byte) = seq.next_element()? {
407               data.push(byte);
408            }
409
410            // Check that the deserialized data has the exact length required.
411            if data.len() != L {
412               return Err(serde::de::Error::invalid_length(
413                  data.len(),
414                  &self,
415               ));
416            }
417
418            SecureArray::try_from(data).map_err(serde::de::Error::custom)
419         }
420      }
421
422      deserializer.deserialize_bytes(SecureArrayVisitor::<LENGTH>)
423   }
424}
425
426#[cfg(all(test, feature = "use_os"))]
427mod tests {
428   use super::*;
429   use std::process::{Command, Stdio};
430   use std::sync::{Arc, Mutex};
431
432   #[test]
433   fn test_creation() {
434      let exposed_mut = &mut [1, 2, 3];
435      let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed_mut).unwrap();
436      assert_eq!(array.len(), 3);
437
438      array.unlock(|slice| {
439         assert_eq!(slice, &[1, 2, 3]);
440      });
441
442      assert_eq!(exposed_mut, &[0u8; 3]);
443
444      let exposed = &[1, 2, 3];
445      let array: SecureArray<u8, 3> = SecureArray::from_slice(exposed).unwrap();
446      assert_eq!(array.len(), 3);
447
448      array.unlock(|slice| {
449         assert_eq!(slice, &[1, 2, 3]);
450      });
451
452      assert_eq!(exposed, &[1, 2, 3]);
453   }
454
455   #[test]
456   fn test_from_secure_vec() {
457      let vec: SecureVec<u8> = SecureVec::from_slice(&[1, 2, 3]).unwrap();
458      let array: SecureArray<u8, 3> = vec.try_into().unwrap();
459      assert_eq!(array.len(), 3);
460      array.unlock(|slice| {
461         assert_eq!(slice, &[1, 2, 3]);
462      });
463   }
464
465   #[test]
466   fn test_erase() {
467      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
468      let mut array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
469      array.erase();
470      array.unlock(|slice| {
471         assert_eq!(slice, &[0u8; 3]);
472      });
473   }
474
475   #[test]
476   #[should_panic]
477   fn test_length_cannot_be_zero() {
478      let secure_vec = SecureVec::new().unwrap();
479      let _secure_array: SecureArray<u8, 0> = SecureArray::try_from(secure_vec).unwrap();
480   }
481
482   #[test]
483   fn lock_unlock() {
484      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
485      let secure: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
486
487      let unlocked = secure.unlock_memory();
488      assert!(unlocked);
489
490      let locked = secure.lock_memory();
491      assert!(locked);
492   }
493
494   #[test]
495   fn test_clone() {
496      let mut array1: SecureArray<u8, 3> = SecureArray::empty().unwrap();
497      array1.unlock_mut(|slice| {
498         slice[0] = 1;
499         slice[1] = 2;
500         slice[2] = 3;
501      });
502
503      let array2 = array1.clone();
504
505      array2.unlock(|slice| {
506         assert_eq!(slice, &[1, 2, 3]);
507      });
508
509      array1.unlock(|slice| {
510         assert_eq!(slice, &[1, 2, 3]);
511      });
512   }
513
514   #[test]
515   fn test_thread_safety() {
516      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
517      let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
518      let arc_array = Arc::new(Mutex::new(array));
519      let mut handles = Vec::new();
520
521      for _ in 0..5u8 {
522         let array_clone = Arc::clone(&arc_array);
523         let handle = std::thread::spawn(move || {
524            let mut guard = array_clone.lock().unwrap();
525            guard.unlock_mut(|slice| {
526               slice[0] += 1;
527            });
528         });
529         handles.push(handle);
530      }
531
532      for handle in handles {
533         handle.join().unwrap();
534      }
535
536      let final_array = arc_array.lock().unwrap();
537      final_array.unlock(|slice| {
538         assert_eq!(slice[0], 6);
539         assert_eq!(slice[1], 2);
540         assert_eq!(slice[2], 3);
541      });
542   }
543
544   #[test]
545   fn test_index_should_fail_when_locked() {
546      let arg = "CRASH_TEST_ARRAY_LOCKED";
547
548      if std::env::args().any(|a| a == arg) {
549         let exposed: &mut [u8; 3] = &mut [1, 2, 3];
550         let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
551         // Deliberately dereference the locked pointer to test that
552         // the security model works as expected.
553         let _value = unsafe { core::hint::black_box(*array.ptr.as_ptr()) };
554
555         std::process::exit(1);
556      }
557
558      let child = Command::new(std::env::current_exe().unwrap())
559         .arg("array::tests::test_index_should_fail_when_locked")
560         .arg(arg)
561         .arg("--nocapture")
562         .stdout(Stdio::piped())
563         .stderr(Stdio::piped())
564         .spawn()
565         .expect("Failed to spawn child process");
566
567      let output = child.wait_with_output().expect("Failed to wait on child");
568      let status = output.status;
569
570      assert!(
571         !status.success(),
572         "Process exited successfully with code {:?}, but it should have crashed.",
573         status.code()
574      );
575
576      #[cfg(unix)]
577      {
578         use std::os::unix::process::ExitStatusExt;
579         let signal = status
580            .signal()
581            .expect("Process was not terminated by a signal on Unix.");
582         assert!(
583            signal == libc::SIGSEGV || signal == libc::SIGBUS,
584            "Process terminated with unexpected signal: {}",
585            signal
586         );
587         println!(
588            "Test passed: Process correctly terminated with signal {}.",
589            signal
590         );
591      }
592
593      #[cfg(windows)]
594      {
595         const STATUS_ACCESS_VIOLATION: i32 = 0xC0000005_u32 as i32;
596         assert_eq!(
597            status.code(),
598            Some(STATUS_ACCESS_VIOLATION),
599            "Process exited with unexpected code: {:x?}. Expected STATUS_ACCESS_VIOLATION.",
600            status.code()
601         );
602         eprintln!("Test passed: Process correctly terminated with STATUS_ACCESS_VIOLATION.");
603      }
604   }
605
606   #[test]
607   fn test_unlock_mut() {
608      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
609      let mut array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
610
611      array.unlock_mut(|slice| {
612         slice[1] = 100;
613      });
614
615      array.unlock(|slice| {
616         assert_eq!(slice, &[1, 100, 3]);
617      });
618   }
619
620   #[cfg(feature = "serde")]
621   #[test]
622   fn test_serde() {
623      let exposed: &mut [u8; 3] = &mut [1, 2, 3];
624      let array: SecureArray<u8, 3> = SecureArray::from_slice_mut(exposed).unwrap();
625      let json_string = serde_json::to_string(&array).expect("Serialization failed");
626      let json_bytes = serde_json::to_vec(&array).expect("Serialization failed");
627
628      let deserialized_string: SecureArray<u8, 3> =
629         serde_json::from_str(&json_string).expect("Deserialization failed");
630
631      let deserialized_bytes: SecureArray<u8, 3> =
632         serde_json::from_slice(&json_bytes).expect("Deserialization failed");
633
634      deserialized_string.unlock(|slice| {
635         assert_eq!(slice, &[1, 2, 3]);
636      });
637
638      deserialized_bytes.unlock(|slice| {
639         assert_eq!(slice, &[1, 2, 3]);
640      });
641   }
642
643   use std::fmt::Debug;
644   use zeroize::Zeroize;
645
646   // === Test helpers for variety of types (bigger than u8, complex) ===
647   #[derive(Clone, Debug, PartialEq)]
648   struct SmallStruct {
649      a: u8,
650      b: u16,
651   }
652   impl Zeroize for SmallStruct {
653      fn zeroize(&mut self) {
654         self.a.zeroize();
655         self.b.zeroize();
656      }
657   }
658
659   #[derive(Clone, Debug, PartialEq)]
660   struct LargeStruct {
661      data: [u64; 4],
662      flag: bool,
663   }
664   impl Zeroize for LargeStruct {
665      fn zeroize(&mut self) {
666         self.data.zeroize();
667         self.flag.zeroize();
668      }
669   }
670
671   #[derive(Clone, Debug, PartialEq)]
672   #[repr(align(64))]
673   struct AlignedStruct {
674      value: u64,
675   }
676   impl Zeroize for AlignedStruct {
677      fn zeroize(&mut self) {
678         self.value.zeroize();
679      }
680   }
681
682   #[derive(Clone, Debug, PartialEq)]
683   struct Person {
684      name: String,
685      age: u32,
686      notes: String,
687   }
688   impl Person {
689      fn new(name: impl Into<String>, age: u32, notes: impl Into<String>) -> Self {
690         Self {
691            name: name.into(),
692            age,
693            notes: notes.into(),
694         }
695      }
696   }
697   impl Zeroize for Person {
698      fn zeroize(&mut self) {
699         self.name.zeroize();
700         self.age.zeroize();
701         self.notes.zeroize();
702      }
703   }
704   fn create_test_person(id: usize) -> Person {
705      Person::new(
706         format!("Person{}", id),
707         (id % 100) as u32,
708         format!("Notes #{}", id),
709      )
710   }
711
712   fn test_array_generic_basics<T: Zeroize + Clone + PartialEq + Debug, const N: usize>(
713      initial: &[T; N],
714   ) {
715      let secure: SecureArray<T, N> = SecureArray::from_slice(initial).unwrap();
716      assert_eq!(secure.len(), N);
717      secure.unlock(|slice| {
718         assert_eq!(slice, initial);
719      });
720      let cloned = secure.clone();
721      cloned.unlock(|slice| {
722         assert_eq!(slice, initial);
723      });
724      let mut er = SecureArray::from_slice(initial).unwrap();
725      er.erase();
726      er.unlock(|slice| {
727         assert_eq!(slice.len(), N);
728      });
729   }
730
731   #[test]
732   fn test_array_u8_variety() {
733      let data: [u8; 3] = [1, 2, 3];
734      test_array_generic_basics(&data);
735   }
736
737   #[test]
738   fn test_array_u64() {
739      let data: [u64; 2] = [100u64, 200];
740      test_array_generic_basics(&data);
741   }
742
743   #[test]
744   fn test_array_byte_array() {
745      let data: [[u8; 16]; 2] = [[1u8; 16], [2u8; 16]];
746      test_array_generic_basics(&data);
747   }
748
749   #[test]
750   fn test_array_small_struct() {
751      let data: [SmallStruct; 2] = [SmallStruct { a: 1, b: 2 }, SmallStruct { a: 3, b: 4 }];
752      test_array_generic_basics(&data);
753   }
754
755   #[test]
756   fn test_array_large_struct() {
757      let data = [
758         LargeStruct {
759            data: [1, 2, 3, 4],
760            flag: true,
761         },
762         LargeStruct {
763            data: [5, 6, 7, 8],
764            flag: false,
765         },
766      ];
767      test_array_generic_basics(&data);
768   }
769
770   #[test]
771   fn test_array_person() {
772      let data = [create_test_person(42), create_test_person(43)];
773      test_array_generic_basics(&data);
774   }
775
776   #[test]
777   fn test_array_aligned() {
778      let data = [
779         AlignedStruct { value: 0xDEAD_BEEF },
780         AlignedStruct { value: 0xCAFE_BABE },
781      ];
782      test_array_generic_basics(&data);
783   }
784}