1#![cfg_attr(not(test), no_std)]
88
89mod access;
90pub use access::{BitMut, DWordMut, LWordMut, WordMut};
91
92#[cfg(feature = "allow_unaligned_tags")]
93#[doc(hidden)]
94#[macro_export]
95macro_rules! alignment_assert {
96 ($align:literal, $addr:literal) => {};
97}
98
99#[cfg(not(feature = "allow_unaligned_tags"))]
100#[doc(hidden)]
101#[macro_export]
102macro_rules! alignment_assert {
103 (2, $addr:literal) => {
104 assert!($addr % 2 == 0, "Word address must be divisible by 2");
105 };
106 (4, $addr:literal) => {
107 assert!($addr % 4 == 0, "Double word address must be divisible by 4");
108 };
109 (8, $addr:literal) => {
110 assert!($addr % 8 == 0, "Long word address must be divisible by 8");
111 };
112}
113
114#[macro_export]
141macro_rules! tag {
142 ($buf:expr, X, $addr1:expr, $addr2:expr) => {{
143 let buffer: &[u8] = $buf;
144 buffer[$addr1] & (1 << $addr2) != 0
145 }};
146 ($buf:expr, B, $addr:expr) => {{
147 let buffer: &[u8] = $buf;
148 buffer[$addr]
149 }};
150 ($buf:expr, W, $addr:expr) => {{
151 let buffer: &[u8] = $buf;
152 $crate::alignment_assert!(2, $addr);
153 u16::from_be_bytes(buffer[$addr..$addr + 2].try_into().unwrap())
154 }};
155 ($buf:expr, D, $addr:expr) => {{
156 let buffer: &[u8] = $buf;
157 $crate::alignment_assert!(4, $addr);
158 u32::from_be_bytes(buffer[$addr..$addr + 4].try_into().unwrap())
159 }};
160 ($buf:expr, L, $addr:expr) => {{
161 let buffer: &[u8] = $buf;
162 $crate::alignment_assert!(8, $addr);
163 u64::from_be_bytes(buffer[$addr..$addr + 8].try_into().unwrap())
164 }};
165 ($buf:expr, $addr1:expr, $addr2:expr) => {{
166 let buffer: &[u8] = $buf;
167 buffer[$addr1] & (1 << $addr2) != 0
168 }};
169}
170
171#[macro_export]
198macro_rules! tag_mut {
199 ($buf:expr, X, $addr1:expr, $addr2:expr) => {{
200 let buffer: &mut [u8] = $buf;
201 $crate::BitMut::new(&mut buffer[$addr1], $addr2)
202 }};
203 ($buf:expr, B, $addr:expr) => {{
204 let buffer: &mut [u8] = $buf;
205 &mut buffer[$addr]
206 }};
207 ($buf:expr, W, $addr:expr) => {{
208 let buffer: &mut [u8] = $buf;
209 $crate::alignment_assert!(2, $addr);
210 $crate::WordMut::new((&mut buffer[$addr..$addr + 2]).try_into().unwrap())
211 }};
212 ($buf:expr, D, $addr:expr) => {{
213 let buffer: &mut [u8] = $buf;
214 $crate::alignment_assert!(4, $addr);
215 $crate::DWordMut::new((&mut buffer[$addr..$addr + 4]).try_into().unwrap())
216 }};
217 ($buf:expr, L, $addr:expr) => {{
218 let buffer: &mut [u8] = $buf;
219 $crate::alignment_assert!(8, $addr);
220 $crate::LWordMut::new((&mut buffer[$addr..$addr + 8]).try_into().unwrap())
221 }};
222 ($buf:expr, $addr1:expr, $addr2:expr) => {{
223 let buffer: &mut [u8] = $buf;
224 $crate::BitMut::new(&mut buffer[$addr1], $addr2)
225 }};
226}
227
228#[doc(hidden)]
229#[macro_export]
230macro_rules! tag_method {
231 ($vis:vis, $name:ident, mut, X, $addr1:literal, $addr2:literal) => {
232 #[inline(always)]
233 $vis fn $name(&mut self) -> $crate::BitMut<'_> {
234 $crate::BitMut::new(&mut self.buf[$addr1], $addr2)
235 }
236 };
237 ($vis:vis, $name:ident, mut, B, $addr:literal) => {
238 #[inline(always)]
239 $vis fn $name(&mut self) -> &mut u8 {
240 &mut self.buf[$addr]
241 }
242 };
243 ($vis:vis, $name:ident, mut, W, $addr:literal) => {
244 #[inline(always)]
245 $vis fn $name(&mut self) -> $crate::WordMut<'_> {
246 $crate::alignment_assert!(2, $addr);
247 $crate::WordMut::new((&mut self.buf[$addr..$addr + 2]).try_into().unwrap())
248 }
249 };
250 ($vis:vis, $name:ident, mut, D, $addr:literal) => {
251 #[inline(always)]
252 $vis fn $name(&mut self) -> $crate::DWordMut<'_> {
253 $crate::alignment_assert!(4, $addr);
254 $crate::DWordMut::new((&mut self.buf[$addr..$addr + 4]).try_into().unwrap())
255 }
256 };
257 ($vis:vis, $name:ident, mut, L, $addr:literal) => {
258 #[inline(always)]
259 $vis fn $name(&mut self) -> $crate::LWordMut<'_> {
260 $crate::alignment_assert!(8, $addr);
261 $crate::LWordMut::new((&mut self.buf[$addr..$addr + 8]).try_into().unwrap())
262 }
263 };
264 ($vis:vis, $name:ident, mut, $addr1:literal, $addr2:literal) => {
265 #[inline(always)]
266 $vis fn $name(&mut self) -> $crate::BitMut<'_> {
267 $crate::BitMut::new(&mut self.buf[$addr1], $addr2)
268 }
269 };
270 ($vis:vis, $name:ident, const, X, $addr1:literal, $addr2:literal) => {
271 #[inline(always)]
272 $vis fn $name(&self) -> bool {
273 self.buf[$addr1] & (1 << $addr2) != 0
274 }
275 };
276 ($vis:vis, $name:ident, const, B, $addr:literal) => {
277 #[inline(always)]
278 $vis fn $name(&self) -> u8 {
279 self.buf[$addr]
280 }
281 };
282 ($vis:vis, $name:ident, const, W, $addr:literal) => {
283 #[inline(always)]
284 $vis fn $name(&self) -> u16 {
285 $crate::alignment_assert!(2, $addr);
286 u16::from_be_bytes(self.buf[$addr..$addr + 2].try_into().unwrap())
287 }
288 };
289 ($vis:vis, $name:ident, const, D, $addr:literal) => {
290 #[inline(always)]
291 $vis fn $name(&self) -> u32 {
292 $crate::alignment_assert!(4, $addr);
293 u32::from_be_bytes(self.buf[$addr..$addr + 4].try_into().unwrap())
294 }
295 };
296 ($vis:vis, $name:ident, const, L, $addr:literal) => {
297 #[inline(always)]
298 $vis fn $name(&self) -> u64 {
299 $crate::alignment_assert!(8, $addr);
300 u64::from_be_bytes(self.buf[$addr..$addr + 8].try_into().unwrap())
301 }
302 };
303 ($vis:vis, $name:ident, const, $addr1:literal, $addr2:literal) => {
304 #[inline(always)]
305 $vis fn $name(&self) -> bool {
306 self.buf[$addr1] & (1 << $addr2) != 0
307 }
308 };
309}
310
311#[macro_export]
382macro_rules! process_image {
383 (
384 $( #[$meta:meta] )*
385 $vis:vis struct $ProcessImage:ident, mut $ProcessImageMut:ident: $SIZE:literal {
386 $(
387 $( #[$field_meta:meta] )*
388 $field_vis:vis $field_name:ident: ($($tag:tt)+)
389 ),*
390 $(,)?
391 }
392 ) => {
393 $( #[$meta] )*
394 $vis struct $ProcessImage<'a> {
395 buf: &'a [u8; $SIZE],
396 }
397
398 impl<'a> $ProcessImage<'a> {
399 $(
400 $( #[$field_meta] )*
401 $crate::tag_method!($vis, $field_name, const, $($tag)+);
402 )*
403 }
404
405 impl<'a> ::core::convert::From<&'a [u8; $SIZE]> for $ProcessImage<'a> {
406 #[inline(always)]
407 fn from(buf: &'a [u8; $SIZE]) -> Self {
408 Self { buf }
409 }
410 }
411
412 impl<'a> ::core::convert::TryFrom<&'a [u8]> for $ProcessImage<'a> {
413 type Error = ::core::array::TryFromSliceError;
414
415 #[inline(always)]
416 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
417 buf.try_into().map(|buf| Self { buf })
418 }
419 }
420
421 impl<'a> ::core::convert::AsRef<[u8]> for $ProcessImage<'a> {
422 #[inline(always)]
423 fn as_ref(&self) -> &[u8] {
424 &self.buf[..]
425 }
426 }
427
428 impl<'a> ::core::fmt::Debug for $ProcessImage<'a> {
429 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
430 f.debug_struct(::core::stringify!($ProcessImage))
431 $(
432 .field(::core::stringify!($field_name), &self.$field_name())
433 )*
434 .finish()
435 }
436 }
437
438 $( #[$meta] )*
439 $vis struct $ProcessImageMut<'a> {
440 buf: &'a mut [u8; $SIZE],
441 }
442
443 impl<'a> ::core::convert::From<&'a mut [u8; $SIZE]> for $ProcessImageMut<'a> {
444 #[inline(always)]
445 fn from(buf: &'a mut [u8; $SIZE]) -> Self {
446 Self { buf }
447 }
448 }
449
450 impl<'a> ::core::convert::TryFrom<&'a mut [u8]> for $ProcessImageMut<'a> {
451 type Error = ::core::array::TryFromSliceError;
452
453 #[inline(always)]
454 fn try_from(buf: &'a mut [u8]) -> Result<Self, Self::Error> {
455 buf.try_into().map(|buf| Self { buf })
456 }
457 }
458
459 impl<'a> ::core::convert::AsRef<[u8]> for $ProcessImageMut<'a> {
460 #[inline(always)]
461 fn as_ref(&self) -> &[u8] {
462 &self.buf[..]
463 }
464 }
465
466 impl<'a> ::core::convert::AsMut<[u8]> for $ProcessImageMut<'a> {
467 #[inline(always)]
468 fn as_mut(&mut self) -> &mut [u8] {
469 &mut self.buf[..]
470 }
471 }
472
473 impl<'a> ::core::fmt::Debug for $ProcessImageMut<'a> {
474 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
475 let pi = $ProcessImage::from(&*self.buf);
476 f.debug_struct(::core::stringify!($ProcessImageMut))
477 $(
478 .field(::core::stringify!($field_name), &pi.$field_name())
479 )*
480 .finish()
481 }
482 }
483
484 impl<'a> $ProcessImageMut<'a> {
485 $(
486 $( #[$field_meta] )*
487 $crate::tag_method!($vis, $field_name, mut, $($tag)+);
488 )*
489 }
490 };
491 (
492 $( #[$meta:meta] )*
493 $vis:vis struct mut $ProcessImageMut:ident: $SIZE:literal {
494 $(
495 $( #[$field_meta:meta] )*
496 $field_vis:vis $field_name:ident: ($($tag:tt)+)
497 ),*
498 $(,)?
499 }
500 ) => {
501 $( #[$meta] )*
502 $vis struct $ProcessImageMut<'a> {
503 buf: &'a mut [u8; $SIZE],
504 }
505
506 impl<'a> ::core::convert::From<&'a mut [u8; $SIZE]> for $ProcessImageMut<'a> {
507 #[inline(always)]
508 fn from(buf: &'a mut [u8; $SIZE]) -> Self {
509 Self { buf }
510 }
511 }
512
513 impl<'a> ::core::convert::TryFrom<&'a mut [u8]> for $ProcessImageMut<'a> {
514 type Error = ::core::array::TryFromSliceError;
515
516 #[inline(always)]
517 fn try_from(buf: &'a mut [u8]) -> Result<Self, Self::Error> {
518 buf.try_into().map(|buf| Self { buf })
519 }
520 }
521
522 impl<'a> ::core::convert::AsRef<[u8]> for $ProcessImageMut<'a> {
523 #[inline(always)]
524 fn as_ref(&self) -> &[u8] {
525 &self.buf[..]
526 }
527 }
528
529 impl<'a> ::core::convert::AsMut<[u8]> for $ProcessImageMut<'a> {
530 #[inline(always)]
531 fn as_mut(&mut self) -> &mut [u8] {
532 &mut self.buf[..]
533 }
534 }
535
536 impl<'a> $ProcessImageMut<'a> {
537 $(
538 $( #[$field_meta] )*
539 $crate::tag_method!($vis, $field_name, mut, $($tag)+);
540 )*
541 }
542 };
543 (
544 $( #[$meta:meta] )*
545 $vis:vis struct $ProcessImage:ident: $SIZE:literal {
546 $(
547 $( #[$field_meta:meta] )*
548 $field_vis:vis $field_name:ident: ($($tag:tt)+)
549 ),*
550 $(,)?
551 }
552 ) => {
553 $( #[$meta] )*
554 $vis struct $ProcessImage<'a> {
555 buf: &'a [u8; $SIZE],
556 }
557
558 impl<'a> $ProcessImage<'a> {
559 $(
560 $( #[$field_meta] )*
561 $crate::tag_method!($vis, $field_name, const, $($tag)+);
562 )*
563 }
564
565 impl<'a> ::core::convert::From<&'a [u8; $SIZE]> for $ProcessImage<'a> {
566 #[inline(always)]
567 fn from(buf: &'a [u8; $SIZE]) -> Self {
568 Self { buf }
569 }
570 }
571
572 impl<'a> ::core::convert::TryFrom<&'a [u8]> for $ProcessImage<'a> {
573 type Error = ::core::array::TryFromSliceError;
574
575 #[inline(always)]
576 fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
577 buf.try_into().map(|buf| Self { buf })
578 }
579 }
580
581 impl<'a> ::core::convert::AsRef<[u8]> for $ProcessImage<'a> {
582 #[inline(always)]
583 fn as_ref(&self) -> &[u8] {
584 &self.buf[..]
585 }
586 }
587
588 impl<'a> ::core::fmt::Debug for $ProcessImage<'a> {
589 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
590 f.debug_struct(::core::stringify!($ProcessImage))
591 $(
592 .field(::core::stringify!($field_name), &self.$field_name())
593 )*
594 .finish()
595 }
596 }
597 };
598}
599
600#[macro_export]
643macro_rules! process_image_owned {
644 (
645 $( #[$meta:meta] )*
646 $vis:vis struct $ProcessImage:ident, mut $ProcessImageMut:ident: $SIZE:literal {
647 $(
648 $( #[$field_meta:meta] )*
649 $field_vis:vis $field_name:ident: ($($tag:tt)+)
650 ),*
651 $(,)?
652 }
653 ) => {
654 $( #[$meta] )*
655 $vis struct $ProcessImage {
656 buf: [u8; $SIZE],
657 }
658
659 impl $ProcessImage {
660 #[allow(dead_code)]
661 #[inline(always)]
662 pub fn new_zeroed() -> Self {
663 Self {
664 buf: [0u8; $SIZE],
665 }
666 }
667
668 #[allow(dead_code)]
669 #[inline(always)]
670 pub fn as_mut(&mut self) -> $ProcessImageMut {
671 $ProcessImageMut::from(&mut self.buf)
672 }
673
674 #[allow(dead_code)]
675 #[inline(always)]
676 pub fn as_slice(&self) -> &[u8] {
677 &self.buf[..]
678 }
679
680 #[allow(dead_code)]
681 #[inline(always)]
682 pub fn as_slice_mut(&mut self) -> &mut [u8] {
683 &mut self.buf[..]
684 }
685
686 $(
687 $( #[$field_meta] )*
688 $crate::tag_method!($vis, $field_name, const, $($tag)+);
689 )*
690 }
691
692 impl ::core::convert::From<&[u8; $SIZE]> for $ProcessImage {
693 #[inline(always)]
694 fn from(buf_in: &[u8; $SIZE]) -> Self {
695 let mut buf = [0u8; $SIZE];
696 buf.copy_from_slice(buf_in);
697 Self { buf }
698 }
699 }
700
701 impl ::core::convert::TryFrom<&[u8]> for $ProcessImage {
702 type Error = ::core::array::TryFromSliceError;
703
704 #[inline(always)]
705 fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
706 buf.try_into().map(|buf: &[u8; $SIZE]| Self { buf: buf.clone() })
707 }
708 }
709
710 impl ::core::convert::AsRef<[u8]> for $ProcessImage {
711 #[inline(always)]
712 fn as_ref(&self) -> &[u8] {
713 &self.buf[..]
714 }
715 }
716
717 impl ::core::convert::AsMut<[u8]> for $ProcessImage {
718 #[inline(always)]
719 fn as_mut(&mut self) -> &mut [u8] {
720 &mut self.buf[..]
721 }
722 }
723
724 impl ::core::fmt::Debug for $ProcessImage {
725 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
726 f.debug_struct(::core::stringify!($ProcessImage))
727 $(
728 .field(::core::stringify!($field_name), &self.$field_name())
729 )*
730 .finish()
731 }
732 }
733
734 $crate::process_image! {
735 $(#[$meta])*
736 $vis struct mut $ProcessImageMut: $SIZE {
737 $(
738 $(#[$field_meta])*
739 $field_vis $field_name: ($($tag)+),
740 )*
741 }
742 }
743 };
744}
745
746#[cfg(test)]
747mod tests {
748 #[test]
749 fn tag_macro_smoke1() {
750 let mut pi = [0x55, 0xaa, 0x00, 0xff];
751
752 assert_eq!(tag!(&pi, X, 2, 0), false);
753 assert_eq!(tag!(&pi, 3, 0), true);
754 assert_eq!(tag!(&pi, X, 0, 0), true);
755 assert_eq!(tag!(&pi, 0, 1), false);
756
757 *tag_mut!(&mut pi, X, 0, 0) = false;
758 assert_eq!(tag!(&pi, X, 0, 0), false);
759
760 assert_eq!(tag!(&pi, B, 2), 0x00);
761 *tag_mut!(&mut pi, X, 2, 7) = true;
762 assert_eq!(tag!(&pi, B, 2), 0x80);
763
764 assert_eq!(tag!(&pi, W, 2), 0x80ff);
765 assert_eq!(*tag_mut!(&mut pi, W, 2), 0x80ff);
766 assert_eq!(tag!(&pi, D, 0), 0x54aa80ff);
767 assert_eq!(*tag_mut!(&mut pi, D, 0), 0x54aa80ff);
768
769 *tag_mut!(&mut pi, W, 2) = 0xbeef;
770 assert_eq!(tag!(&pi, W, 2), 0xbeef);
771 }
772
773 process_image! {
774 pub struct TestPi, mut TestPiMut: 4 {
775 pub btn_start: (X, 1, 0),
776 pub btn_stop: (1, 1),
777 pub btn_reset: (X, 1, 2),
778 pub speed: (W, 2),
779 pub length: (B, 0),
780 }
781 }
782
783 #[test]
784 fn pi_macro_smoke1() {
785 let mut pi_buffer = [128, 0x55, 0xde, 0xad];
786
787 let pi = TestPi::try_from(&pi_buffer).unwrap();
788 assert_eq!(pi.btn_start(), true);
789 assert_eq!(pi.btn_stop(), false);
790 assert_eq!(pi.btn_reset(), true);
791 assert_eq!(pi.speed(), 0xdead);
792 assert_eq!(pi.length(), 128);
793
794 let mut pi = TestPiMut::try_from(&mut pi_buffer).unwrap();
795 assert_eq!(*pi.btn_start(), true);
796 assert_eq!(*pi.btn_stop(), false);
797 assert_eq!(*pi.btn_reset(), true);
798 assert_eq!(*pi.speed(), 0xdead);
799 assert_eq!(*pi.length(), 128);
800
801 *pi.btn_start() = false;
802 *pi.btn_stop() = true;
803
804 *pi.speed() = 1337;
805 *pi.length() = 1;
806
807 let pi = TestPi::try_from(&pi_buffer).unwrap();
808 assert_eq!(pi.btn_start(), false);
809 assert_eq!(pi.btn_stop(), true);
810 assert_eq!(pi.btn_reset(), true);
811 assert_eq!(pi.speed(), 1337);
812 assert_eq!(pi.length(), 1);
813
814 assert_eq!(tag!(&pi_buffer, 1, 0), false);
815 assert_eq!(tag!(&pi_buffer, W, 2), 1337);
816 assert_eq!(tag!(&pi_buffer, B, 0), 1);
817 }
818
819 process_image_owned! {
820 pub struct TestPiOwned, mut TestPiOwnedMut: 4 {
821 pub btn_start: (X, 1, 0),
822 pub btn_stop: (1, 1),
823 pub btn_reset: (X, 1, 2),
824 pub speed: (W, 2),
825 pub length: (B, 0),
826 }
827 }
828
829 #[test]
830 fn pi_owned_macro_smoke() {
831 let pi_buffer = [128, 0x55, 0xde, 0xad];
832
833 let mut pi = TestPiOwned::new_zeroed();
834 assert_eq!(pi.btn_start(), false);
835 assert_eq!(pi.btn_stop(), false);
836 assert_eq!(pi.btn_reset(), false);
837 assert_eq!(pi.speed(), 0);
838 assert_eq!(pi.length(), 0);
839
840 pi.as_slice_mut().copy_from_slice(&pi_buffer);
841 assert_eq!(pi.btn_start(), true);
842 assert_eq!(pi.btn_stop(), false);
843 assert_eq!(pi.btn_reset(), true);
844 assert_eq!(pi.speed(), 0xdead);
845 assert_eq!(pi.length(), 128);
846
847 let mut pi = TestPiOwned::try_from(&pi_buffer).unwrap();
848 assert_eq!(pi.btn_start(), true);
849 assert_eq!(pi.btn_stop(), false);
850 assert_eq!(pi.btn_reset(), true);
851 assert_eq!(pi.speed(), 0xdead);
852 assert_eq!(pi.length(), 128);
853
854 *pi.as_mut().btn_start() = false;
855 *pi.as_mut().btn_stop() = true;
856 *pi.as_mut().btn_reset() = true;
857
858 *pi.as_mut().speed() = 1337;
859 *pi.as_mut().length() = 1;
860
861 assert_eq!(pi.btn_start(), false);
862 assert_eq!(pi.btn_stop(), true);
863 assert_eq!(pi.btn_reset(), true);
864 assert_eq!(pi.speed(), 1337);
865 assert_eq!(pi.length(), 1);
866
867 let pi_buffer = pi.as_slice();
868 assert_eq!(tag!(&pi_buffer, 1, 0), false);
869 assert_eq!(tag!(&pi_buffer, W, 2), 1337);
870 assert_eq!(tag!(&pi_buffer, B, 0), 1);
871 }
872
873 #[test]
874 #[cfg_attr(
875 not(feature = "allow_unaligned_tags"),
876 should_panic(expected = "Word address must be divisible by 2")
877 )]
878 fn test_unaligned_word_tag() {
879 let buf = [
880 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
881 ];
882 assert_eq!(tag!(&buf, W, 1), 0xadbe);
883 }
884
885 #[test]
886 #[cfg_attr(
887 not(feature = "allow_unaligned_tags"),
888 should_panic(expected = "Word address must be divisible by 2")
889 )]
890 fn test_unaligned_word_tag_mut() {
891 let mut buf = [
892 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
893 ];
894 *tag_mut!(&mut buf, W, 1) = 0xcafe;
895 assert_eq!(tag!(&buf, W, 1), 0xcafe);
896 }
897
898 process_image_owned! {
899 pub struct TestPiPanic, mut TestPiPanicMut: 12 {
900 pub unaligned_word: (W, 1),
901 pub unaligned_dword: (D, 2),
902 pub unaligned_lword: (L, 4),
903 }
904 }
905
906 #[test]
907 #[cfg_attr(
908 not(feature = "allow_unaligned_tags"),
909 should_panic(expected = "Word address must be divisible by 2")
910 )]
911 fn test_unaligned_word() {
912 let pi = TestPiPanic::try_from(&[
913 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
914 ])
915 .unwrap();
916 assert_eq!(pi.unaligned_word(), 0xadbe);
917 }
918
919 #[test]
920 #[cfg_attr(
921 not(feature = "allow_unaligned_tags"),
922 should_panic(expected = "Word address must be divisible by 2")
923 )]
924 fn test_unaligned_word_mut() {
925 let mut pi = TestPiPanic::try_from(&[
926 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
927 ])
928 .unwrap();
929 *pi.as_mut().unaligned_word() = 0xcafe;
930 assert_eq!(pi.unaligned_word(), 0xcafe);
931 }
932
933 #[test]
934 #[cfg_attr(
935 not(feature = "allow_unaligned_tags"),
936 should_panic(expected = "Double word address must be divisible by 4")
937 )]
938 fn test_unaligned_dword() {
939 let pi = TestPiPanic::try_from(&[
940 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
941 ])
942 .unwrap();
943 assert_eq!(pi.unaligned_dword(), 0xbeefdead);
944 }
945
946 #[test]
947 #[cfg_attr(
948 not(feature = "allow_unaligned_tags"),
949 should_panic(expected = "Double word address must be divisible by 4")
950 )]
951 fn test_unaligned_dword_mut() {
952 let mut pi = TestPiPanic::try_from(&[
953 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
954 ])
955 .unwrap();
956 *pi.as_mut().unaligned_dword() = 0xc0ffee77;
957 assert_eq!(pi.unaligned_dword(), 0xc0ffee77);
958 }
959
960 #[test]
961 #[cfg_attr(
962 not(feature = "allow_unaligned_tags"),
963 should_panic(expected = "Long word address must be divisible by 8")
964 )]
965 fn test_unaligned_lword() {
966 let pi = TestPiPanic::try_from(&[
967 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
968 ])
969 .unwrap();
970 assert_eq!(pi.unaligned_lword(), 0xdeadbeefdeadbeef);
971 }
972
973 #[test]
974 #[cfg_attr(
975 not(feature = "allow_unaligned_tags"),
976 should_panic(expected = "Long word address must be divisible by 8")
977 )]
978 fn test_unaligned_lword_mut() {
979 let mut pi = TestPiPanic::try_from(&[
980 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
981 ])
982 .unwrap();
983 *pi.as_mut().unaligned_lword() = 0x7fff000000c0ffee;
984 assert_eq!(pi.unaligned_lword(), 0x7fff000000c0ffee);
985 }
986}