1mod comp;
2mod no_patterns;
3mod pattern;
4mod trim;
5
6pub use trim::split_ascii_whitespace;
7
8use crate::integer::bigint::static_unsigned::StaticUnsignedBigInt;
9use crate::integer::prelude::*;
10use crate::integer::{BooleanBlock, RadixCiphertext, ServerKey as IntegerServerKey};
11use crate::strings::ciphertext::{num_ascii_blocks, FheAsciiChar, FheString};
12use crate::strings::N;
13use rayon::prelude::*;
14use std::borrow::Borrow;
15use std::cmp::Ordering;
16
17pub struct ServerKey<T>
18where
19 T: Borrow<IntegerServerKey> + Sync,
20{
21 inner: T,
22}
23
24pub type ServerKeyRef<'a> = ServerKey<&'a IntegerServerKey>;
25
26impl<T> ServerKey<T>
27where
28 T: Borrow<IntegerServerKey> + Sync,
29{
30 pub fn inner(&self) -> &IntegerServerKey {
31 self.inner.borrow()
32 }
33
34 pub fn new(inner: T) -> Self {
35 Self { inner }
36 }
37}
38
39pub enum FheStringLen {
42 NoPadding(usize),
43 Padding(RadixCiphertext),
44}
45
46pub enum FheStringIsEmpty {
47 NoPadding(bool),
48 Padding(BooleanBlock),
49}
50
51impl<T: Borrow<IntegerServerKey> + Sync> ServerKey<T> {
53 pub(super) fn num_ascii_blocks(&self) -> usize {
54 let sk = self.inner();
55
56 assert_eq!(sk.message_modulus().0, sk.carry_modulus().0);
57
58 num_ascii_blocks(sk.message_modulus())
59 }
60
61 pub fn trivial_encrypt_ascii(&self, str: &str, padding: Option<u32>) -> FheString {
62 let sk = self.inner.borrow();
63
64 super::ciphertext::trivial_encrypt_ascii(
65 &sk.key,
66 &crate::shortint::ServerKey::create_trivial,
67 str,
68 padding,
69 )
70 }
71
72 fn asciis_eq<'a, I, U>(&self, str: I, pat: U) -> BooleanBlock
75 where
76 I: DoubleEndedIterator<Item = &'a FheAsciiChar>,
77 U: DoubleEndedIterator<Item = &'a FheAsciiChar>,
78 {
79 let sk = self.inner();
80
81 let blocks_str = str
82 .into_iter()
83 .rev()
84 .flat_map(|c| c.ciphertext().blocks().to_owned())
85 .collect();
86
87 let blocks_pat = pat
88 .into_iter()
89 .rev()
90 .flat_map(|c| c.ciphertext().blocks().to_owned())
91 .collect();
92
93 let mut uint_str = RadixCiphertext::from_blocks(blocks_str);
94 let mut uint_pat = RadixCiphertext::from_blocks(blocks_pat);
95
96 self.trim_ciphertexts_lsb(&mut uint_str, &mut uint_pat);
97
98 sk.eq_parallelized(&uint_str, &uint_pat)
99 }
100
101 fn clear_asciis_eq<'a, I>(&self, str: I, pat: &str) -> BooleanBlock
102 where
103 I: DoubleEndedIterator<Item = &'a FheAsciiChar>,
104 {
105 let sk = self.inner();
106
107 let num_blocks = self.num_ascii_blocks();
108
109 let blocks_str: Vec<_> = str
110 .into_iter()
111 .rev()
112 .flat_map(|c| c.ciphertext().blocks().to_owned())
113 .collect();
114 let mut clear_pat = pat;
115
116 let str_block_len = blocks_str.len();
117 let pat_block_len = clear_pat.len() * num_blocks;
118
119 let mut uint_str = RadixCiphertext::from_blocks(blocks_str);
120
121 match str_block_len.cmp(&pat_block_len) {
123 Ordering::Less => {
124 clear_pat = &clear_pat[..str_block_len / num_blocks];
127 }
128 Ordering::Greater => {
129 let diff = str_block_len - pat_block_len;
130 sk.trim_radix_blocks_lsb_assign(&mut uint_str, diff);
131 }
132 Ordering::Equal => (),
133 }
134
135 let clear_pat_uint = self.pad_cipher_and_cleartext_lsb(&mut uint_str, clear_pat);
136
137 sk.scalar_eq_parallelized(&uint_str, clear_pat_uint)
138 }
139
140 fn asciis_eq_ignore_pat_pad<'a, I>(&self, str_pat: I) -> BooleanBlock
141 where
142 I: ParallelIterator<Item = (&'a FheAsciiChar, &'a FheAsciiChar)>,
143 {
144 let sk = self.inner();
145
146 let mut result = sk.create_trivial_boolean_block(true);
147
148 let eq_or_null_pat: Vec<_> = str_pat
149 .map(|(str_char, pat_char)| {
150 let (are_eq, pat_is_null) = rayon::join(
151 || sk.eq_parallelized(str_char.ciphertext(), pat_char.ciphertext()),
152 || sk.scalar_eq_parallelized(pat_char.ciphertext(), 0u8),
153 );
154
155 sk.boolean_bitor(&are_eq, &pat_is_null)
158 })
159 .collect();
160
161 for eq_or_null in eq_or_null_pat {
162 sk.boolean_bitand_assign(&mut result, &eq_or_null);
164 }
165
166 result
167 }
168
169 fn pad_cipher_and_cleartext_lsb(
170 &self,
171 lhs: &mut RadixCiphertext,
172 rhs: &str,
173 ) -> StaticUnsignedBigInt<{ N * 8 / 64 }> {
174 let sk = self.inner();
175
176 let num_blocks = self.num_ascii_blocks();
177
178 let mut rhs_bytes = rhs.as_bytes().to_vec();
179
180 rhs_bytes.resize(N, 0);
183
184 let mut rhs_clear_uint = StaticUnsignedBigInt::<{ N * 8 / 64 }>::from(0u8);
185 rhs_clear_uint.copy_from_be_byte_slice(&rhs_bytes);
186
187 if lhs.blocks().len() < N * num_blocks {
189 let diff = N * num_blocks - lhs.blocks().len();
190 sk.extend_radix_with_trivial_zero_blocks_lsb_assign(lhs, diff);
191 }
192
193 rhs_clear_uint
194 }
195
196 fn pad_ciphertexts_lsb(&self, lhs: &mut RadixCiphertext, rhs: &mut RadixCiphertext) {
197 let sk = self.inner();
198
199 let lhs_blocks = lhs.blocks().len();
200 let rhs_blocks = rhs.blocks().len();
201
202 match lhs_blocks.cmp(&rhs_blocks) {
203 Ordering::Less => {
204 let diff = rhs_blocks - lhs_blocks;
205 sk.extend_radix_with_trivial_zero_blocks_lsb_assign(lhs, diff);
206 }
207 Ordering::Greater => {
208 let diff = lhs_blocks - rhs_blocks;
209 sk.extend_radix_with_trivial_zero_blocks_lsb_assign(rhs, diff);
210 }
211 Ordering::Equal => (),
212 }
213 }
214
215 fn pad_or_trim_ciphertext(&self, cipher: &mut RadixCiphertext, len: usize) {
216 let sk = self.inner();
217
218 let cipher_len = cipher.blocks().len();
219
220 match cipher_len.cmp(&len) {
221 Ordering::Less => {
222 let diff = len - cipher_len;
223 sk.extend_radix_with_trivial_zero_blocks_msb_assign(cipher, diff);
224 }
225 Ordering::Greater => {
226 let diff = cipher_len - len;
227 sk.trim_radix_blocks_msb_assign(cipher, diff);
228 }
229 Ordering::Equal => (),
230 }
231 }
232
233 fn trim_ciphertexts_lsb(&self, lhs: &mut RadixCiphertext, rhs: &mut RadixCiphertext) {
234 let sk = self.inner();
235
236 let lhs_blocks = lhs.blocks().len();
237 let rhs_blocks = rhs.blocks().len();
238
239 match lhs_blocks.cmp(&rhs_blocks) {
240 Ordering::Less => {
241 let diff = rhs_blocks - lhs_blocks;
242 sk.trim_radix_blocks_lsb_assign(rhs, diff);
243 }
244 Ordering::Greater => {
245 let diff = lhs_blocks - rhs_blocks;
246 sk.trim_radix_blocks_lsb_assign(lhs, diff);
247 }
248 Ordering::Equal => (),
249 }
250 }
251
252 fn conditional_string(
253 &self,
254 condition: &BooleanBlock,
255 true_ct: &FheString,
256 false_ct: &FheString,
257 ) -> FheString {
258 let sk = self.inner();
259
260 let mut true_ct = true_ct.clone();
261 let mut false_ct = false_ct.clone();
262
263 self.pad_strings(&mut true_ct, &mut false_ct);
264
265 let true_is_padded = true_ct.is_padded();
266 let false_is_padded = false_ct.is_padded();
267
268 let true_ct_uint = true_ct.into_uint();
269 let false_ct_uint = false_ct.into_uint();
270
271 let result_uint = sk.if_then_else_parallelized(condition, &true_ct_uint, &false_ct_uint);
272
273 let mut result = FheString::from_uint(result_uint, false);
274
275 match (true_is_padded, false_is_padded) {
276 (true, true) => {
277 result.set_is_padded(true);
278 }
279 (true, false) | (false, true) => {
280 result.append_null(self);
283 }
284 (false, false) => {}
285 }
286
287 result
288 }
289
290 fn pad_strings(&self, rhs: &mut FheString, lhs: &mut FheString) {
291 loop {
292 match rhs.len().cmp(&lhs.len()) {
293 Ordering::Less => rhs.append_null(self),
294 Ordering::Equal => break,
295 Ordering::Greater => lhs.append_null(self),
296 }
297 }
298 }
299
300 fn left_shift_chars(&self, str: &FheString, shift: &RadixCiphertext) -> FheString {
301 let sk = self.inner();
302
303 let uint = str.to_uint();
304 let mut shift_bits = sk.scalar_left_shift_parallelized(shift, 3);
305
306 self.pad_or_trim_ciphertext(&mut shift_bits, uint.blocks().len());
308
309 let len = uint.blocks.len();
310
311 let shifted = if len == 0 {
312 uint
313 } else {
314 sk.left_shift_parallelized(&uint, &shift_bits)
315 };
316
317 let bit_len = (str.len() * 8) as u32;
320 let shift_ge_than_str = sk.scalar_ge_parallelized(&shift_bits, bit_len);
321
322 let result = sk.if_then_else_parallelized(
323 &shift_ge_than_str,
324 &sk.create_trivial_zero_radix(len),
325 &shifted,
326 );
327
328 FheString::from_uint(result, false)
329 }
330
331 fn right_shift_chars(&self, str: &FheString, shift: &RadixCiphertext) -> FheString {
332 let sk = self.inner();
333
334 let uint = str.to_uint();
335 let mut shift_bits = sk.scalar_left_shift_parallelized(shift, 3);
336
337 self.pad_or_trim_ciphertext(&mut shift_bits, uint.blocks().len());
339
340 let len = uint.blocks().len();
341
342 let shifted = if len == 0 {
343 uint
344 } else {
345 sk.right_shift_parallelized(&uint, &shift_bits)
346 };
347
348 let bit_len = (str.len() * 8) as u32;
351 let shift_ge_than_str = sk.scalar_ge_parallelized(&shift_bits, bit_len);
352
353 let result = sk.if_then_else_parallelized(
354 &shift_ge_than_str,
355 &sk.create_trivial_zero_radix(len),
356 &shifted,
357 );
358
359 FheString::from_uint(result, false)
360 }
361}
362
363pub trait FheStringIterator<T: Borrow<IntegerServerKey> + Sync> {
364 fn next(&mut self, sk: &ServerKey<T>) -> (FheString, BooleanBlock);
365}