qcow2_rs/
helpers.rs

1// These are helpers, do not issue a warning if not all of them are used because of some disabled
2// features (and making all conditionally-used features conditional themselves is too cumbersome to
3// be worth it).
4#![allow(dead_code)]
5use std::ops::{Deref, DerefMut};
6
7#[macro_export]
8macro_rules! numerical_enum {
9    (
10        $(#[$attr:meta])*
11        pub enum $enum_name:ident as $repr:tt {
12            $(
13                $(#[$id_attr:meta])*
14                $identifier:ident = $value:literal,
15            )+
16        }
17    ) => {
18        $(#[$attr])*
19        #[derive(Copy, Clone, Debug, Eq, PartialEq)]
20        #[repr($repr)]
21        pub enum $enum_name {
22            $(
23                $(#[$id_attr])*
24                $identifier = $value,
25            )+
26        }
27
28        impl TryFrom<$repr> for $enum_name {
29            type Error = $crate::error::Qcow2Error;
30            fn try_from(val: $repr) -> $crate::error::Qcow2Result<Self> {
31                match val {
32                    $($value => Ok($enum_name::$identifier),)*
33                    _ => Err($crate::error::Qcow2Error::from_desc(format!(
34                        "Invalid value for {}: {:x}",
35                        stringify!($enum_name),
36                        val
37                    ))),
38                }
39            }
40        }
41    }
42}
43
44// TODO: Replace by int_roundings once that is stable
45pub trait IntAlignment: Sized {
46    /// Align `self` down to the closest value less or equal to `self` that is aligned to
47    /// `alignment`.  Returns `None` if and only if there is no such value.
48    /// `alignment` must be a power of two.
49    fn align_down<T: Into<Self>>(self, alignment: T) -> Option<Self>;
50
51    /// Align `self` up to the closest value greater or equal to `self` that is aligned to
52    /// `alignment`.  Returns `None` if and only if there is no such value.
53    /// `alignment` must be a power of two.
54    fn align_up<T: Into<Self>>(self, alignment: T) -> Option<Self>;
55}
56
57macro_rules! impl_int_alignment_for_primitive {
58    ($type:tt) => {
59        impl IntAlignment for $type {
60            fn align_down<T: Into<Self>>(self, alignment: T) -> Option<Self> {
61                let alignment: Self = alignment.into();
62                debug_assert!(alignment.is_power_of_two());
63
64                Some(self & !(alignment - 1))
65            }
66
67            fn align_up<T: Into<Self>>(self, alignment: T) -> Option<Self> {
68                let alignment: Self = alignment.into();
69                debug_assert!(alignment.is_power_of_two());
70
71                if self & (alignment - 1) == 0 {
72                    return Some(self);
73                }
74                (self | (alignment - 1)).checked_add(1)
75            }
76        }
77    };
78}
79
80impl_int_alignment_for_primitive!(u8);
81impl_int_alignment_for_primitive!(u16);
82impl_int_alignment_for_primitive!(u32);
83impl_int_alignment_for_primitive!(u64);
84impl_int_alignment_for_primitive!(usize);
85
86/// Slice like buffer, which address is aligned with 4096.
87///
88pub struct Qcow2IoBuf<T> {
89    ptr: *mut T,
90    size: usize,
91}
92
93// Users of Qcow2IoBuf has to deal with Send & Sync
94unsafe impl<T> Send for Qcow2IoBuf<T> {}
95unsafe impl<T> Sync for Qcow2IoBuf<T> {}
96
97impl<T> Qcow2IoBuf<T> {
98    pub fn new(size: usize) -> Self {
99        let layout = std::alloc::Layout::from_size_align(size, 4096).unwrap();
100        let ptr = unsafe { std::alloc::alloc(layout) } as *mut T;
101
102        assert!(size != 0);
103
104        Qcow2IoBuf { ptr, size }
105    }
106
107    /// how many elements in this buffer
108    #[allow(clippy::len_without_is_empty)]
109    pub fn len(&self) -> usize {
110        let elem_size = core::mem::size_of::<T>();
111        self.size / elem_size
112    }
113
114    /// Return raw address of this buffer
115    pub fn as_ptr(&self) -> *const T {
116        self.ptr
117    }
118
119    /// Return mutable raw address of this buffer
120    pub fn as_mut_ptr(&self) -> *mut T {
121        self.ptr
122    }
123
124    /// slice with u8 element, only for RefBlock
125    pub(crate) fn as_u8_slice(&self) -> &[u8] {
126        unsafe { std::slice::from_raw_parts(self.ptr as *const u8, self.size) }
127    }
128
129    /// mutable slice with u8 element, only for RefBlock
130    pub(crate) fn as_u8_slice_mut(&mut self) -> &mut [u8] {
131        unsafe { std::slice::from_raw_parts_mut(self.ptr as *mut u8, self.size) }
132    }
133
134    /// fill zero for every bits of this buffer
135    pub fn zero_buf(&mut self) {
136        unsafe {
137            std::ptr::write_bytes(self.as_mut_ptr(), 0, self.len());
138        }
139    }
140}
141
142impl<T> std::fmt::Debug for Qcow2IoBuf<T> {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(
145            f,
146            "ptr {:?} size {} element type {}",
147            self.ptr,
148            self.size,
149            qcow2_type_of(unsafe { &*self.ptr })
150        )
151    }
152}
153
154/// Slice reference of this buffer
155impl<T> Deref for Qcow2IoBuf<T> {
156    type Target = [T];
157    fn deref(&self) -> &[T] {
158        let elem_size = core::mem::size_of::<T>();
159        unsafe { std::slice::from_raw_parts(self.ptr, self.size / elem_size) }
160    }
161}
162
163/// Mutable slice reference of this buffer
164impl<T> DerefMut for Qcow2IoBuf<T> {
165    fn deref_mut(&mut self) -> &mut [T] {
166        let elem_size = core::mem::size_of::<T>();
167        unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size / elem_size) }
168    }
169}
170
171/// Free buffer with same alloc layout
172impl<T> Drop for Qcow2IoBuf<T> {
173    fn drop(&mut self) {
174        let layout = std::alloc::Layout::from_size_align(self.size, 4096).unwrap();
175        unsafe { std::alloc::dealloc(self.ptr as *mut u8, layout) };
176    }
177}
178
179/// It is user's responsibility to not free buffer of the slice
180pub fn slice_to_vec<T>(s: &[T]) -> Vec<T> {
181    // Get a pointer to the data and its length from the existing slice
182    let ptr = s.as_ptr();
183    let len = s.len();
184
185    unsafe { Vec::from_raw_parts(ptr as *mut T, len, len) }
186}
187
188#[macro_export]
189macro_rules! zero_buf {
190    ($buffer:expr) => {{
191        unsafe {
192            std::ptr::write_bytes($buffer.as_mut_ptr(), 0, $buffer.len());
193        }
194    }};
195}
196
197pub fn qcow2_type_of<T>(_: &T) -> String {
198    std::any::type_name::<T>().to_string()
199}