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
//! # Description
//!
//! This method encodes n-bits using available set of unicode whitespace. Larger sets provide better capacity
//! but might not work for every channel - it depends which characters get sanitized or are actually used.
//!
//! The whitespace are added at the end of the line as some of them are actually visible and might
//! lower the imperceptibility of this method.
//!
//! The amount of bits encoded by this implementation depends on used Unicode character set.
//! It it at most 5 bits per line with [FULL_UNICODE_CHARACTER_SET].
//!
//! Encoder does not inform if there is not data left!
pub mod character_sets;

use std::error::Error;

use crate::{
    binary::BitVec,
    context::{Context, ContextError},
    decoder::Decoder,
    encoder::{Capacity, Encoder, EncoderResult},
};
use log::trace;

use crate::binary::Bit;

use self::character_sets::{CharacterSetType, GetCharacterSet};

use super::Method;

/// Trailing unicode encoder for generic Unicode character sets.
/// It uses the [UnicodeSet] to get the character given the n-bits
/// (where n is the binary logarithm of the set size).
///
/// Accepts any [Context](crate::context::Context).
#[derive(Debug, PartialEq)]
pub struct TrailingUnicodeMethod {
    character_set: CharacterSetType,
}

impl Default for TrailingUnicodeMethod {
    fn default() -> Self {
        Self::new(CharacterSetType::FullUnicodeSet)
    }
}

impl TrailingUnicodeMethod {
    pub fn new(unicode_set: CharacterSetType) -> Self {
        TrailingUnicodeMethod {
            character_set: unicode_set,
        }
    }
}

impl Capacity for TrailingUnicodeMethod {
    fn bitrate(&self) -> usize {
        let amount_of_bits = std::mem::size_of::<usize>() * 8;
        amount_of_bits - self.character_set.size().leading_zeros() as usize
    }
}

impl<E> Encoder<E> for TrailingUnicodeMethod
where
    E: Context,
{
    fn partial_encode(
        &self,
        context: &mut E,
        data: &mut dyn Iterator<Item = Bit>,
    ) -> Result<EncoderResult, Box<dyn Error>> {
        let set_capacity = self.bitrate();
        let next_n_bits = data.take(set_capacity).collect::<Vec<Bit>>();
        // We might not take exactly 5 bits, lets ensure we properly pad with 0 bits
        let amount_bits_taken = next_n_bits.len();
        let mut number: u32 = BitVec::from(next_n_bits).into();
        number <<= set_capacity - amount_bits_taken;

        trace!(
            "Took {} bits and assembled a number: {}",
            set_capacity,
            number
        );
        if let Some(character) = self.character_set.get_character(number) {
            trace!(
                "Putting unicode character {:?} at the end of the line",
                character
            );
            context.get_current_text_mut()?.push(*character);
        }

        Ok(EncoderResult::Success)
    }
}

impl<D> Decoder<D> for TrailingUnicodeMethod
where
    D: Context,
{
    fn partial_decode(&self, context: &D) -> Result<Vec<Bit>, ContextError> {
        if let Some(character) = context.get_current_text()?.chars().last() {
            let decoded_number = self.character_set.character_to_bits(&character);
            trace!(
                "Found {:?} at the end of the line, decoded into {}",
                &character,
                decoded_number
            );
            let data: Vec<Bit> = BitVec::from(decoded_number).into();
            let data_length = data.len();
            // Skip the unnecessary zeroes from the beginning
            let data_iter = data.into_iter().skip(data_length - self.bitrate());
            let decoded_data = data_iter.collect::<Vec<Bit>>();
            return Ok(decoded_data);
        }

        Ok(BitVec::filled_with(0, self.bitrate()).into())
    }
}

impl<E, D> Method<E, D> for TrailingUnicodeMethod
where
    E: Context,
    D: Context,
{
    fn method_name(&self) -> String {
        "TrailingUnicodeMethod".to_string()
    }
}