ptero/method/
random_whitespace.rs

1//! # Description
2//!
3//! This method puts [ASCII_WHITESPACE](crate::method::random_whitespace::ASCII_WHITESPACE) between randomly selected two words.
4//! If the duplicate whitespace is present the bit 1 is encoded, otherwise 0.
5use std::error::Error;
6
7use crate::{binary::Bit, context::{Context, ContextError}, decoder::Decoder, encoder::{Capacity, Encoder, EncoderResult}};
8
9use log::{trace};
10use rand::{thread_rng, Rng};
11
12/// Character used as the random whitespace in the method.
13pub const ASCII_WHITESPACE: char = ' ';
14
15use super::Method;
16
17/// Unit structure representing the random whitespace method.
18/// Implements both [Encoder](crate::encoder::Encoder) and [Decoder](crate::decoder::Decoder) traits.
19///
20/// Accepts any [Context](crate::context::Context).
21#[derive(Debug, PartialEq)]
22pub struct RandomWhitespaceMethod;
23
24impl Default for RandomWhitespaceMethod {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl RandomWhitespaceMethod {
31    pub fn new() -> Self {
32        RandomWhitespaceMethod {}
33    }
34}
35
36impl Capacity for RandomWhitespaceMethod {
37    fn bitrate(&self) -> usize {
38        1
39    }
40}
41
42impl<T> Encoder<T> for RandomWhitespaceMethod
43where
44    T: Context,
45{
46    fn partial_encode(
47        &self,
48        context: &mut T,
49        data: &mut dyn Iterator<Item = Bit>,
50    ) -> Result<EncoderResult, Box<dyn Error>> {
51        Ok(match data.next() {
52            Some(Bit(1)) => {
53                let mut rng = thread_rng();
54                let text = context.get_current_text_mut()?;
55                let position_determinant = rng.gen_range(0, &text.len());
56                let mut position = text.find(' ').unwrap_or_else(|| text.len());
57                for (index, character) in text.char_indices() {
58                    if index > position_determinant {
59                        break;
60                    }
61                    if character.is_whitespace() {
62                        position = index;
63                    }
64                }
65                trace!("Putting space at position {}", position);
66                text.insert_str(position, &String::from(ASCII_WHITESPACE));
67                EncoderResult::Success
68            }
69            None => EncoderResult::NoDataLeft,
70            _ => {
71                trace!("Skipping double whitespace");
72                EncoderResult::Success
73            }
74        })
75    }
76}
77
78impl<D> Decoder<D> for RandomWhitespaceMethod
79where
80    D: Context,
81{
82    fn partial_decode(&self, context: &D) -> Result<Vec<Bit>, ContextError> {
83        let mut seen_whitespace = false;
84        for character in context.get_current_text()?.chars() {
85            let is_whitespace = character == ASCII_WHITESPACE;
86            if seen_whitespace && is_whitespace {
87                trace!("Found two consecutive '{}' between words", ASCII_WHITESPACE,);
88                return Ok(vec![Bit(1)]);
89            }
90            seen_whitespace = is_whitespace;
91        }
92        trace!("No double whitespace found");
93        Ok(vec![Bit(0)])
94    }
95}
96
97impl<E, D> Method<E, D> for RandomWhitespaceMethod
98where
99    E: Context,
100    D: Context,
101{
102    fn method_name(&self) -> String {
103        "RandomWhitespaceMethod".to_string()
104    }
105}