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
90
91
92
93
94
95
96
97
use super::{ChunkArray, ChunkDesc, DataArray, DataSource, TableArray};
use crate::{Chunk, Identifier, Table};

/// Description of a table.
#[derive(Debug)]
pub struct TableDesc<'a> {
    /// The table identifier.
    identifier: Identifier,
    /// The metadata for the table.
    metadata: Option<DataSource<'a>>,
    /// The chunks attached to the table.
    chunks: Vec<ChunkDesc<'a>>,
    /// The child tables.
    children: Vec<TableDesc<'a>>,
}

impl<'a> TableDesc<'a> {
    /// Create a new table description.
    pub fn new(
        identifier: Identifier,
        metadata: Option<DataSource<'a>>,
        chunks: Vec<ChunkDesc<'a>>,
        children: Vec<TableDesc<'a>>,
    ) -> Self {
        Self {
            identifier,
            metadata,
            chunks,
            children,
        }
    }

    /// Flatten the description into separate portions of tables,
    /// chunks and the data blob.
    pub fn flatten(
        self,
        has_sibling: bool,
        tables: &mut TableArray,
        chunks: &mut ChunkArray,
        data: &mut DataArray<'a>,
    ) {
        // First, record if the table had metadata and push that to the
        // data array if so.
        let had_metadata = if let Some(metadata) = self.metadata {
            data.push(metadata);
            true
        } else {
            false
        };

        // Record the start of the chunks and how many there are.
        let chunk_start = chunks.len();
        let chunk_count = self.chunks.len();

        // Second, push the chunks for this table into the chunk and data arrays.
        for chunk in self.chunks {
            // Push without offset/length, we don't know them at this time.
            chunks.push(Chunk::new(chunk.identifier(), 0, 0));
            data.push(chunk.data_source());
        }

        // Record how many tables there are so we can fix up the sibling
        // after pushing children.
        let table_index = tables.len();

        // Push the table.
        tables.push(
            had_metadata,
            Table::create()
                .identifier(self.identifier)
                .chunk_index(if chunk_count > 0 {
                    chunk_start as u32
                } else {
                    0
                })
                .chunk_count(chunk_count as u32)
                .child_count(self.children.len() as u32)
                .end(),
        );

        // Now, flatten each child in turn.
        let child_count = self.children.len();
        for (index, child) in self.children.into_iter().enumerate() {
            child.flatten(index < child_count - 1, tables, chunks, data);
        }

        // Adjust the sibling index by the number of children added if required.
        if has_sibling {
            // Compute the sibling.
            let sibling = (tables.len() - table_index) as u32;

            // Get the table we pushed.
            let table = &mut tables[table_index];
            *table.1.sibling_mut() = sibling;
        }
    }
}