smdiff_merger/
lib.rs

1use std::io::{Read, Seek, Write};
2
3use smdiff_common::{Format, Run, MAX_INST_SIZE, MAX_WIN_SIZE};
4use smdiff_decoder::reader::SectionIterator;
5use smdiff_encoder::{writer::section_writer, SecondaryCompression};
6use smdiff_reader::Op;
7use smdiff_writer::make_sections;
8
9pub mod transcoder;
10///Extracted Instruction with the starting position in the output buffer.
11pub type SparseOp = (u64, Op);
12
13impl MergeOp for Run {
14    fn skip(&mut self,amt:u32) {
15        self.len -= amt as u8;
16    }
17    fn trunc(&mut self,amt:u32) {
18        self.len -= amt as u8;
19    }
20}
21impl MergeOp for smdiff_common::Copy {
22    fn skip(&mut self,amt:u32) {
23        self.addr += amt as u64;
24        self.len -= amt as u16;
25    }
26    fn trunc(&mut self,amt:u32) {
27        self.len -= amt as u16;
28    }
29}
30impl MergeOp for smdiff_reader::Add {
31    fn skip(&mut self,amt:u32){
32        self.bytes = self.bytes.split_off(amt as usize);
33    }
34    fn trunc(&mut self,amt:u32){
35        self.bytes.truncate(self.bytes.len() - amt as usize);
36    }
37}
38impl MergeOp for Op{
39    fn skip(&mut self,amt:u32){
40        match self {
41            Op::Run(run) => run.skip(amt),
42            Op::Copy(copy) => copy.skip(amt),
43            Op::Add(add) => add.skip(amt),
44        }
45    }
46    fn trunc(&mut self,amt:u32){
47        match self {
48            Op::Run(run) => run.trunc(amt),
49            Op::Copy(copy) => copy.trunc(amt),
50            Op::Add(add) => add.trunc(amt),
51        }
52    }
53}
54
55pub trait MergeOp:Clone+Sized{
56    ///Shorten the 'front' of the instruction
57    fn skip(&mut self,amt:u32);
58    ///Truncate off the 'back' of the instruction
59    fn trunc(&mut self,amt:u32);
60}
61
62///Finds the index of the op that controls the given output position.
63/// # Arguments
64/// * `ops` - The list of ops to search.
65/// * `o_pos` - The output position to find the controlling instruction for.
66/// # Returns
67/// The index of the controlling instruction, or None if no such instruction exists.
68pub fn find_controlling_op(ops:&[SparseOp],o_pos:u64)->Option<usize>{
69    let inst = ops.binary_search_by(|probe|{
70        let end = probe.0 + probe.1.oal() as u64;
71        if (probe.0..end).contains(&o_pos){
72            return std::cmp::Ordering::Equal
73        }else if probe.0 > o_pos {
74            return std::cmp::Ordering::Greater
75        }else{
76            return std::cmp::Ordering::Less
77        }
78    });
79    if let Ok(idx) = inst {
80        Some(idx)
81    }else {
82        None
83    }
84}
85
86///Returns a cloned and clipped subslice of ops that exactly covers the requested output range.
87/// # Arguments
88/// * `ops` - The list of ops to extract from.
89/// * `start` - The output position (output byte offset) to start the slice at.
90/// * `len` - The length of the slice in output bytes.
91/// # Returns
92/// A vector containing the cloned and clipped ops that exactly cover the requested output range.
93/// If the output range is not covered by the ops, None is returned.
94///
95/// This does not check that the ops are sequential.
96pub fn get_exact_slice(ops:&[SparseOp],start:u64,len:u32)->Option<Vec<SparseOp>>{
97    let start_idx = find_controlling_op(ops,start)?;
98    let end_pos = start + len as u64;
99    let mut slice = Vec::new();
100    let mut complete = false;
101
102    for (o_start, inst) in ops[start_idx..].iter() {
103        let inst_len = inst.oal();
104        let cur_inst_end = o_start + inst_len as u64;
105        let mut cur_inst = inst.clone();
106        let op_start = if &start > o_start {
107            let skip = start - o_start;
108            cur_inst.skip(skip as u32);
109            start
110        }else{*o_start};
111        if end_pos < cur_inst_end {
112            let trunc = cur_inst_end - end_pos;
113            cur_inst.trunc(trunc as u32);
114        }
115        debug_assert!(cur_inst.oal() > 0, "The instruction length is zero");
116        slice.push((op_start,cur_inst));
117
118        if cur_inst_end >= end_pos {
119            complete = true;
120            //debug_assert!(sum_len_in_o(&slice)==len as u64,"{} != {} start:{} end_pos:{} ... {:?} from {:?}",sum_len_in_o(&slice),len,start,end_pos,&slice,instructions);
121            break;
122        }
123    }
124    if !complete {
125        return None;
126    }
127    Some(slice)
128}
129
130//Should maybe move this to Reader?
131///Stats about the patch file.
132#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
133pub struct Stats{
134    pub add_bytes:usize,
135    pub run_bytes:usize,
136    pub copy_bytes:usize,
137    pub add_cnt:usize,
138    pub run_cnt:usize,
139    pub copy_d_cnt:usize,
140    pub copy_o_cnt:usize,
141    pub output_size:usize,
142}
143
144impl Stats {
145    pub fn new() -> Self {
146        Default::default()
147    }
148    pub fn add(&mut self, len:usize){
149        self.add_bytes += len;
150        self.add_cnt += 1;
151        self.output_size += len;
152    }
153    pub fn run(&mut self, len:usize){
154        self.run_bytes += len;
155        self.run_cnt += 1;
156        self.output_size += len;
157    }
158    pub fn copy_d(&mut self, len:usize){
159        self.copy_bytes += len;
160        self.copy_d_cnt += 1;
161        self.output_size += len;
162    }
163    pub fn copy_o(&mut self, len:usize){
164        self.copy_bytes += len;
165        self.copy_o_cnt += 1;
166        self.output_size += len;
167    }
168    pub fn has_copy(&self)->bool{
169        self.copy_bytes > 0
170    }
171}
172
173///Extracts all instructions from all windows.
174///Memory consumption may be 2-4x the size of the encoded (uncompressed) patch.
175pub fn extract_patch_instructions<R:Read + Seek>(patch:R)->std::io::Result<(Vec<SparseOp>, Stats)>{
176    let mut output = Vec::new();
177    let mut reader = SectionIterator::new(patch);
178    let mut o_pos_start = 0;
179    let mut stats = Stats::new();
180    while let Some(res) = reader.next() {
181        let (insts,_output_size) = res?;
182        for inst in insts{
183            let oal_len = inst.oal() as usize;
184            match &inst{
185                smdiff_common::Op::Run(_) => {
186                    output.push((o_pos_start,inst));
187                    stats.run(oal_len);
188                },
189                smdiff_common::Op::Copy(c) => {
190                    match c.src{
191                        smdiff_common::CopySrc::Dict => {
192                            stats.copy_d(oal_len);
193                        },
194                        smdiff_common::CopySrc::Output => {
195                            stats.copy_o(oal_len);
196                        },
197                    }
198                    output.push((o_pos_start,inst));
199                    stats.copy_d(oal_len);
200
201                },
202                smdiff_common::Op::Add(_) => {
203                    output.push((o_pos_start,inst));
204                    stats.add(oal_len);
205                },
206            }
207            o_pos_start += oal_len as u64;
208        }
209    }
210
211    Ok((output,stats))
212}
213
214/// This function will dereference all Copy_Output instructions in the extracted instructions.
215pub fn deref_copy_o(extracted:Vec<SparseOp>)->Vec<SparseOp>{
216    //TODO: We could optimize by having get_exact_slice return *what to do* to dereference the copy.
217    // The advantage would be we wouldn't clone any Ops.
218    // We would point to the first and last index in `extracted` and the skip/trunc values for those two ops.
219    // This is faster and more memory efficient.
220    // However, we need to deal with an enum type that will make a mess of things when we get to the main merge fn.
221    let mut output:Vec<SparseOp> = Vec::with_capacity(extracted.len());
222    let mut cur_o_pos = 0;
223    for (_,op) in extracted {
224        match op {
225            Op::Copy(copy) if matches!(copy.src, smdiff_common::CopySrc::Output) => {
226                //let copy = copy.clone();
227                let o_start = copy.addr;
228                let resolved = get_exact_slice(output.as_slice(), o_start, copy.len as u32).unwrap();
229                for (_,resloved_op) in resolved {
230                    let o_pos_start = cur_o_pos;
231                    cur_o_pos += resloved_op.oal() as u64;
232                    output.push((o_pos_start,resloved_op));
233                }
234            },
235            _ => {
236                let o_pos_start = cur_o_pos;
237                cur_o_pos += op.oal() as u64;
238                output.push((o_pos_start,op))
239
240            },
241        }
242    }
243    output
244}
245
246fn find_mergeable_copies(extract:&[SparseOp],shift:usize,dest:&mut Vec<usize>){
247    for (i,(_,op)) in extract.iter().enumerate(){
248        match op {
249            Op::Copy(copy) if matches!(copy.src, smdiff_common::CopySrc::Dict) => {
250                dest.push(i+shift);
251            },
252            _ => (),
253        }
254    }
255}
256///Merger struct that can accept merging of additional patches.
257#[derive(Clone, Debug)]
258pub struct Merger{
259    ///The summary patch that will be written to the output.
260    terminal_patch: Vec<SparseOp>,
261    ///If this is empty, merging a patch will have no effect.
262    ///These are where TerminalInst::CopySS are found.
263    terminal_copy_indices: Vec<usize>,
264    //final_size: u64,
265}
266
267impl Merger {
268    ///Creates a new Merger from a terminal patch.
269    ///This should only be called using the patch that generates the output file you want.
270    /// # Arguments
271    /// * `terminal_patch` - The terminal patch that will serve as the core set of instructions.
272    /// # Returns
273    /// If the terminal summary patch has no Copy instructions, a SummaryPatch is returned.
274    /// If the terminal summary patch has even a single Copy instructions, a Merger is returned.
275    pub fn new<R:Read + Seek>(terminal_patch:R) -> std::io::Result<Result<Merger,SummaryPatch>> {
276        let (terminal_patch,stats) = extract_patch_instructions(terminal_patch)?;
277        if stats.copy_bytes == 0{
278            return Ok(Err(SummaryPatch(terminal_patch.into_iter().map(|s|s.1).collect())));
279        }
280        let mut terminal_copy_indices = Vec::new();
281        //we for sure need to translate local. I think translate global isn't needed??
282        //will need to check this.
283        let terminal_patch = deref_copy_o(terminal_patch);
284        find_mergeable_copies(&terminal_patch,0,&mut terminal_copy_indices);
285        debug_assert!(!terminal_copy_indices.is_empty(), "terminal_copy_indices should not be empty");
286        Ok(Ok(Merger{
287            terminal_patch,
288            terminal_copy_indices,
289            //final_size:stats.output_size as u64
290        }))
291    }
292    ///Merges a predecessor patch into the terminal patch.
293    ///This should be called using proper order of patches.
294    /// # Arguments
295    /// * `predecessor_patch` - The patch to merge into the current summary patch.
296    /// # Returns
297    /// If the resulting summary patch has no Copy instructions, a SummaryPatch is returned.
298    /// If the resulting summary patch has even a single Copy instructions, a Merger is returned.
299    pub fn merge<R:Read + Seek>(mut self, predecessor_patch:R) -> std::io::Result<Result<Merger,SummaryPatch>> {
300        debug_assert!({
301            let mut x = 0;
302            for inst in self.terminal_patch.iter(){
303                assert_eq!(x,inst.0);
304                x += inst.1.oal() as u64;
305            }
306            true
307        });
308        let (mut predecessor_patch,stats) = extract_patch_instructions(predecessor_patch)?;
309        if stats.has_copy(){
310            predecessor_patch = deref_copy_o(predecessor_patch);
311        }
312        let mut terminal_copy_indices = Vec::with_capacity(self.terminal_copy_indices.len());
313        let mut inserts = Vec::with_capacity(self.terminal_copy_indices.len());
314        let mut shift = 0;
315        for i in self.terminal_copy_indices{
316            let (_,op) = self.terminal_patch[i].clone();
317            let copy = op.take_copy().expect("Expected Copy");
318            //this a src window copy that we need to resolve from the predecessor patch.
319            debug_assert!(matches!(copy.src, smdiff_common::CopySrc::Dict));
320            let o_start = copy.addr; //ssp is o_pos, u is offset from that.
321            let resolved = get_exact_slice(&predecessor_patch, o_start, copy.len as u32).unwrap();
322            //debug_assert_eq!(sum_len_in_o(&resolved), copy.len_in_o() as u64, "resolved: {:?} copy: {:?}",resolved,copy);
323            find_mergeable_copies(&resolved, i+shift, &mut terminal_copy_indices);
324            shift += resolved.len() - 1;
325            inserts.push((i, resolved));
326
327        }
328        //now we expand the old copy values with the derefd instructions.
329        //debug_assert_eq!(sum_len_in_o(&self.terminal_patch), self.final_size, "final size: {} sum_len: {}",self.final_size,sum_len_in_o(&self.terminal_patch));
330        if terminal_copy_indices.is_empty(){
331
332            Ok(Err(SummaryPatch(expand_to(self.terminal_patch, inserts, |s|s.1))))
333        }else{
334            self.terminal_patch = expand_to(self.terminal_patch, inserts, |s|s);
335            self.terminal_copy_indices = terminal_copy_indices;
336            Ok(Ok(self))
337        }
338    }
339    ///Finishes the merger and returns the final summary patch ready to be written or applied.
340    pub fn finish(self)->SummaryPatch{
341        SummaryPatch(self.terminal_patch.into_iter().map(|s|s.1).collect())
342    }
343
344}
345
346/// This is returned when the current summary patch contains no Copy instructions, OR when you are finished with the Merger.
347#[derive(Debug)]
348pub struct SummaryPatch(Vec<Op>);
349impl SummaryPatch{
350    ///Writes the summary patch to a sink.
351    /// # Arguments
352    /// * `sink` - The sink to write the summary patch to.
353    /// * `max_win_size` - The maximum output size for any window of instructions. Ignored If this will fit in micro format.
354    /// # Returns
355    /// The sink that was passed in.
356    pub fn write<W:Write>(self,sink:&mut W,max_win_size:Option<usize>,format:Option<Format>,sec_comp:Option<SecondaryCompression>)->std::io::Result<()>{
357        //window needs to be MAX_INST_SIZE..=MAX_WIN_SIZE
358        let max_win_size = max_win_size.unwrap_or(MAX_WIN_SIZE).min(MAX_WIN_SIZE).max(MAX_INST_SIZE);
359        let format = format.unwrap_or(Format::Interleaved);
360        let mut sec_data_buffer = Vec::new();
361        for (seg_ops,mut header) in make_sections(&self.0, max_win_size){
362            header.format = format;
363            section_writer(&sec_comp, header, sink, seg_ops, &mut sec_data_buffer)?;
364        }
365        Ok(())
366    }
367    /// Returns the ops that represents the summary patch.
368    /// This allows applying them directly to a source file without translating them to a patch file.
369    pub fn take_ops(self)->Vec<Op>{
370        self.0
371    }
372}
373
374/// This is trying to efficiently splice in the new operations from the merge.
375/// We need the generics as we might return either a Vec<SparseOp> or a Vec<Op>
376fn expand_to<T, F>(
377    mut target: Vec<SparseOp>,
378    inserts: Vec<(usize, Vec<SparseOp>)>,
379    mut converter: F,
380) -> Vec<T>
381where
382    F: FnMut(SparseOp) -> T,
383{
384    // Calculate the total number of elements to be inserted to determine the new vector's length.
385    let total_insertions: usize = inserts.iter().map(|(_, ins)| ins.len()).sum();
386    let final_length = target.len() + total_insertions;
387
388    // Allocate a new vector with the final required size.
389    let mut result = Vec::with_capacity(final_length);
390
391    // Sort inserts by position to process them in order.
392    let mut sorted_inserts = inserts;
393    sorted_inserts.sort_by_key(|k| k.0);
394
395    target.reverse();
396    // Trackers for the current position in the original vector and the inserts.
397    let mut cur_idx = 0;
398    let mut cur_o_pos = 0;
399    for (insert_pos, insert_vec) in sorted_inserts {
400        // Copy elements from the current position up to the insert position.
401        while cur_idx < insert_pos {
402            match target.pop() {
403                Some(mut elem) => {
404                    let len = elem.1.oal();
405                    elem.0 = cur_o_pos;
406                    cur_o_pos += len as u64;
407                    result.push(converter(elem));
408                    cur_idx += 1;
409                }
410                None => break,
411            }
412        }
413        // Insert the new elements.
414        for mut elem in insert_vec {
415            let len = elem.1.oal();
416            elem.0 = cur_o_pos;
417            cur_o_pos += len as u64;
418            result.push(converter(elem));
419        }
420        target.pop();//we get rid of the expanded element.
421        cur_idx += 1;
422    }
423
424    // After processing all inserts, copy any remaining elements from the original vector.
425    while let Some(mut elem) = target.pop() {
426        let len = elem.1.oal();
427        elem.0 = cur_o_pos;
428        cur_o_pos += len as u64;
429        result.push(converter(elem));
430    }
431    result
432
433}
434
435
436
437#[cfg(test)]
438mod test_super {
439
440    use smdiff_common::{Copy, CopySrc, SectionHeader};
441    use smdiff_decoder::apply_patch;
442    use smdiff_reader::Add;
443    use super::*;
444    /*
445    Basic merger tests will start with a src file of '01234'
446    We will then create a series of patches that will make certain *changes* to the file.
447    That is, we want to be able to apply them in different orders for different effects.
448    To this end, all of the target windows must be the same size.
449    We will pick 10 bytes as our target window size. This is twice the length of 'hello'
450
451    We need to test the following:
452    Copy Passthrough
453    Add/Run precedence
454
455    For the copy:
456    We will make a patch that will copy the first five bytes to the last five bytes.
457    This should turn '01234' into '0123401234'
458
459    For the add/run:
460    We will make a patch that will insert 'A' (ADD) at first pos Copy next 2, Then 'XXX'(Run) + 'YZ'(Add) The COpy rem
461    This should turn '01234' into 'A12XXXYZ34'
462
463    Then we do a patch with multiple transforms internally
464    Complex:
465    We will Add 'Y' Run(2) 'Z' CopyD 4,1 CopyO 2,2 (Z4) Copy u_pos 1 len 4
466    This should turn '01234' into 'YZZ4Z41234'
467
468    We can then mix and match these patches and we should be able to reason about the outputs.
469    */
470    use std::io::Cursor;
471    fn copy_patch() -> Cursor<Vec<u8>> {
472        let mut sink = Cursor::new(Vec::new());
473        let header = SectionHeader {
474            num_operations:2,
475            num_add_bytes: 0,
476            output_size: 10,
477            compression_algo: 0,
478            format: Format::Interleaved,
479            more_sections: false,
480
481        };
482        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
483        let ops = &[
484            Op::Copy(Copy { src: CopySrc::Dict, addr: 0, len: 5}),
485            Op::Copy(Copy { src: CopySrc::Dict, addr: 0, len: 5}),
486        ];
487        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
488        sink.rewind().unwrap();
489        sink
490    }
491    fn add_run_patch() -> Cursor<Vec<u8>> {
492        let mut sink = Cursor::new(Vec::new());
493        let header = SectionHeader {
494            num_operations:5,
495            num_add_bytes: 0,
496            output_size: 10,
497            compression_algo: 0,
498            format: Format::Interleaved,
499            more_sections: false,
500
501        };
502        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
503        let ops = &[
504            Op::Add(Add{bytes:b"A".to_vec()}),
505            Op::Copy(Copy { src: CopySrc::Dict, addr: 1, len: 2}),
506            Op::Run(Run { byte: b'X', len: 3}),
507            Op::Add(Add{bytes:b"YZ".to_vec()}),
508            Op::Copy(Copy { src: CopySrc::Dict, addr: 3, len: 2}),
509        ];
510        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
511        sink.rewind().unwrap();
512        sink
513    }
514    fn complex_patch()->Cursor<Vec<u8>>{
515        let mut sink = Cursor::new(Vec::new());
516        let header = SectionHeader {
517            num_operations:5,
518            num_add_bytes: 0,
519            output_size: 10,
520            compression_algo: 0,
521            format: Format::Interleaved,
522            more_sections: false,
523
524        };
525        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
526        let ops = &[
527            Op::Add(Add{bytes:b"Y".to_vec()}),
528            Op::Run(Run { byte: b'Z', len: 2}),
529            Op::Copy(Copy { src: CopySrc::Dict, addr: 4, len: 1}),
530            Op::Copy(Copy { src: CopySrc::Output, addr: 2, len: 2}),
531            Op::Copy(Copy { src: CopySrc::Dict, addr: 1, len: 4}),
532        ];
533        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
534        sink.rewind().unwrap();
535        sink
536    }
537    const SRC:&[u8] = b"01234";
538    #[test]
539    fn test_copy_add(){
540        //01234 Copy 0123401234 Add-> A12XXXYZ34
541        let answer = b"A12XXXYZ34";
542        let copy = copy_patch();
543        let add_run = add_run_patch();
544        let merger = Merger::new(add_run).unwrap().unwrap();
545        let merger = merger.merge(copy).unwrap().unwrap();
546        let mut merged_patch = Vec::new();
547        merger.finish().write(&mut merged_patch, None,None,None).unwrap();
548        let mut cursor = Cursor::new(merged_patch);
549        let mut output = Cursor::new(Vec::new());
550        apply_patch(&mut cursor, Some(&mut Cursor::new(SRC.to_vec())), &mut output).unwrap();
551        //print output as a string
552        let output = output.into_inner();
553        let as_str = std::str::from_utf8(&output).unwrap();
554        println!("{}",as_str);
555        assert_eq!(output,answer);
556    }
557    #[test]
558    fn test_add_copy(){
559        //01234 Add -> A12XXXYZ34 Copy-> A12XXA12XX
560        let answer = b"A12XXA12XX";
561        let copy = copy_patch();
562        let add_run = add_run_patch();
563        let merger = Merger::new(copy).unwrap().unwrap();
564        let merger = merger.merge(add_run).unwrap().unwrap();
565        let mut merged_patch = Vec::new();
566        merger.finish().write(&mut merged_patch, None,None,None).unwrap();
567        let mut cursor = Cursor::new(merged_patch);
568        let mut output = Cursor::new(Vec::new());
569        apply_patch(&mut cursor, Some(&mut Cursor::new(SRC.to_vec())), &mut output).unwrap();
570        //print output as a string
571        let output = output.into_inner();
572        let as_str = std::str::from_utf8(&output).unwrap();
573        println!("{}",as_str);
574        assert_eq!(output,answer);
575    }
576    #[test]
577    fn test_add_complex(){
578        //01234 Add-> A12XXXYZ34 Compl YZZXZX12XX
579        let answer = b"YZZXZX12XX";
580        let add_run = add_run_patch();
581        let comp = complex_patch();
582        let merger = Merger::new(comp).unwrap().unwrap();
583        let merger = merger.merge(add_run).unwrap().unwrap();
584        let mut merged_patch = Vec::new();
585        merger.finish().write(&mut merged_patch, None,None,None).unwrap();
586        let mut cursor = Cursor::new(merged_patch);
587        let mut output = Cursor::new(Vec::new());
588        apply_patch(&mut cursor, Some(&mut Cursor::new(SRC.to_vec())), &mut output).unwrap();
589        //print output as a string
590        let output = output.into_inner();
591        let as_str = std::str::from_utf8(&output).unwrap();
592        println!("{}",as_str);
593        assert_eq!(output,answer);
594    }
595    #[test]
596    fn test_complex_add(){
597        //01234 Compl-> YZZ4Z41234 Add AZZXXXYZ4Z
598        let answer = b"AZZXXXYZ4Z";
599        let add_run = add_run_patch();
600        let comp = complex_patch();
601        let merger = Merger::new(add_run).unwrap().unwrap();
602        let merger = merger.merge(comp).unwrap().unwrap();
603        let mut merged_patch = Vec::new();
604        merger.finish().write(&mut merged_patch, None,None,None).unwrap();
605        let mut cursor = Cursor::new(merged_patch);
606        let mut output = Cursor::new(Vec::new());
607        apply_patch(&mut cursor, Some(&mut Cursor::new(SRC.to_vec())), &mut output).unwrap();
608        //print output as a string
609        let output = output.into_inner();
610        let as_str = std::str::from_utf8(&output).unwrap();
611        println!("{}",as_str);
612        assert_eq!(output,answer);
613    }
614    #[test]
615    fn test_all_seq(){
616        //01234 Add-> A12XXXYZ34 Compl YZZXZX12XX -> Copy YZZXZYZZXZ
617        let answer = b"YZZXZYZZXZ";
618        let add_run = add_run_patch();
619        let comp = complex_patch();
620        let copy = copy_patch();
621        let merger = Merger::new(copy).unwrap().unwrap();
622        let merger = merger.merge(comp).unwrap().unwrap();
623        let merger = merger.merge(add_run).unwrap().unwrap_err();
624        let mut merged_patch = Vec::new();
625        merger.write(&mut merged_patch, None,None,None).unwrap();
626        let mut cursor = Cursor::new(merged_patch);
627        let mut output = Cursor::new(Vec::new());
628        //We don't need Src, since the last merge yielded SummaryPatch
629        apply_patch::<_, Cursor<Vec<u8>>,_>(&mut cursor, None, &mut output).unwrap();
630        //print output as a string
631        let output = output.into_inner();
632        let as_str = std::str::from_utf8(&output).unwrap();
633        println!("{}",as_str);
634        assert_eq!(output,answer);
635    }
636    #[test]
637    fn test_kitchen_sink(){
638        //"hello" -> "hello world!" -> "Hello! Hello! Hello. hello. hello..."
639        //we need to use a series of VCD_TARGET windows and Sequences across multiple patches
640        //we should use copy/seq excessively since add/run is simple in the code paths.
641        let src = b"hello!";
642        let mut sink = Cursor::new(Vec::new());
643        let header = SectionHeader {
644            num_operations:1,
645            num_add_bytes: 0,
646            output_size: 5,
647            compression_algo: 0,
648            format: Format::Interleaved,
649            more_sections: true,
650
651        };
652        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
653        let ops = &[
654            Op::Copy(Copy { src: CopySrc::Dict, addr: 0, len: 5}),
655        ];
656        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
657
658        let header = SectionHeader {
659            num_operations:3,
660            num_add_bytes: 0,
661            output_size: 6,
662            compression_algo: 0,
663            format: Format::Interleaved,
664            more_sections: true,
665
666        };
667        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
668        let ops = &[
669            Op::Add(Add{bytes:b" w".to_vec()}),
670            Op::Copy(Copy { src: CopySrc::Output, addr: 4, len: 1}),
671            Op::Add(Add{bytes:b"rld".to_vec()}),
672        ];
673        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
674
675        let header = SectionHeader {
676            num_operations:1,
677            num_add_bytes: 0,
678            output_size: 1,
679            compression_algo: 0,
680            format: Format::Interleaved,
681            more_sections: false,
682
683        };
684        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
685        let ops = &[
686            Op::Copy(Copy { src: CopySrc::Dict, addr: 5, len: 1}),
687        ];
688        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
689        let p1 = sink.into_inner();
690        let p1_answer = b"hello world!";
691        let mut cursor = Cursor::new(p1.clone());
692        let mut output = Cursor::new(Vec::new());
693        apply_patch(&mut cursor, Some(&mut Cursor::new(src.to_vec())), &mut output).unwrap();
694        let output = output.into_inner();
695        println!("{}",std::str::from_utf8(&output).unwrap());
696        assert_eq!(output,p1_answer); //ensure our instructions do what we think they are.
697        let patch_1 = Cursor::new(p1);
698        let mut sink = Cursor::new(Vec::new());
699
700        let header = SectionHeader {
701            num_operations:4,
702            num_add_bytes: 0,
703            output_size: 7,
704            compression_algo: 0,
705            format: Format::Interleaved,
706            more_sections: true,
707
708        };
709        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
710        let ops = &[
711            Op::Add(Add{bytes:b"H".to_vec()}),
712            Op::Copy(Copy { src: CopySrc::Dict, addr: 1, len: 4}), //ello
713            Op::Copy(Copy { src: CopySrc::Dict, addr: 11, len: 1}), //'!'
714            Op::Copy(Copy { src: CopySrc::Dict, addr: 5, len: 1}), //' '
715        ];
716        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
717
718        let header = SectionHeader {
719            num_operations:4,
720            num_add_bytes: 0,
721            output_size: 14,
722            compression_algo: 0,
723            format: Format::Interleaved,
724            more_sections: true,
725
726        };
727        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
728        let ops = &[
729            Op::Copy(Copy { src: CopySrc::Output, addr: 0, len: 7}), //'Hello! '
730            Op::Copy(Copy { src: CopySrc::Output, addr: 7, len: 5}),  //'Hello'
731            Op::Add(Add{bytes:b".".to_vec()}),
732            Op::Copy(Copy { src: CopySrc::Output, addr: 13, len: 1}), // ' '
733        ];
734        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
735
736        let header = SectionHeader {
737            num_operations:2,
738            num_add_bytes: 0,
739            output_size: 7,
740            compression_algo: 0,
741            format: Format::Interleaved,
742            more_sections: true,
743
744        };
745        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
746        let ops = &[
747            Op::Add(Add{bytes:b"h".to_vec()}),
748            Op::Copy(Copy { src: CopySrc::Output, addr: 15, len: 6}),  //'ello. '
749        ];
750        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
751
752        let header = SectionHeader {
753            num_operations:2,
754            num_add_bytes: 0,
755            output_size: 8,
756            compression_algo: 0,
757            format: Format::Interleaved,
758            more_sections: false,
759
760        };
761        smdiff_writer::write_section_header(&header, &mut sink).unwrap();
762        let ops = &[
763            Op::Copy(Copy { src: CopySrc::Output, addr: 21, len: 5}),  //'hello'
764            Op::Run(Run { byte: b'.', len: 3}),
765        ];
766        smdiff_writer::write_ops(ops, &header,&mut sink).unwrap();
767        let p2 = sink.into_inner();
768        let p2_answer = b"Hello! Hello! Hello. hello. hello...";
769        let mut cursor = Cursor::new(p2.clone());
770        let mut output = Cursor::new(Vec::new());
771        apply_patch(&mut cursor, Some(&mut Cursor::new(p1_answer.to_vec())), &mut output).unwrap();
772        let output = output.into_inner();
773        println!("{}",std::str::from_utf8(&output).unwrap());
774        assert_eq!(output,p2_answer); //ensure our instructions do what we think they are.
775        let patch_2 = Cursor::new(p2);
776        let merger = Merger::new(patch_2).unwrap().unwrap();
777        let merger = merger.merge(patch_1).unwrap().unwrap();
778        let mut merged_patch = Vec::new();
779        merger.finish().write(&mut merged_patch, None,None,None).unwrap();
780        let mut cursor = Cursor::new(merged_patch);
781        let mut output = Cursor::new(Vec::new());
782        let answer = b"Hello! Hello! Hello. hello. hello...";
783        apply_patch(&mut cursor, Some(&mut Cursor::new(src.to_vec())), &mut output).unwrap();
784        //print output as a string
785        let output = output.into_inner();
786        let as_str = std::str::from_utf8(&output).unwrap();
787        println!("{}",as_str);
788        assert_eq!(output,answer);
789
790    }
791
792}