bmail/headers/
layout.rs

1#[derive(Copy, Clone, Debug)]
2struct Break {
3    index: usize,
4    priority: usize,
5    space: bool,
6}
7pub struct HeaderFieldFormatter {
8    max_width: usize,
9    distinct_priorities: usize,
10    // scores[n * distinct_priorities + k]
11    // is the number of breaks of priority k
12    // in the optimal solution for tokens
13    // 0..=n.
14    scores: Vec<usize>,
15    // optim[n] is the index of the
16    // last break (if any) in the optimal solution for
17    // tokens 0..=n.
18    optim: Vec<Option<usize>>,
19    buffer: Vec<u8>,
20    // potential breaks
21    breaks: Vec<Break>,
22}
23
24#[derive(Debug, Clone, Copy)]
25pub struct FormatError;
26
27impl HeaderFieldFormatter {
28    pub fn new(
29        max_width: usize,
30        distinct_priorities: usize,
31        initial_text: &[u8],
32        initial_break_priority: usize,
33        initial_text_space: bool,
34    ) -> Self {
35        let buffer = if initial_text_space {
36            let mut buffer = Vec::with_capacity(initial_text.len() + 1);
37            buffer.extend_from_slice(initial_text);
38            buffer.push(b' ');
39            buffer
40        } else {
41            initial_text.to_vec()
42        };
43        Self {
44            max_width,
45            distinct_priorities,
46            scores: vec![0; distinct_priorities],
47            optim: vec![None],
48            buffer,
49            breaks: vec![Break {
50                index: initial_text.len() + (initial_text_space as usize),
51                priority: initial_break_priority,
52                space: initial_text_space,
53            }],
54        }
55    }
56    pub fn push(&mut self, token: &[u8], priority: usize, space: bool) -> Result<(), FormatError> {
57        if priority >= self.distinct_priorities {
58            panic!()
59        }
60        if token.len() > self.avail(false) {
61            return Err(FormatError);
62        }
63        self.buffer.extend(token);
64        let pre_space_len = self.buffer.len();
65        if space {
66            self.buffer.push(b' ');
67        }
68        if pre_space_len <= self.avail(true) {
69            // We can fit everything on the same line
70            // Make sure there are zeros...
71            self.next_score(self.breaks.len());
72            self.optim.push(None);
73        } else {
74            // We have to break somewhere.
75            // Try all the possible breakpoints starting at the end.
76            // (I.e.: try one token on the last line, then two, then three, and so on,
77            //  until we either reach the beginning of the tokens or we run out of space on the line.)
78            //
79            // The score for each scenario is the optimum score up to the broken token,
80            // plus one in the coordinate of the priority of that token's break.
81            assert!(
82                !self.breaks.is_empty(),
83                "Breaking after the field name is always possible"
84            );
85            // We know we can fit at least one token on this line
86            // (because otherwise we would have already returned
87            //  an error), so initialize "best score"
88            // to breaking after the former last token
89            let mut try_break_idx = self.breaks.len() - 1;
90            let mut best_score = self.score(try_break_idx).to_vec();
91            best_score[self.breaks[try_break_idx].priority] += 1;
92            let mut best_score_break_idx = try_break_idx;
93
94            while try_break_idx > 0 {
95                try_break_idx -= 1;
96                let r#break = self.breaks[try_break_idx];
97                let potential_line_width = pre_space_len - r#break.index;
98                if potential_line_width > self.avail(false) {
99                    break;
100                }
101                let mut score = self.score(try_break_idx).to_vec();
102                score[self.breaks[try_break_idx].priority] += 1;
103                if &score < &best_score {
104                    for i in 0..best_score.len() {
105                        best_score[i] = score[i];
106                    }
107                    best_score_break_idx = try_break_idx;
108                }
109            }
110
111            let result_score = self.next_score(self.breaks.len());
112            for i in 0..best_score.len() {
113                result_score[i] = best_score[i];
114            }
115
116            self.optim.push(Some(best_score_break_idx));
117        }
118        self.breaks.push(Break {
119            index: self.buffer.len(),
120            priority,
121            space,
122        });
123        Ok(())
124    }
125
126    pub fn done(self, ret: &mut Vec<u8>) {
127        let mut cur_break_and_idx = match self.optim.last().unwrap() {
128            Some(break_idx) => Some((self.breaks[*break_idx], *break_idx)),
129            None => {
130                if self.breaks.last().unwrap().space {
131                    ret.extend_from_slice(&self.buffer[0..self.buffer.len() - 1]);
132                    ret.push(b'\r');
133                    ret.push(b'\n');
134                } else {
135                    ret.extend_from_slice(&self.buffer);
136                    ret.push(b'\r');
137                    ret.push(b'\n');
138                };
139                return;
140            }
141        };
142        let mut indices = vec![];
143        let mut to_process_r = self.buffer.len() - self.breaks.last().unwrap().space as usize;
144        while let Some((cur_break, cur_break_idx)) = cur_break_and_idx {
145            let r = to_process_r;
146            let l = cur_break.index;
147            indices.push((l, r));
148            to_process_r = l - cur_break.space as usize;
149            cur_break_and_idx = self.optim[cur_break_idx].map(|idx| (self.breaks[idx], idx));
150        }
151        indices.push((0, to_process_r));
152        let mut first = true;
153        for (l, r) in indices.iter().rev().copied() {
154            if !first {
155                ret.push(b' ');
156            }
157            first = false;
158            ret.extend_from_slice(&self.buffer[l..r]);
159            ret.push(b'\r');
160            ret.push(b'\n');
161        }
162    }
163
164    fn score(&self, n: usize) -> &[usize] {
165        let l = n * self.distinct_priorities;
166        let r = (n + 1) * self.distinct_priorities;
167        &self.scores[l..r]
168    }
169
170    fn next_score(&mut self, n: usize) -> &mut [usize] {
171        assert!(n * self.distinct_priorities == self.scores.len());
172        self.scores.resize((n + 1) * self.distinct_priorities, 0);
173        let l = n * self.distinct_priorities;
174        let r = (n + 1) * self.distinct_priorities;
175        &mut self.scores[l..r]
176    }
177
178    // maximum space available in a line
179    fn avail(&self, is_first: bool) -> usize {
180        self.max_width - ((!is_first) as usize) // to accommodate the space for folding
181    }
182}
183
184// Test script; use as follows:
185// 0!brennan
186// 0!@
187// 0!umanwizard.com
188// 1 ;
189// 0!mary-poppins-supercalifragilisticexpialidocious
190// 0!@
191// 0!mary-poppins-supercalifragilisticexpialidocious.com
192// 1 ;
193// 0!some-normal-address
194// 0!@
195// 0!hotmail.com
196// 1 ;
197// 0!brennan.vincent
198// 0!@
199// 0!gmail.com
200
201#[allow(dead_code)]
202fn main() {
203    use std::io::stdin;
204    use std::io::BufRead;
205
206    let mut hff = HeaderFieldFormatter::new(78, 3, b"To:", 2, true);
207    for line in stdin().lock().lines() {
208        let line = line.unwrap();
209        let chs = line.as_bytes();
210        let mut prio = 0;
211        let mut cursor = 0;
212        while chs[cursor].is_ascii_digit() {
213            prio *= 10;
214            prio += (chs[cursor] - b'0') as usize;
215            cursor += 1;
216        }
217
218        let space = match chs[cursor] {
219            b' ' => true,
220            b'!' => false,
221            _ => panic!("{}", line),
222        };
223
224        hff.push(&chs[cursor + 1..], prio, space).unwrap();
225    }
226    let mut result = vec![];
227    hff.done(&mut result);
228    print!("{}", std::str::from_utf8(&result).unwrap());
229}