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
98
99
100
101
102
103
104
105
106
107
use crate::{decoder::PhysicalPageScheduler, format::pb};

use self::{
    basic::BasicPageScheduler, bitmap::DenseBitmapScheduler, fixed_size_list::FixedListScheduler,
    value::ValuePageScheduler,
};

pub mod basic;
pub mod bitmap;
pub mod fixed_size_list;
pub mod value;

/// These contain the file buffers shared across the entire file
#[derive(Clone, Copy, Debug)]
pub struct FileBuffers<'a> {
    pub positions: &'a Vec<u64>,
}

/// These contain the file buffers and also buffers specific to a column
#[derive(Clone, Copy, Debug)]
pub struct ColumnBuffers<'a, 'b> {
    pub file_buffers: FileBuffers<'a>,
    pub positions: &'b Vec<u64>,
}
/// These contain the file & column buffers and also buffers specific to a page
#[derive(Clone, Copy, Debug)]
pub struct PageBuffers<'a, 'b, 'c> {
    pub column_buffers: ColumnBuffers<'a, 'b>,
    pub positions: &'c Vec<u64>,
}

// Translate a protobuf buffer description into a position in the file.  This could be a page
// buffer, a column buffer, or a file buffer.
fn get_buffer(buffer_desc: &pb::Buffer, buffers: &PageBuffers) -> u64 {
    match pb::buffer::BufferType::try_from(buffer_desc.buffer_type).unwrap() {
        pb::buffer::BufferType::Page => buffers.positions[buffer_desc.buffer_index as usize],
        pb::buffer::BufferType::Column => {
            buffers.column_buffers.positions[buffer_desc.buffer_index as usize]
        }
        pb::buffer::BufferType::File => {
            buffers.column_buffers.file_buffers.positions[buffer_desc.buffer_index as usize]
        }
    }
}

/// Convert a protobuf buffer encoding into a physical page scheduler
fn decoder_from_buffer_encoding(
    encoding: &pb::BufferEncoding,
    buffers: &PageBuffers,
) -> Box<dyn PhysicalPageScheduler> {
    match encoding.buffer_encoding.as_ref().unwrap() {
        pb::buffer_encoding::BufferEncoding::Value(value) => Box::new(ValuePageScheduler::new(
            value.bytes_per_value,
            get_buffer(value.buffer.as_ref().unwrap(), buffers),
        )),
        pb::buffer_encoding::BufferEncoding::Bitmap(bitmap) => Box::new(DenseBitmapScheduler::new(
            get_buffer(bitmap.buffer.as_ref().unwrap(), buffers),
        )),
    }
}

/// Convert a protobuf array encoding into a physical page scheduler
pub fn decoder_from_array_encoding(
    encoding: &pb::ArrayEncoding,
    buffers: &PageBuffers,
) -> Box<dyn PhysicalPageScheduler> {
    match encoding.array_encoding.as_ref().unwrap() {
        pb::array_encoding::ArrayEncoding::Basic(basic) => {
            match basic.nullability.as_ref().unwrap() {
                pb::basic::Nullability::NoNulls(no_nulls) => {
                    Box::new(BasicPageScheduler::new_non_nullable(
                        decoder_from_buffer_encoding(no_nulls.values.as_ref().unwrap(), buffers),
                    ))
                }
                pb::basic::Nullability::SomeNulls(some_nulls) => {
                    Box::new(BasicPageScheduler::new_nullable(
                        decoder_from_buffer_encoding(
                            some_nulls.validity.as_ref().unwrap(),
                            buffers,
                        ),
                        decoder_from_buffer_encoding(some_nulls.values.as_ref().unwrap(), buffers),
                    ))
                }
                pb::basic::Nullability::AllNulls(_) => todo!(),
            }
        }
        pb::array_encoding::ArrayEncoding::FixedSizeList(fixed_size_list) => {
            let item_encoding = fixed_size_list.items.as_ref().unwrap();
            let item_scheduler = decoder_from_array_encoding(item_encoding, buffers);
            Box::new(FixedListScheduler::new(
                item_scheduler,
                fixed_size_list.dimension,
            ))
        }
        // This is a column containing the list offsets.  This wrapper is superfluous at the moment
        // since we know it is a list based on the schema.  In the future there may be different ways
        // of storing the list offsets.
        pb::array_encoding::ArrayEncoding::List(list) => {
            decoder_from_array_encoding(list.offsets.as_ref().unwrap(), buffers)
        }
        // Currently there is no way to encode struct nullability and structs are encoded with a "header" column
        // (that has no data).  We never actually decode that column and so this branch is never actually encountered.
        //
        // This will change in the future when we add support for struct nullability.
        pb::array_encoding::ArrayEncoding::Struct(_) => unreachable!(),
    }
}