fuels_types/
unresolved_bytes.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use crate::constants::WORD_SIZE;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Data {
    // Write the enclosed data immediately.
    Inline(Vec<u8>),
    // The enclosed data should be written somewhere else and only a pointer
    // should be left behind to point to it.
    Dynamic(Vec<Data>),
}

// To get the final encoded bytes, we need to know the address at which these
// bytes are going to be loaded at. Once the address is given to `resolve`
// normal bytes can be retrieved.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct UnresolvedBytes {
    data: Vec<Data>,
}

impl UnresolvedBytes {
    pub fn new(data: Vec<Data>) -> Self {
        Self { data }
    }

    #[allow(clippy::should_implement_trait)]
    pub fn default() -> Self {
        Default::default()
    }

    /// Uses the `start_addr` to resolve any pointers contained within. Once
    /// they are resolved the raw bytes are returned.
    ///
    /// # Arguments
    ///
    /// * `start_addr`: The address at which the encoded bytes are to be loaded
    ///                 in.
    pub fn resolve(&self, start_addr: u64) -> Vec<u8> {
        Self::resolve_data(&self.data, start_addr)
    }

    fn resolve_data(data: &[Data], start_addr: u64) -> Vec<u8> {
        // We must find a place for the dynamic data where it will not bother
        // anyone. Best place for it is immediately after all the inline/normal
        // data is encoded.

        let start_of_dynamic_data = start_addr + Self::amount_of_inline_bytes(data);

        let mut inline_data: Vec<u8> = vec![];
        let mut dynamic_data: Vec<u8> = vec![];
        for chunk in data {
            match chunk {
                Data::Inline(bytes) => inline_data.extend(bytes),
                Data::Dynamic(chunk_of_dynamic_data) => {
                    let ptr_to_next_free_location: u64 =
                        start_of_dynamic_data + dynamic_data.len() as u64;

                    // If this is a vector, its `ptr` will now be encoded, the
                    // `cap` and `len` parts should follow as two Data::Inline
                    // chunks.
                    inline_data.extend(ptr_to_next_free_location.to_be_bytes().to_vec());

                    // The dynamic data could have had more dynamic data inside
                    // of it -- think of a Vec<Vec<...>>. Hence Data::Dynamic
                    // doesn't contain bytes but rather more `Data`.
                    let resolved_dynamic_data =
                        Self::resolve_data(chunk_of_dynamic_data, ptr_to_next_free_location);

                    dynamic_data.extend(resolved_dynamic_data)
                }
            }
        }

        let mut data = inline_data;
        data.extend(dynamic_data);
        data
    }

    fn amount_of_inline_bytes(data: &[Data]) -> u64 {
        data.iter()
            .map(|chunk| match chunk {
                Data::Inline(bytes) => bytes.len(),
                Data::Dynamic(_) => {
                    // Only the ptr is encoded inline
                    WORD_SIZE
                }
            } as u64)
            .sum()
    }
}