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
//! Library for the [kripher][1] file encoder.
//!
//! [1]: https://github.com/marionauta/kripher.rs

pub mod utils;
pub mod options;

/// Calculates how many digits the number 'n' has.
fn num_size(n: u64) -> u64 {
    if n < 10 {
        1
    } else {
        1 + num_size(n / 10)
    }
}

/// Returns the digit at a number's position.
///
/// Given a number and a position, returns the digit that occupies that position
/// in the number. Positions are calculated 'pos % key.length'.
/// Position 0 is the first.
fn calculate_shift(number: u64, size: u64, position: u64) -> u64 {
    let actual_position = (position % size) + 1;
    let exponent = (size - actual_position) as u32;

    (number / 10u64.pow(exponent)) % 10
}

/// Performs operations and encodes a byte (`u8`) into a number (`u64`).
fn encode_byte(c: u8, key: u64, shift: u64) -> u64 {
    let mut nc = c as u64 + shift;

    nc *= key;
    nc += key;
    nc *= key;

    nc - 2 * key
}

/// Performs operations and decodes a number (`u64`) into a byte (`u8`).
fn decode_byte(c: u64, key: u64, shift: u64) -> u8 {
    let mut nc = c;

    nc += 2 * key;
    nc /= key;
    nc -= key;
    nc /= key;

    (nc - shift) as u8
}

/// Encodes a vector of chars (`u8`).
///
/// Uses a custom algorithm, nothing too fancy.
///
/// To decode again, use [`kripher::decode`][1] function.
///
/// [1]: fn.decode.html
///
/// # Examples
///
/// ```
/// use kripher::encode;
///
/// let message = vec![65, 66, 67];
/// let encoded = encode(message, 234);
///
/// assert_eq!(encoded, vec![3722940, 3832452, 3941964]);
/// ```
pub fn encode(characters: Vec<u8>, key: u64) -> Vec<u64> {
    let key_size = num_size(key);

    let shifts = (0..key_size)
        .cycle()
        .map(|i| calculate_shift(key, key_size, i));

    characters.into_iter()
        .zip(shifts)
        .map(|(c, shift)| encode_byte(c, key, shift))
        .collect::<Vec<u64>>()
}

/// Decodes a vector of numbers (`u64`) into a `String`.
///
/// Usually you want to use this function with the output of the
/// [`kripher::encode`][1] function, because there is no other easy way to
/// calculate values that will work.
///
/// [1]: fn.encode.html
///
/// # Examples
///
/// ```
/// use kripher::decode;
///
/// let numbers = vec![3722940, 3832452, 3941964];
/// let decoded = decode(numbers, 234);
///
/// assert_eq!(decoded, "ABC".to_string());
/// ```
pub fn decode(numbers: Vec<u64>, key: u64) -> String {
    let key_size = num_size(key);

    let shifts = (0..key_size)
        .cycle()
        .map(|i| calculate_shift(key, key_size, i));

    let bytes = numbers.into_iter()
        .zip(shifts)
        .map(|(c, shift)| decode_byte(c as u64, key, shift))
        .collect::<Vec<u8>>();

    // Decode into characters taking care of invalid ones.
    String::from_utf8_lossy(&bytes).into_owned()
}

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

    #[test]
    fn test_num_size() {
        assert_eq!(num_size(1234), 4);
        assert_eq!(num_size(345342), 6);
    }

    #[test]
    fn test_calculate_shift() {
        let n = 1235;
        let size = num_size(n);

        assert_eq!(calculate_shift(n, size, 0), 1);
        assert_eq!(calculate_shift(n, size, 7), 5);
    }
}