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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// LNP/BP Core Library implementing LNPBP specifications & standards
// Written in 2020 by
//     Julian Knutsen
//     Rajarshi Maitra
//     Arik Sosman
//     Matt Corallo
//     Antoine Riard
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the MIT License
// along with this software.
// If not, see <https://opensource.org/licenses/MIT>.

use std::{cmp, ops};

pub const ACT_ONE_LENGTH: usize = 50;
pub const ACT_TWO_LENGTH: usize = 50;
pub const ACT_THREE_LENGTH: usize = 66;
pub const EMPTY_ACT_ONE: ActOne = [0; ACT_ONE_LENGTH];
pub const EMPTY_ACT_TWO: ActTwo = [0; ACT_TWO_LENGTH];
pub const EMPTY_ACT_THREE: ActThree = [0; ACT_THREE_LENGTH];
type ActOne = [u8; ACT_ONE_LENGTH];
type ActTwo = [u8; ACT_TWO_LENGTH];
type ActThree = [u8; ACT_THREE_LENGTH];

/// Wrapper for any act message
#[derive(Copy, Clone, Debug)]
pub enum Act {
    One(ActOne),
    Two(ActTwo),
    Three(ActThree),
}

impl Act {
    /// Returns the size of the underlying array
    fn len(&self) -> usize { self.as_ref().len() }
}

impl From<ActBuilder> for Act {
    /// Convert a finished ActBuilder into an Act
    fn from(act_builder: ActBuilder) -> Self {
        assert!(act_builder.is_finished());
        act_builder.partial_act
    }
}

impl ops::Deref for Act {
    type Target = [u8];

    /// Allows automatic coercion to slices in function calls
    /// &Act -> &[u8]
    fn deref(&self) -> &Self::Target {
        match self {
            Act::One(ref act) => act,
            Act::Two(ref act) => act,
            Act::Three(ref act) => act,
        }
    }
}

impl AsRef<[u8]> for Act {
    /// Allow convenient exposure of the underlying array through as_ref()
    /// Act.as_ref() -> &[u8]
    fn as_ref(&self) -> &[u8] { self }
}

/// Light wrapper around an Act that allows multiple fill() calls before finally
/// converting to an Act via Act::from(act_builder). Handles all of the
/// bookkeeping and edge cases of the array fill
#[derive(Debug)]
pub struct ActBuilder {
    partial_act: Act,
    write_pos: usize,
}

impl ActBuilder {
    /// Returns a new ActBuilder for Act::One
    pub fn new(empty_act: Act) -> Self {
        Self {
            partial_act: empty_act,
            write_pos: 0,
        }
    }

    /// Fills the Act with bytes from input and returns the number of bytes
    /// consumed from input.
    pub fn fill(&mut self, input: &[u8]) -> usize {
        // Simple fill implementation for both almost-identical structs to
        // deduplicate code $act: Act[One|Two|Three], $input: &[u8];
        // returns &[u8] of remaining input that was not processed
        macro_rules! fill_act_content {
            ($act:expr, $write_pos:expr, $input:expr) => {{
                let fill_amount =
                    cmp::min($act.len() - $write_pos, $input.len());

                $act[$write_pos..$write_pos + fill_amount]
                    .copy_from_slice(&$input[..fill_amount]);

                $write_pos += fill_amount;
                fill_amount
            }};
        }

        match self.partial_act {
            Act::One(ref mut act) => {
                fill_act_content!(act, self.write_pos, input)
            }
            Act::Two(ref mut act) => {
                fill_act_content!(act, self.write_pos, input)
            }
            Act::Three(ref mut act) => {
                fill_act_content!(act, self.write_pos, input)
            }
        }
    }

    /// Returns true if the Act is finished building (enough bytes via fill())
    pub fn is_finished(&self) -> bool {
        self.write_pos == self.partial_act.len()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Test bookkeeping of partial fill
    #[test]
    fn partial_fill() {
        let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE));

        let input = [1, 2, 3];
        let bytes_read = builder.fill(&input);
        assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH);
        assert_eq!(builder.write_pos, 3);
        assert!(!builder.is_finished());
        assert_eq!(bytes_read, input.len());
    }

    // Test bookkeeping of exact fill
    #[test]
    fn exact_fill() {
        let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE));

        let input = [0; ACT_ONE_LENGTH];
        let bytes_read = builder.fill(&input);
        assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH);
        assert_eq!(builder.write_pos, ACT_ONE_LENGTH);
        assert!(builder.is_finished());
        assert_eq!(Act::from(builder).as_ref(), &input[..]);
        assert_eq!(bytes_read, input.len());
    }

    // Test bookkeeping of overfill
    #[test]
    fn over_fill() {
        let mut builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE));

        let input = [0; ACT_ONE_LENGTH + 1];
        let bytes_read = builder.fill(&input);

        assert_eq!(builder.partial_act.len(), ACT_ONE_LENGTH);
        assert_eq!(builder.write_pos, ACT_ONE_LENGTH);
        assert!(builder.is_finished());
        assert_eq!(Act::from(builder).as_ref(), &input[..ACT_ONE_LENGTH]);
        assert_eq!(bytes_read, ACT_ONE_LENGTH);
    }

    // Converting an unfinished ActBuilder panics
    #[test]
    #[should_panic(expected = "assertion failed: act_builder.is_finished()")]
    fn convert_not_finished_panics() {
        let builder = ActBuilder::new(Act::One(EMPTY_ACT_ONE));
        let _should_panic = Act::from(builder);
    }
}