imago/qcow2/
types.rs

1//! Helper types.
2//!
3//! Contains types like `GuestOffset` or `HostCluster`.  This strong typing ensures there is no
4//! confusion between what is what.
5
6use super::*;
7use std::fmt::{self, Display, Formatter};
8use std::ops::{Add, AddAssign, Sub, SubAssign};
9
10/// Guest offset.
11#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub(super) struct GuestOffset(pub u64);
13
14/// Guest cluster index.
15#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub(super) struct GuestCluster(pub u64);
17
18/// Host cluster offset.
19#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20pub(super) struct HostOffset(pub u64);
21
22/// Host cluster index.
23#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub(super) struct HostCluster(pub u64);
25
26/// Cluster count.
27#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
28pub(super) struct ClusterCount(pub u64);
29
30impl GuestOffset {
31    /// Return the offset from the start of the containing guest clusters.
32    pub fn in_cluster_offset(self, cluster_bits: u32) -> usize {
33        (self.0 % (1 << cluster_bits)) as usize
34    }
35
36    /// Return the containing cluster’s index in its L2 table.
37    pub fn l2_index(self, cluster_bits: u32) -> usize {
38        self.cluster(cluster_bits).l2_index(cluster_bits)
39    }
40
41    /// Return the containing cluster’s L2 table’s index in the L1 table.
42    pub fn l1_index(self, cluster_bits: u32) -> usize {
43        self.cluster(cluster_bits).l1_index(cluster_bits)
44    }
45
46    /// Return the containing cluster’s index.
47    pub fn cluster(self, cluster_bits: u32) -> GuestCluster {
48        GuestCluster(self.0 >> cluster_bits)
49    }
50
51    /// If this offset points to the start of a cluster, get its index.
52    ///
53    /// If this offset points inside of a cluster, return `None`.  As oposed to just `cluster()`,
54    /// this will not discard information: `self.checked_cluster(cb).unwrap().offset() == self`,
55    /// because there is no in-cluster offset that could be lost.
56    pub fn checked_cluster(self, cluster_bits: u32) -> Option<GuestCluster> {
57        (self.in_cluster_offset(cluster_bits) == 0).then_some(self.cluster(cluster_bits))
58    }
59
60    /// How many bytes remain in this cluster after this offset.
61    pub fn remaining_in_cluster(self, cluster_bits: u32) -> u64 {
62        ((1 << cluster_bits) - self.in_cluster_offset(cluster_bits)) as u64
63    }
64
65    /// How many bytes remain in this L2 table after this offset.
66    pub fn remaining_in_l2_table(self, cluster_bits: u32) -> u64 {
67        // See `Header::l2_entries()`
68        let l2_entries = 1 << (cluster_bits - 3);
69        let after_this = ((l2_entries - (self.l2_index(cluster_bits) + 1)) as u64) << cluster_bits;
70        self.remaining_in_cluster(cluster_bits) + after_this
71    }
72}
73
74impl GuestCluster {
75    /// Return this cluster’s offset.
76    pub fn offset(self, cluster_bits: u32) -> GuestOffset {
77        GuestOffset(self.0 << cluster_bits)
78    }
79
80    /// Return this cluster’s index in its L2 table.
81    pub fn l2_index(self, cluster_bits: u32) -> usize {
82        // See `Header::l2_entries()`
83        let l2_entries = 1 << (cluster_bits - 3);
84        (self.0 % l2_entries) as usize
85    }
86
87    /// Return this cluster’s L2 table’s index in the L1 table.
88    pub fn l1_index(self, cluster_bits: u32) -> usize {
89        let l2_entries_shift = cluster_bits - 3;
90        (self.0 >> l2_entries_shift) as usize
91    }
92
93    /// Return the cluster at the given L1 and L2 table indices.
94    pub fn from_l1_l2_indices(l1_index: usize, l2_index: usize, cluster_bits: u32) -> Self {
95        let l2_entries_shift = cluster_bits - 3;
96        GuestCluster(((l1_index as u64) << l2_entries_shift) + l2_index as u64)
97    }
98
99    /// Return the next cluster in this L2 table, if any.
100    ///
101    /// Return `None` if this is the last cluster in this L2 table.
102    pub fn next_in_l2(self, cluster_bits: u32) -> Option<GuestCluster> {
103        // See `Header::l2_entries()`
104        let l2_entries = 1 << (cluster_bits - 3);
105        let l1_index = self.l1_index(cluster_bits);
106        let l2_index = self.l2_index(cluster_bits);
107        let l2_index = l2_index.checked_add(1)?;
108        if l2_index >= l2_entries {
109            None
110        } else {
111            Some(GuestCluster::from_l1_l2_indices(
112                l1_index,
113                l2_index,
114                cluster_bits,
115            ))
116        }
117    }
118
119    /// Return the first cluster in the next L2 table.
120    pub fn first_in_next_l2(self, cluster_bits: u32) -> GuestCluster {
121        let l2_entries = 1 << (cluster_bits - 3);
122        GuestCluster((self.0 + 1).next_multiple_of(l2_entries))
123    }
124}
125
126impl HostOffset {
127    /// Return the offset from the start of the containing host cluster.
128    pub fn in_cluster_offset(self, cluster_bits: u32) -> usize {
129        (self.0 % (1 << cluster_bits)) as usize
130    }
131
132    /// Return the containing cluster’s index.
133    pub fn cluster(self, cluster_bits: u32) -> HostCluster {
134        HostCluster(self.0 >> cluster_bits)
135    }
136
137    /// If this offset points to the start of a cluster, get its index.
138    ///
139    /// If this offset points inside of a cluster, return `None`.  As oposed to just `cluster()`,
140    /// this will not discard information: `self.checked_cluster(cb).unwrap().offset() == self`,
141    /// because there is no in-cluster offset that could be lost.
142    pub fn checked_cluster(self, cluster_bits: u32) -> Option<HostCluster> {
143        (self.in_cluster_offset(cluster_bits) == 0).then_some(self.cluster(cluster_bits))
144    }
145}
146
147impl HostCluster {
148    /// Return this cluster’s offset.
149    pub fn offset(self, cluster_bits: u32) -> HostOffset {
150        HostOffset(self.0 << cluster_bits)
151    }
152
153    /// Get this cluster’s index in its refcount block.
154    pub fn rb_index(self, rb_bits: u32) -> usize {
155        let rb_entries = 1 << rb_bits;
156        (self.0 % rb_entries) as usize
157    }
158
159    /// Get this cluster’s refcount block’s index in the refcount table.
160    pub fn rt_index(self, rb_bits: u32) -> usize {
161        (self.0 >> rb_bits) as usize
162    }
163
164    /// Get both the reftable and refblock indices for this cluster.
165    pub fn rt_rb_indices(self, rb_bits: u32) -> (usize, usize) {
166        (self.rt_index(rb_bits), self.rb_index(rb_bits))
167    }
168
169    /// Construct a cluster index from its reftable and refblock indices.
170    pub fn from_ref_indices(rt_index: usize, rb_index: usize, rb_bits: u32) -> Self {
171        HostCluster(((rt_index as u64) << rb_bits) + rb_index as u64)
172    }
173
174    /// Returns the host offset corresponding to `guest_offset`.
175    ///
176    /// Assuming `guest_offset.cluster()` is mapped to `self`, return the exact host offset
177    /// matching `guest_offset`.
178    ///
179    /// Same as `self.offset(cb) + guest_offset.in_cluster_offset`.
180    pub fn relative_offset(self, guest_offset: GuestOffset, cluster_bits: u32) -> HostOffset {
181        self.offset(cluster_bits) + guest_offset.in_cluster_offset(cluster_bits) as u64
182    }
183}
184
185impl ClusterCount {
186    /// Get how many clusters are required to cover `byte_size`.
187    ///
188    /// This rounds up.
189    pub fn from_byte_size(byte_size: u64, cluster_bits: u32) -> Self {
190        ClusterCount(byte_size.div_ceil(1 << cluster_bits))
191    }
192
193    /// Get how many clusters are in `byte_size`.
194    ///
195    /// Checks that `byte_size` is a multiple of the cluster size, or will return `None`.
196    pub fn checked_from_byte_size(byte_size: u64, cluster_bits: u32) -> Option<Self> {
197        (byte_size & ((1 << cluster_bits) - 1) == 0)
198            .then_some(ClusterCount(byte_size >> cluster_bits))
199    }
200
201    /// Return the full byte size of this many clusters.
202    pub fn byte_size(self, cluster_bits: u32) -> u64 {
203        self.0 << cluster_bits
204    }
205}
206
207impl Add<ClusterCount> for GuestCluster {
208    type Output = Self;
209
210    fn add(self, rhs: ClusterCount) -> Self {
211        GuestCluster(self.0 + rhs.0)
212    }
213}
214
215impl AddAssign<ClusterCount> for GuestCluster {
216    fn add_assign(&mut self, rhs: ClusterCount) {
217        self.0 += rhs.0;
218    }
219}
220
221impl Sub<ClusterCount> for GuestCluster {
222    type Output = Self;
223
224    fn sub(self, rhs: ClusterCount) -> Self {
225        GuestCluster(self.0 - rhs.0)
226    }
227}
228
229impl SubAssign<ClusterCount> for GuestCluster {
230    fn sub_assign(&mut self, rhs: ClusterCount) {
231        self.0 -= rhs.0;
232    }
233}
234
235impl Sub<GuestCluster> for GuestCluster {
236    type Output = ClusterCount;
237
238    fn sub(self, rhs: Self) -> ClusterCount {
239        ClusterCount(self.0 - rhs.0)
240    }
241}
242
243impl Add<ClusterCount> for HostCluster {
244    type Output = Self;
245
246    fn add(self, rhs: ClusterCount) -> Self {
247        HostCluster(self.0 + rhs.0)
248    }
249}
250
251impl AddAssign<ClusterCount> for HostCluster {
252    fn add_assign(&mut self, rhs: ClusterCount) {
253        self.0 += rhs.0;
254    }
255}
256
257impl Sub<ClusterCount> for HostCluster {
258    type Output = Self;
259
260    fn sub(self, rhs: ClusterCount) -> Self {
261        HostCluster(self.0 - rhs.0)
262    }
263}
264
265impl SubAssign<ClusterCount> for HostCluster {
266    fn sub_assign(&mut self, rhs: ClusterCount) {
267        self.0 -= rhs.0;
268    }
269}
270
271impl Sub<HostCluster> for HostCluster {
272    type Output = ClusterCount;
273
274    fn sub(self, rhs: Self) -> ClusterCount {
275        ClusterCount(self.0 - rhs.0)
276    }
277}
278
279impl Add<ClusterCount> for ClusterCount {
280    type Output = Self;
281
282    fn add(self, rhs: ClusterCount) -> Self {
283        ClusterCount(self.0 + rhs.0)
284    }
285}
286
287impl AddAssign<ClusterCount> for ClusterCount {
288    fn add_assign(&mut self, rhs: ClusterCount) {
289        self.0 += rhs.0;
290    }
291}
292
293impl Sub<ClusterCount> for ClusterCount {
294    type Output = Self;
295
296    fn sub(self, rhs: ClusterCount) -> Self {
297        ClusterCount(self.0 - rhs.0)
298    }
299}
300
301impl SubAssign<ClusterCount> for ClusterCount {
302    fn sub_assign(&mut self, rhs: ClusterCount) {
303        self.0 -= rhs.0;
304    }
305}
306
307impl Add<u64> for GuestOffset {
308    type Output = Self;
309
310    fn add(self, rhs: u64) -> Self {
311        GuestOffset(self.0 + rhs)
312    }
313}
314
315impl Sub<u64> for GuestOffset {
316    type Output = Self;
317
318    fn sub(self, rhs: u64) -> Self {
319        GuestOffset(self.0 - rhs)
320    }
321}
322
323impl Sub<GuestOffset> for GuestOffset {
324    type Output = u64;
325
326    fn sub(self, rhs: Self) -> u64 {
327        self.0 - rhs.0
328    }
329}
330
331impl Add<u64> for HostOffset {
332    type Output = Self;
333
334    fn add(self, rhs: u64) -> Self {
335        HostOffset(self.0 + rhs)
336    }
337}
338
339impl Sub<u64> for HostOffset {
340    type Output = Self;
341
342    fn sub(self, rhs: u64) -> Self {
343        HostOffset(self.0 - rhs)
344    }
345}
346
347impl Sub<HostOffset> for HostOffset {
348    type Output = u64;
349
350    fn sub(self, rhs: Self) -> u64 {
351        self.0 - rhs.0
352    }
353}
354
355impl Display for GuestOffset {
356    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
357        write!(f, "0x{:x}", self.0)
358    }
359}
360
361impl Display for HostOffset {
362    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
363        write!(f, "0x{:x}", self.0)
364    }
365}
366
367impl Display for ClusterCount {
368    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
369        write!(f, "{}", self.0)
370    }
371}