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:expr) => {};
97}
98
99#[cfg(not(feature = "allow_unaligned_tags"))]
100#[doc(hidden)]
101#[macro_export]
102macro_rules! alignment_assert {
103 (2, $addr:expr) => {
104 assert!($addr % 2 == 0, "Word address must be divisible by 2");
105 };
106 (4, $addr:expr) => {
107 assert!($addr % 4 == 0, "Double word address must be divisible by 4");
108 };
109 (8, $addr:expr) => {
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 let addr = 2;
767 assert_eq!(*tag_mut!(&mut pi, W, addr), 0x80ff);
768 assert_eq!(tag!(&pi, D, 0), 0x54aa80ff);
769 assert_eq!(*tag_mut!(&mut pi, D, 0), 0x54aa80ff);
770
771 *tag_mut!(&mut pi, W, 2) = 0xbeef;
772 assert_eq!(tag!(&pi, W, 2), 0xbeef);
773 }
774
775 process_image! {
776 pub struct TestPi, mut TestPiMut: 4 {
777 pub btn_start: (X, 1, 0),
778 pub btn_stop: (1, 1),
779 pub btn_reset: (X, 1, 2),
780 pub speed: (W, 2),
781 pub length: (B, 0),
782 }
783 }
784
785 #[test]
786 fn pi_macro_smoke1() {
787 let mut pi_buffer = [128, 0x55, 0xde, 0xad];
788
789 let pi = TestPi::try_from(&pi_buffer).unwrap();
790 assert_eq!(pi.btn_start(), true);
791 assert_eq!(pi.btn_stop(), false);
792 assert_eq!(pi.btn_reset(), true);
793 assert_eq!(pi.speed(), 0xdead);
794 assert_eq!(pi.length(), 128);
795
796 let mut pi = TestPiMut::try_from(&mut pi_buffer).unwrap();
797 assert_eq!(*pi.btn_start(), true);
798 assert_eq!(*pi.btn_stop(), false);
799 assert_eq!(*pi.btn_reset(), true);
800 assert_eq!(*pi.speed(), 0xdead);
801 assert_eq!(*pi.length(), 128);
802
803 *pi.btn_start() = false;
804 *pi.btn_stop() = true;
805
806 *pi.speed() = 1337;
807 *pi.length() = 1;
808
809 let pi = TestPi::try_from(&pi_buffer).unwrap();
810 assert_eq!(pi.btn_start(), false);
811 assert_eq!(pi.btn_stop(), true);
812 assert_eq!(pi.btn_reset(), true);
813 assert_eq!(pi.speed(), 1337);
814 assert_eq!(pi.length(), 1);
815
816 assert_eq!(tag!(&pi_buffer, 1, 0), false);
817 assert_eq!(tag!(&pi_buffer, W, 2), 1337);
818 assert_eq!(tag!(&pi_buffer, B, 0), 1);
819 }
820
821 process_image_owned! {
822 pub struct TestPiOwned, mut TestPiOwnedMut: 4 {
823 pub btn_start: (X, 1, 0),
824 pub btn_stop: (1, 1),
825 pub btn_reset: (X, 1, 2),
826 pub speed: (W, 2),
827 pub length: (B, 0),
828 }
829 }
830
831 #[test]
832 fn pi_owned_macro_smoke() {
833 let pi_buffer = [128, 0x55, 0xde, 0xad];
834
835 let mut pi = TestPiOwned::new_zeroed();
836 assert_eq!(pi.btn_start(), false);
837 assert_eq!(pi.btn_stop(), false);
838 assert_eq!(pi.btn_reset(), false);
839 assert_eq!(pi.speed(), 0);
840 assert_eq!(pi.length(), 0);
841
842 pi.as_slice_mut().copy_from_slice(&pi_buffer);
843 assert_eq!(pi.btn_start(), true);
844 assert_eq!(pi.btn_stop(), false);
845 assert_eq!(pi.btn_reset(), true);
846 assert_eq!(pi.speed(), 0xdead);
847 assert_eq!(pi.length(), 128);
848
849 let mut pi = TestPiOwned::try_from(&pi_buffer).unwrap();
850 assert_eq!(pi.btn_start(), true);
851 assert_eq!(pi.btn_stop(), false);
852 assert_eq!(pi.btn_reset(), true);
853 assert_eq!(pi.speed(), 0xdead);
854 assert_eq!(pi.length(), 128);
855
856 *pi.as_mut().btn_start() = false;
857 *pi.as_mut().btn_stop() = true;
858 *pi.as_mut().btn_reset() = true;
859
860 *pi.as_mut().speed() = 1337;
861 *pi.as_mut().length() = 1;
862
863 assert_eq!(pi.btn_start(), false);
864 assert_eq!(pi.btn_stop(), true);
865 assert_eq!(pi.btn_reset(), true);
866 assert_eq!(pi.speed(), 1337);
867 assert_eq!(pi.length(), 1);
868
869 let pi_buffer = pi.as_slice();
870 assert_eq!(tag!(&pi_buffer, 1, 0), false);
871 assert_eq!(tag!(&pi_buffer, W, 2), 1337);
872 assert_eq!(tag!(&pi_buffer, B, 0), 1);
873 }
874
875 #[test]
876 #[cfg_attr(
877 not(feature = "allow_unaligned_tags"),
878 should_panic(expected = "Word address must be divisible by 2")
879 )]
880 fn test_unaligned_word_tag() {
881 let buf = [
882 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
883 ];
884 assert_eq!(tag!(&buf, W, 1), 0xadbe);
885 }
886
887 #[test]
888 #[cfg_attr(
889 not(feature = "allow_unaligned_tags"),
890 should_panic(expected = "Word address must be divisible by 2")
891 )]
892 fn test_unaligned_word_tag_mut() {
893 let mut buf = [
894 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
895 ];
896 *tag_mut!(&mut buf, W, 1) = 0xcafe;
897 assert_eq!(tag!(&buf, W, 1), 0xcafe);
898 }
899
900 process_image_owned! {
901 pub struct TestPiPanic, mut TestPiPanicMut: 12 {
902 pub unaligned_word: (W, 1),
903 pub unaligned_dword: (D, 2),
904 pub unaligned_lword: (L, 4),
905 }
906 }
907
908 #[test]
909 #[cfg_attr(
910 not(feature = "allow_unaligned_tags"),
911 should_panic(expected = "Word address must be divisible by 2")
912 )]
913 fn test_unaligned_word() {
914 let pi = TestPiPanic::try_from(&[
915 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
916 ])
917 .unwrap();
918 assert_eq!(pi.unaligned_word(), 0xadbe);
919 }
920
921 #[test]
922 #[cfg_attr(
923 not(feature = "allow_unaligned_tags"),
924 should_panic(expected = "Word address must be divisible by 2")
925 )]
926 fn test_unaligned_word_mut() {
927 let mut pi = TestPiPanic::try_from(&[
928 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
929 ])
930 .unwrap();
931 *pi.as_mut().unaligned_word() = 0xcafe;
932 assert_eq!(pi.unaligned_word(), 0xcafe);
933 }
934
935 #[test]
936 #[cfg_attr(
937 not(feature = "allow_unaligned_tags"),
938 should_panic(expected = "Double word address must be divisible by 4")
939 )]
940 fn test_unaligned_dword() {
941 let pi = TestPiPanic::try_from(&[
942 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
943 ])
944 .unwrap();
945 assert_eq!(pi.unaligned_dword(), 0xbeefdead);
946 }
947
948 #[test]
949 #[cfg_attr(
950 not(feature = "allow_unaligned_tags"),
951 should_panic(expected = "Double word address must be divisible by 4")
952 )]
953 fn test_unaligned_dword_mut() {
954 let mut pi = TestPiPanic::try_from(&[
955 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
956 ])
957 .unwrap();
958 *pi.as_mut().unaligned_dword() = 0xc0ffee77;
959 assert_eq!(pi.unaligned_dword(), 0xc0ffee77);
960 }
961
962 #[test]
963 #[cfg_attr(
964 not(feature = "allow_unaligned_tags"),
965 should_panic(expected = "Long word address must be divisible by 8")
966 )]
967 fn test_unaligned_lword() {
968 let pi = TestPiPanic::try_from(&[
969 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
970 ])
971 .unwrap();
972 assert_eq!(pi.unaligned_lword(), 0xdeadbeefdeadbeef);
973 }
974
975 #[test]
976 #[cfg_attr(
977 not(feature = "allow_unaligned_tags"),
978 should_panic(expected = "Long word address must be divisible by 8")
979 )]
980 fn test_unaligned_lword_mut() {
981 let mut pi = TestPiPanic::try_from(&[
982 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
983 ])
984 .unwrap();
985 *pi.as_mut().unaligned_lword() = 0x7fff000000c0ffee;
986 assert_eq!(pi.unaligned_lword(), 0x7fff000000c0ffee);
987 }
988}