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
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Debt64

Copyright (C) 2018-2019, 2021-2023  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2018-2019".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Encoder

use {
    alloc::string::String,
    super::{Debt64, PAD_CHAR},
};

/// # Adds a char to a string
///
/// Line column will be updated based on max line length and line separators.
fn add_char(s: &mut String, chr: &char, max_line_len: &usize, line_separators: &Option<&[char]>, line_column: &mut usize) {
    s.push(*chr);

    if *max_line_len == 0 {
        return;
    }

    if let Some(line_separators) = line_separators {
        *line_column = (*line_column).saturating_add(1);
        if *line_column >= *max_line_len {
            for chr in line_separators.iter() {
                s.push(*chr);
            }
            *line_column = 0;
        }
    }
}

/// # Encodes
pub (super) fn encode<B>(bytes: B, debt64: &Debt64) -> String where B: AsRef<[u8]> {
    let bytes = bytes.as_ref();

    let mut result = String::with_capacity(debt64.estimate_encoding_capacity(bytes));

    let max_line_len = debt64.max_line_len().unwrap_or(0);
    let line_separators = debt64.line_separators();
    let mut line_column = 0;

    let must_use_pad = debt64.must_use_pad();
    let last_chars = debt64.last_chars();
    let len = bytes.len();
    let mut index = 0;

    loop {
        // First
        let byte = if index < len { bytes[index] } else { break; };
        add_char(&mut result, Debt64::get_char((byte >> 2) as usize, last_chars), &max_line_len, &line_separators, &mut line_column);
        index += 1;

        // Second
        let bits = (byte << 6) >> 2;
        if index < len {
            let byte = bytes[index];
            index += 1;
            add_char(
                &mut result, Debt64::get_char((bits | (byte >> 4)) as usize, last_chars), &max_line_len, &line_separators, &mut line_column
            );

            // Third
            let bits = (byte << 4) >> 2;
            if index < len {
                let byte = bytes[index];
                index += 1;

                add_char(
                    &mut result, Debt64::get_char((bits | (byte >> 6)) as usize, last_chars), &max_line_len, &line_separators,
                    &mut line_column
                );
                add_char(
                    &mut result, Debt64::get_char(((byte << 2) >> 2) as usize, last_chars), &max_line_len, &line_separators,
                    &mut line_column
                );
            } else {
                add_char(&mut result, Debt64::get_char(bits as usize, last_chars), &max_line_len, &line_separators, &mut line_column);
                if must_use_pad {
                    result.push(PAD_CHAR);
                }
                break;
            }
        } else {
            add_char(&mut result, Debt64::get_char(bits as usize, last_chars), &max_line_len, &line_separators, &mut line_column);
            if must_use_pad {
                for _ in 0..2 {
                    add_char(&mut result, &PAD_CHAR, &max_line_len, &line_separators, &mut line_column);
                }
            }
            break;
        }
    }

    result
}