diff --git a/tfhe/src/strings/ciphertext.rs b/tfhe/src/strings/ciphertext.rs index 64432930b0..a5e5eaaec1 100644 --- a/tfhe/src/strings/ciphertext.rs +++ b/tfhe/src/strings/ciphertext.rs @@ -175,6 +175,21 @@ impl crate::integer::ciphertext::Expandable for FheString { } } +impl crate::integer::ciphertext::Compressible for FheString { + fn compress_into(self, messages: &mut Vec) -> DataKind { + let n_chars = self.chars().len() as u32; + let padded = self.is_padded(); + + for char in self.enc_string { + for block in char.enc_char.blocks { + messages.push(block); + } + } + + DataKind::String { n_chars, padded } + } +} + #[derive(Clone)] pub enum GenericPattern { Clear(ClearString), diff --git a/tfhe/src/strings/test_functions/mod.rs b/tfhe/src/strings/test_functions/mod.rs index ac0993b532..5937bf56f7 100644 --- a/tfhe/src/strings/test_functions/mod.rs +++ b/tfhe/src/strings/test_functions/mod.rs @@ -1,5 +1,6 @@ mod test_common; mod test_compact; +mod test_compression; mod test_concat; mod test_contains; mod test_find_replace; diff --git a/tfhe/src/strings/test_functions/test_compression.rs b/tfhe/src/strings/test_functions/test_compression.rs new file mode 100644 index 0000000000..94922ea28d --- /dev/null +++ b/tfhe/src/strings/test_functions/test_compression.rs @@ -0,0 +1,58 @@ +use crate::integer::ciphertext::CompressedCiphertextListBuilder; +use crate::integer::{gen_keys, IntegerKeyKind}; +use crate::shortint::parameters::*; +use crate::strings::ciphertext::FheString; +use crate::strings::ClientKey as StringClientKey; +use rand::prelude::*; + +#[test] +fn test_compressed_list_with_strings() { + let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64.into(); + const NB_TESTS: usize = 5; + const MAX_STRING_SIZE: u32 = 255; + const MAX_PADDING_SIZE: u32 = 50; + + let (cks, _) = gen_keys::(params, IntegerKeyKind::Radix); + + let private_compression_key = + cks.new_compression_private_key(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64); + + let (compression_key, decompression_key) = + cks.new_compression_decompression_keys(&private_compression_key); + + let cks = StringClientKey::new(cks); + + let mut rng = rand::thread_rng(); + + let printable_ascii_range = 32..=126u8; // Range of printable chars + + for _ in 0..NB_TESTS { + let len = rng.gen_range(0..MAX_STRING_SIZE); + let ascci_bytes = (0..len) + .map(|_| rng.gen_range(printable_ascii_range.clone())) + .collect::>(); + let clear_string1 = String::from_utf8(ascci_bytes).unwrap(); + let string1 = cks.encrypt_ascii(&clear_string1, None); + + let len = rng.gen_range(0..MAX_STRING_SIZE); + let padding = rng.gen_range(0..MAX_PADDING_SIZE); + let ascci_bytes = (0..len) + .map(|_| rng.gen_range(printable_ascii_range.clone())) + .collect::>(); + let clear_string2 = String::from_utf8(ascci_bytes).unwrap(); + let string2 = cks.encrypt_ascii(&clear_string2, Some(padding)); + + let mut builder = CompressedCiphertextListBuilder::new(); + builder.push(string1); + builder.push(string2); + let compressed = builder.build(&compression_key); + + let s1: FheString = compressed.get(0, &decompression_key).unwrap().unwrap(); + let decrypted = cks.decrypt_ascii(&s1); + assert_eq!(decrypted, clear_string1); + + let s2: FheString = compressed.get(1, &decompression_key).unwrap().unwrap(); + let decrypted = cks.decrypt_ascii(&s2); + assert_eq!(decrypted, clear_string2); + } +}