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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::serde::{Datum, DatumBodyLen, DatumType, DatumTypeInt, OptDatum, TupleMembersCount};
use anyhow::Result;
use derive_more::Deref;
use std::io::Write;
use std::mem;

#[derive(Deref)]
pub struct WriteLen(usize);
impl WriteLen {
    pub fn new_manual(i: usize) -> Self {
        Self(i)
    }
}

impl OptDatum<Datum> {
    pub fn ser(&self, w: &mut impl Write) -> Result<WriteLen> {
        match self {
            OptDatum::Tombstone => {
                let dtype = DatumTypeInt::from(DatumType::Tombstone);
                let w_len = w.write(&dtype.to_le_bytes())?;
                Ok(WriteLen(w_len))
            }
            OptDatum::Some(datum) => datum.ser(w),
        }
    }
}

impl Datum {
    pub fn ser(&self, w: &mut impl Write) -> Result<WriteLen> {
        self.ser_::<true>(w)
    }

    fn ser_<const IS_ROOT: bool>(&self, w: &mut impl Write) -> Result<WriteLen> {
        let mut w_len = WriteLen(0);

        /* datum_type */
        let dtype = DatumType::from(self);
        let dtype = DatumTypeInt::from(dtype);
        w_len.0 += w.write(&dtype.to_le_bytes())?;

        /* datum_body_len */
        let dbody_len = match self {
            Datum::I64(_) => None,
            Datum::Bytes(b) => Some(DatumBodyLen::from_dynalen_body(b)?),
            Datum::Str(s) => Some(DatumBodyLen::from_dynalen_body(s.as_bytes())?),
            Datum::Tuple(_) => {
                if IS_ROOT {
                    Some(self.intra_tuple_datum_len::<true>()?)
                } else {
                    None
                }
            }
        };
        if let Some(datum_body_len) = dbody_len {
            w_len.0 += w.write(&datum_body_len.to_le_bytes())?;
        }

        /* datum_body */
        match self {
            Datum::I64(i) => w_len.0 += w.write(&i.to_le_bytes())?,
            Datum::Bytes(b) => w_len.0 += w.write(b)?,
            Datum::Str(s) => w_len.0 += w.write(s.as_bytes())?,
            Datum::Tuple(members) => {
                /* members_count */
                let membs_ct = TupleMembersCount::from_members(members)?;
                w_len.0 += w.write(&membs_ct.to_le_bytes())?;

                /* members */
                for member in members {
                    w_len.0 += member.ser_::<false>(w)?.0;
                }
            }
        }

        Ok(w_len)
    }

    fn intra_tuple_datum_len<const IS_ROOT: bool>(&self) -> Result<DatumBodyLen> {
        /* datum_type's len */
        let dtype_len = match self {
            Datum::I64(_) | Datum::Bytes(_) | Datum::Str(_) => mem::size_of::<DatumTypeInt>(),
            Datum::Tuple(_) => {
                if IS_ROOT {
                    0
                } else {
                    mem::size_of::<DatumTypeInt>()
                }
            }
        };

        /* datum_body_len's len */
        let dbody_len_len = match self {
            Datum::I64(_) => 0,
            Datum::Bytes(_) | Datum::Str(_) => mem::size_of::<DatumBodyLen>(),
            Datum::Tuple(_) => 0,
        };

        /* datum_body's len */
        let dbody_len = match self {
            Datum::I64(i) => mem::size_of_val(i),
            Datum::Bytes(b) => b.len(),
            Datum::Str(s) => s.as_bytes().len(),
            Datum::Tuple(members) => {
                /* members_count's len */
                let mut root_body_len = mem::size_of::<TupleMembersCount>();

                /* members' lens */
                for memb in members {
                    let memb_body_len = memb.intra_tuple_datum_len::<false>()?;
                    root_body_len += *memb_body_len as usize;
                }

                root_body_len
            }
        };

        /* total */
        let tot = dtype_len + dbody_len_len + dbody_len;
        let tot = u32::try_from(tot)?;
        Ok(DatumBodyLen::new_manual(tot))
    }
}