1use std::convert::TryInto;
34
35#[derive(Debug)]
36pub enum Error {
37 BadChecksum,
38 CopyExceedsSize,
39 CopyExtendsPasteEndOfInput,
40 UnexpectedCharacter(u8),
41}
42
43const NHASH: usize = 16;
44pub fn b64str(n: u32) -> String {
46 if n == 0 {
47 String::from("0")
48 } else {
49 let mut res = String::new();
50 let mut _n = n;
51 while _n > 0 {
52 res.insert(0, B64DIGITS[(_n & 63) as usize]);
53 _n = _n >> 6;
54 }
55 res
56 }
57}
58
59pub fn b64int<T: AsRef<[u8]> + ?Sized>(a: &T) -> u32 {
61 b64int_read(a.as_ref()).0 as u32
62}
63
64pub fn b64int_read<T: AsRef<[u8]> + ?Sized>(a: &T) -> (usize, &[u8]) {
65 let mut res = 0_usize;
66 for (j, i) in a.as_ref().iter().enumerate() {
67 let k = B64VALUES[(i & 127) as usize];
68 if k == 255 {
69 let a = a.as_ref();
70 return (res, &a[j..]);
71 }
72 res = (res << 6) + (k as usize);
73 }
74 (res, b"")
75}
76
77const B64DIGITS: [char; 64] = [
78 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
79 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', 'a',
80 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
81 'u', 'v', 'w', 'x', 'y', 'z', '~',
82];
83const B64VALUES: [u8; 128] = [
84 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8,
85 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8,
86 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8,
87 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 0u8, 1u8, 2u8, 3u8, 4u8, 5u8,
88 6u8, 7u8, 8u8, 9u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 10u8, 11u8, 12u8, 13u8,
89 14u8, 15u8, 16u8, 17u8, 18u8, 19u8, 20u8, 21u8, 22u8, 23u8, 24u8, 25u8, 26u8, 27u8, 28u8, 29u8,
90 30u8, 31u8, 32u8, 33u8, 34u8, 35u8, 255u8, 255u8, 255u8, 255u8, 36u8, 255u8, 37u8, 38u8, 39u8,
91 40u8, 41u8, 42u8, 43u8, 44u8, 45u8, 46u8, 47u8, 48u8, 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8,
92 56u8, 57u8, 58u8, 59u8, 60u8, 61u8, 62u8, 255u8, 255u8, 255u8, 63u8, 255u8,
93];
94
95pub fn digit_count(v: usize) -> usize {
97 let mut x = 64;
98 for i in 1..10 {
99 if x > v {
100 return i;
101 };
102 x = x << 6;
103 }
104 11
105}
106
107fn checksum<T: AsRef<[u8]>>(z_in: T) -> u32 {
112 let it = z_in.as_ref().chunks_exact(4);
113 let b = it.remainder();
114 let a_b: [u8; 4] = match b.len() {
115 0 => [0, 0, 0, 0],
116 1 => [b[0], 0, 0, 0],
117 2 => [b[0], b[1], 0, 0],
118 _ => [b[0], b[1], b[2], 0],
119 };
120 let mut s: u32 = u32::from_be_bytes(a_b);
121 for b in it {
122 let a_b: &[u8; 4] = unsafe { &*(b.as_ptr() as *const [u8; 4]) };
123 let a = u32::from_be_bytes(*a_b);
124 s = s.overflowing_add(a).0;
125 }
126 s
127}
128pub fn generate_delta<T: AsRef<[u8]>, V: AsRef<[u8]>>(
182 z_out_t: T, z_src_t: V, z_delta: &mut Vec<u8>, ) {
186 z_delta.clear();
187 let z_src = z_src_t.as_ref();
188 let z_out = z_out_t.as_ref();
189 let mb_backward = |i, j, n| {
191 if i == 0 || j <= n {
192 return 0;
193 }
194 let mut k = i - 1;
195 let mut m = j - 1;
196 let n_1 = if n == 0 { 0 } else { n - 1 };
197 while k > 0 && m > n_1 {
198 if z_src[k] != z_out[m] {
199 return i - k - 1;
200 }
201 k -= 1;
202 m -= 1;
203 }
204 i - k - 1
205 };
206 let mb_forward = |i, j| {
208 let mut k = i + 1;
209 let mut m = j + 1;
210 while k < z_src.len() && m < z_out.len() {
211 if z_src[k] != z_out[m] {
212 return k - i - 1;
213 }
214 k += 1;
215 m += 1;
216 }
217 if z_src.len() - i < z_out.len() - j {
218 z_src.len() - i - 1
219 } else {
220 z_out.len() - j - 1
221 }
222 };
223 z_delta.extend_from_slice(b64str(z_out.len() as u32).as_bytes());
224 z_delta.push(b'\n');
225
226 if z_src.len() <= NHASH {
231 z_delta.extend_from_slice(b64str(z_out.len() as u32).as_bytes());
232 z_delta.push(b':');
233 z_delta.extend_from_slice(z_out);
234 z_delta.extend_from_slice(b64str(checksum(&z_out)).as_bytes());
235 z_delta.push(b';');
236 return;
237 }
238 let n_hash = z_src.len() / NHASH;
242 let mut collide = vec![0xffff_ffff_u32; 2 * n_hash];
243 let mut h = Hash::new();
244 for i in 0..n_hash {
245 h.init(&z_src[(NHASH * i)..]);
246 let hv = h.as_usize() % n_hash + n_hash;
247 collide[i] = collide[hv];
248 collide[hv] = i as u32;
249 }
250 let mut base = 0usize;
251 while base + NHASH < z_out.len() {
252 let mut i = 0;
253 h.init(&z_out[base..]);
254 let mut best_count = 0;
255 let mut best_offset = 0;
256 let mut best_lit_size = 0;
257 loop {
258 let hv = h.as_usize() % n_hash;
259 let mut i_block = collide[n_hash + hv];
260 let mut limit = 250;
261 while i_block != 0xffff_ffff && limit > 0 {
262 limit -= 1;
263 let i_src = (i_block as usize) * NHASH;
264 if z_src[i_src] == z_out[base + i] {
265 let j = mb_forward(i_src, base + i);
266 let k = mb_backward(i_src, base + i, base);
267 let ofst = i_src - k;
268 let cnt = j + k + 1;
269 let litsz = i - k;
270 let sz = digit_count(litsz) + digit_count(cnt) + digit_count(ofst) + 3;
271 if cnt > sz && cnt > best_count {
272 best_count = cnt;
273 best_offset = ofst;
274 best_lit_size = litsz;
275 }
276 }
277 i_block = collide[i_block as usize];
278 }
279 if best_count > 0 {
280 if best_lit_size > 0 {
281 z_delta.extend_from_slice(b64str(best_lit_size as u32).as_bytes());
282 z_delta.push(b':');
283 z_delta.extend_from_slice(&z_out[base..(base + best_lit_size)]);
284 base += best_lit_size;
285 }
286 base += best_count;
287 z_delta.extend_from_slice(b64str(best_count as u32).as_bytes());
288 z_delta.push(b'@');
289 z_delta.extend_from_slice(b64str(best_offset as u32).as_bytes());
290 z_delta.push(b',');
291 break;
292 } else if base + i + NHASH >= z_out.len() {
293 z_delta.extend_from_slice(b64str((z_out.len() - base) as u32).as_bytes());
294 z_delta.push(b':');
295 z_delta.extend_from_slice(&z_out[base..]);
296 base = z_out.len();
297 break;
298 } else {
299 h.update(z_out[base + NHASH + i]);
300 i += 1;
301 }
302 }
303 }
304 if base < z_out.len() {
305 z_delta.extend_from_slice(b64str((z_out.len() - base) as u32).as_bytes());
306 z_delta.push(b':');
307 z_delta.extend_from_slice(&z_out[base..]);
308 }
309 z_delta.extend_from_slice(b64str(checksum(z_out)).as_bytes());
310 z_delta.push(b';');
311}
312
313pub fn delta<T: AsRef<[u8]>, V: AsRef<[u8]>>(a: T, b: V) -> Vec<u8> {
316 let mut d = Vec::with_capacity(b.as_ref().len() + 60);
317 generate_delta(a, b, &mut d);
318 d
319}
320
321pub fn apply<T: AsRef<[u8]>, V: AsRef<[u8]>>(source: T, delta: V) -> Result<Vec<u8>, Error> {
322 let source = source.as_ref();
323 let mut total = 0;
324 let source_length = source.len();
325 let (total_length, mut delta) = b64int_read(&delta);
326 let mut output = Vec::with_capacity(total_length);
327
328 delta = &delta[1..];
329 while delta.len() > 0 {
330 let (cnt, delta_read) = b64int_read(delta);
331 match delta_read[0] {
332 b'@' => {
333 total += cnt;
334 let (offset, delta_read) = b64int_read(&delta_read[1..]);
335 if total > total_length {
336 return Err(Error::CopyExceedsSize);
337 }
338 if offset + cnt > source_length {
339 return Err(Error::CopyExtendsPasteEndOfInput);
340 }
341 output.extend_from_slice(&source[offset..(offset + cnt)]);
342 delta = &delta_read[1..];
343 }
344 b':'=> {
345 total += cnt;
346 let i = delta.len() - delta_read.len() + 1;
347 if total > total_length {
348 return Err(Error::CopyExceedsSize);
349 }
350 output.extend_from_slice(&delta[i..(cnt + i)]);
351 delta = &delta_read[(1 + cnt)..];
352 }
353 b';' => {
354 if cnt != checksum(&output).try_into().unwrap() {
355 return Err(Error::BadChecksum);
356 }
357 return Ok(output);
358 }
359 c => {
360 return Err(Error::UnexpectedCharacter(c));
361 }
362 }
363 }
364
365 Ok(output)
366}
367
368pub fn delta_output_size<T: AsRef<[u8]>>(z_delta: T) -> usize {
377 b64int(&z_delta) as usize
378}
379
380pub fn deltainv<T: AsRef<[u8]>, V: AsRef<[u8]>>(b_txt: T, d_txt: V) -> Vec<u8> {
383 let (total_length, mut d_src) = b64int_read(&d_txt);
384
385 let mut a_res = Vec::with_capacity(total_length);
386 let b_txt = b_txt.as_ref();
387 let b_bytes = b_txt;
388 let d_txt = d_txt.as_ref();
389 d_src = &d_src[1..];
390 while d_src.len() > 0 {
391 let (cnt, d1_src) = b64int_read(d_src);
392 match d1_src[0] {
393 b'@' => {
394 let (ofst, d1_src) = b64int_read(&d1_src[1..]);
395 a_res.extend_from_slice(&b_bytes[ofst..(ofst + cnt)]);
396 d_src = &d1_src[1..];
397 }
398 b':' => {
399 let i = d_txt.len() - d1_src.len() + 1;
400 a_res.extend_from_slice(&d_txt[i..(cnt + i)]);
401 d_src = &d1_src[(1 + cnt)..];
402 }
403 b';' => return a_res,
404 _ => {
405 let msg = format!(
406 r#"Error in applying delta
407 txt: {:?}
408 -----------------------------
409 delta: {:?}
410 =============================
411 index: {}
412 "#,
413 b_txt,
414 d_txt,
415 d_txt.len() - d1_src.len()
416 );
417 panic!("{}", msg)
418 }
419 }
420 }
421 a_res
422}
423
424const NHASH_1: usize = NHASH - 1;
425const NHASHI32: i32 = NHASH as i32;
426struct Hash {
427 a: u16,
428 b: u16,
429 i: usize,
430 z: [u8; NHASH],
431}
432impl Hash {
433 fn new() -> Self {
434 Hash {
435 a: 0,
436 b: 0,
437 i: 0,
438 z: [0; NHASH],
439 }
440 }
441 fn init(&mut self, z: &[u8]) {
443 let mut a = z[0] as u32;
444 let mut b = z[0] as u32;
445 self.z[0] = z[0];
446 for i in 1..NHASH {
447 a = (a + (z[i] as u32)) & 0xffff;
448 b = (b + a) & 0xffff;
449 self.z[i] = z[i];
450 }
451 self.a = a as u16;
452 self.b = b as u16;
453 self.i = 0;
454 }
455 fn update(&mut self, c: u8) {
457 let old = self.z[self.i];
458 self.z[self.i] = c;
459 self.i = (self.i + 1) & NHASH_1;
460 let a = (self.a as i32) + (c as i32) - (old as i32);
461 let b = (self.b as i32) - NHASHI32 * (old as i32) + (a & 0xffff);
462 self.a = (a & 0xffff) as u16;
463 self.b = (b & 0xffff) as u16;
464 }
465 fn as_usize(&self) -> usize {
467 (self.a as usize) | ((self.b as usize) << 16)
468 }
469}
470#[cfg(test)]
471mod tests {
472 use super::*;
473 #[test]
474 fn b64_works() {
475 for i in 0..1000 {
476 let s = b64str(i);
477 let s1 = b64str(i + 0x1_00_0000);
478 assert_eq!(i, b64int(&s));
479 assert_eq!(i, b64int(&s1) - 0x1_00_0000);
480 }
481 }
482 #[test]
483 fn test_hash_update() {
484 let mut h = Hash::new();
485 h.init(b"0123456789ABCDEFFEDCBA9876543210");
486 assert_eq!(h.as_usize(), 0x1cbb03a2);
487 let mut h2 = Hash::new();
488 h2.init(b"123456789ABCDEFFEDCBA9876543210");
489 h.update(b'F');
490 assert_eq!(h.as_usize(), h2.as_usize())
491 }
492 #[test]
493 fn delta_gen() {
494 let old = include_str!("test-data/file-a.txt");
495 let cur = include_str!("test-data/file-b.txt");
496 let d1: &[u8] = include_bytes!("test-data/file-delta.txt");
497 let mut d = Vec::new();
498 generate_delta(&cur, &old, &mut d);
499 assert_eq!(d.as_slice(), d1);
500 }
501 #[test]
502 fn round_trip_test() {
503 let a = b"line 1
504 yet another (a bit longer) line 2
505 yet another (a bit longer) line 3
506 yet another (a bit longer) line 4
507 yet another (a bit longer) line 5
508 yet another (a bit longer) line 6
509 yet another (a bit longer) line 7
510 yet another (a bit longer) line 8
511 yet another (a bit longer) line 9
512 yet another (a bit longer) line 10";
513 let b = b"line 1
514 yet another (a bit longer) line 2
515 yet another (a bit longer) line 3
516 yet another (a bit longer) line 4
517 yet another (a bit longer) line 5
518 yet another (a bit longer) line 6
519 yet another (a bit longer) line 6 1/2
520 yet another (a bit longer) line 7
521 yet another (a bit longer) line 8
522 yet another (a bit longer) line 9
523 and finally last line 10";
524 let d = delta(a, b);
525 println!("delta:{:?}", &d);
526 let s = deltainv(b, &d);
527 assert_eq!(&s, a);
528 assert_eq!(d.len(), 43);
529 }
530 #[test]
531 fn round_trip_test2() {
532 let a = r#"def do_Expression(self, node):\n '''An inner expression'''\n self.visit(node.body)\n"#.as_bytes();
533 let b = r#"sion(self, node):\n '''An inner expression'''\n self.visit(node.body)\n"#.as_bytes();
534 println!(
535 "a.len={}, b.len={}, b64={}",
536 a.len(),
537 b.len(),
538 &b64str(a.len() as u32)
539 );
540 let d = delta(b, a);
541 println!("delta:{:?}", &d);
542 let s = deltainv(a, &d);
543 assert_eq!(&s, b);
544 }
545 #[test]
546 fn empty_txt() {
547 let a = "".as_bytes();
548 let b = r#"line 1
549 yet another (a bit longer) line 2
550 yet another (a bit longer) line 3
551 yet another (a bit longer) line 4
552 yet another (a bit longer) line 5
553 yet another (a bit longer) line 6
554 yet another (a bit longer) line 6 1/2
555 yet another (a bit longer) line 7
556 yet another (a bit longer) line 8
557 yet another (a bit longer) line 9
558 and finally last line 10"#.as_bytes();
559 let d = delta(b, a);
560 println!("empty delta:{:?}", &d);
561 let s = deltainv(a, &d);
562 assert_eq!(b, &s);
563 }
564 #[test]
565 fn test_deltainv() {
566 let old = include_bytes!("test-data/file-a.txt");
567 let cur = include_bytes!("test-data/file-b.txt");
568 let d1: &[u8] = include_bytes!("test-data/file-delta.txt");
569 let res = deltainv(cur, d1);
570 assert_eq!(&res[..30], &old[..30]);
571 }
572 #[test]
573 fn apply_test() {
574 let old = include_str!("test-data/file-a.txt");
575 let cur = include_str!("test-data/file-b.txt");
576 let d1: &[u8] = include_bytes!("test-data/file-delta.txt");
577 let out = apply(&old, &d1).unwrap();
578 assert_eq!(cur, String::from_utf8_lossy(&out));
579 }
580 #[test]
581 fn test_bug_001() {
582 let a=b"send-snap\nimport zmq\n#c.user_dict.pop('sendsnap', None)\n@others\nmsg = \"snapshot %s\"% snap()\nsend(msg)\n#msg = \"getat 2019-07-11 10:06:21\"\n#res = send(msg, True)\n#with open('/tmp/proba', 'w') as out:\n# out.write(res)\ng.es('ok')\n";
583 let b=b"send-snap\nimport zmq\n#c.user_dict.pop('sendsnap', None)\n@others\nmsg = \"snapshot %s\"% snap()\n\nsend(msg)\n#msg = \"getat 2019-07-11 10:06:21\"\n#res = send(msg, True)\n#with open('/tmp/proba', 'w') as out:\n# out.write(res)\ng.es('ok')\n";
584 let d = b"3a\n1S@0,29@1T,31_Pqh;";
585 let d1 = delta(&a, &b);
586 assert_eq!(&d1, &d);
587 let s = deltainv(b, &d1);
588 assert_eq!(&s, a);
589 }
590 #[test]
591 fn test_bug_002() {
592 let a="from student import moja_tajna_funkcija\n\ndef check(a, b):\n assert moja_tajna_funkcija(a, b) == a + b, \"Функција не даје добар резултат за аргументе: %r и %r\"%(a, b)\n\nif __name__ == '__main__':\n for x in range(-100, 101):\n for y in range(-100, 101):\n check(x, y)\n print(\"Функција ради коректно\")\n";
593 let b="from student import moja_tajna_funkcija\n\ndef check(a, b):\n assert moja_tajna_funkcija(a, b) == a + b, \"Функција не даје добар резултат за аргументе: %r и %r\"%(a, b)\n\nif __name__ == '__main__':\n for x in range(-100, 101):\n for y in range(-100, 101):\n check(x, y)\n print(\"Није пронађена грешка у твом програму.\")\n";
594 let d = delta(&b, &a);
595 let mut d1 = Vec::new();
596 d1.extend_from_slice(b"6P\n5H@0,18:\x9d\xd0\xb8\xd1\x98\xd0\xb5 \xd0\xbf\xd1\x80\xd0\xbe\xd0\xbd\xd0\xb0\xd1\x92\xd0\xb5\xd0\xbd\xd0\xb0 \xd0\xb3\xd1\x80\xd0\xb5\xd1\x88\xd0\xba\xd0\xb0 \xd1\x83 \xd1\x82\xd0\xb2\xd0\xbe\xd0\xbc \xd0\xbf\xd1\x80\xd0\xbe\xd0\xb3\xd1\x80\xd0\xb0\xd0\xbc\xd1\x83.\")\n2mdlCq;");
597 assert_eq!(d, d1);
598 }
599 #[allow(dead_code)]
600 static BUG002_A:&str = r#"from student import moja_tajna_funkcija
601
602def check(a, b):
603 assert moja_tajna_funkcija(a, b) == a + b, "Функција не даје добар резултат за аргументе: %r и %r"%(a, b)
604
605if __name__ == '__main__':
606 for x in range(-100, 101):
607 for y in range(-100, 101):
608 check(x, y)
609 print("Функција ради коректно")"#;
610
611 #[allow(dead_code)]
612 static BUG002_B:&str = r#"from student import moja_tajna_funkcija
613
614def check(a, b):
615 assert moja_tajna_funkcija(a, b) == a + b, "Функција не даје добар резултат за аргументе: %r и %r"%(a, b)
616
617if __name__ == '__main__':
618 for x in range(-100, 101):
619 for y in range(-100, 101):
620 check(x, y)
621 print("Није пронађена грешка у твом програму.")"#;
622}