1use crate::types::{Command, DeltaError, PlacedCommand};
2
3pub fn output_size(commands: &[Command]) -> usize {
5 commands
6 .iter()
7 .map(|cmd| match cmd {
8 Command::Copy { length, .. } => *length,
9 Command::Add { data } => data.len(),
10 })
11 .sum()
12}
13
14pub fn place_commands(commands: Vec<Command>) -> Vec<PlacedCommand> {
18 let mut placed = Vec::with_capacity(commands.len());
19 let mut dst = 0;
20 for cmd in commands {
21 match cmd {
22 Command::Copy { offset, length } => {
23 placed.push(PlacedCommand::Copy { src: offset, dst, length });
24 dst += length;
25 }
26 Command::Add { data } => {
27 let len = data.len();
28 placed.push(PlacedCommand::Add { dst, data }); dst += len;
30 }
31 }
32 }
33 placed
34}
35
36pub fn unplace_commands(mut placed: Vec<PlacedCommand>) -> Vec<Command> {
48 placed.sort_by_key(|c| match c {
49 PlacedCommand::Copy { dst, .. } => *dst,
50 PlacedCommand::Add { dst, .. } => *dst,
51 PlacedCommand::Move { dst, .. } => *dst,
52 });
53 placed
54 .into_iter()
55 .map(|c| match c {
56 PlacedCommand::Copy { src, length, .. } => Command::Copy { offset: src, length },
57 PlacedCommand::Add { data, .. } => Command::Add { data },
58 PlacedCommand::Move { .. } => panic!(
59 "unplace_commands: Move has no algorithm-level equivalent; \
60 Move commands are DLT\\x04-only and cannot be unplaced"
61 ),
62 })
63 .collect()
64}
65
66pub fn apply_placed_to(r: &[u8], commands: &[PlacedCommand], out: &mut [u8]) -> usize {
70 let mut max_written = 0;
71 for cmd in commands {
72 match cmd {
73 PlacedCommand::Copy { src, dst, length } => {
74 out[*dst..*dst + *length].copy_from_slice(&r[*src..*src + *length]);
75 let end = dst + length;
76 if end > max_written { max_written = end; }
77 }
78 PlacedCommand::Add { dst, data } => {
79 out[*dst..*dst + data.len()].copy_from_slice(data);
80 let end = dst + data.len();
81 if end > max_written { max_written = end; }
82 }
83 PlacedCommand::Move { src, dst, length } => {
84 out.copy_within(*src..*src + *length, *dst);
89 let end = dst + length;
90 if end > max_written { max_written = end; }
91 }
92 }
93 }
94 max_written
95}
96
97pub fn apply_placed_inplace_to(commands: &[PlacedCommand], buf: &mut [u8]) {
101 for cmd in commands {
102 match cmd {
103 PlacedCommand::Copy { src, dst, length } => {
104 buf.copy_within(*src..*src + *length, *dst);
105 }
106 PlacedCommand::Add { dst, data } => {
107 buf[*dst..*dst + data.len()].copy_from_slice(data);
108 }
109 PlacedCommand::Move { src, dst, length } => {
110 buf.copy_within(*src..*src + *length, *dst);
112 }
113 }
114 }
115}
116
117pub fn validate_placed_commands(
119 commands: &[PlacedCommand],
120 reference_size: usize,
121 version_size: usize,
122 inplace: bool,
123) -> Result<(), DeltaError> {
124 let source_limit = if inplace {
125 reference_size.max(version_size)
126 } else {
127 reference_size
128 };
129 for cmd in commands {
130 match cmd {
131 PlacedCommand::Copy { src, dst, length } => {
132 validate_apply_range(*dst, *length, version_size, "copy destination")?;
133 validate_apply_range(*src, *length, source_limit, "copy source")?;
134 }
135 PlacedCommand::Add { dst, data } => {
136 validate_apply_range(*dst, data.len(), version_size, "add destination")?;
137 }
138 PlacedCommand::Move { src, dst, length } => {
139 validate_apply_range(*dst, *length, version_size, "move destination")?;
140 validate_apply_range(*src, *length, version_size, "move source")?;
141 if src + length > *dst {
146 return Err(DeltaError::InvalidFormat(format!(
147 "move src+len ({}+{}) > dst ({}): source not yet written",
148 src, length, dst
149 )));
150 }
151 }
152 }
153 }
154 Ok(())
155}
156
157pub fn apply_delta_to(r: &[u8], commands: &[Command], out: &mut [u8]) -> usize {
163 let mut pos = 0;
164 for cmd in commands {
165 match cmd {
166 Command::Add { data } => {
167 out[pos..pos + data.len()].copy_from_slice(data);
168 pos += data.len();
169 }
170 Command::Copy { offset, length } => {
171 out[pos..pos + *length].copy_from_slice(&r[*offset..*offset + *length]);
172 pos += *length;
173 }
174 }
175 }
176 pos
177}
178
179pub fn apply_delta(r: &[u8], commands: &[Command]) -> Vec<u8> {
181 let mut out = vec![0u8; output_size(commands)];
182 apply_delta_to(r, commands, &mut out);
183 out
184}
185
186pub fn apply_delta_inplace(
188 r: &[u8],
189 commands: &[PlacedCommand],
190 version_size: usize,
191) -> Vec<u8> {
192 let buf_size = r.len().max(version_size);
193 let mut buf = vec![0u8; buf_size];
194 buf[..r.len()].copy_from_slice(r);
195 apply_placed_inplace_to(commands, &mut buf);
196 buf.truncate(version_size);
197 buf
198}
199
200fn validate_apply_range(
201 start: usize,
202 length: usize,
203 limit: usize,
204 name: &str,
205) -> Result<(), DeltaError> {
206 if start > limit || length > limit.saturating_sub(start) {
207 return Err(DeltaError::InvalidFormat(format!("{} out of range", name)));
208 }
209 Ok(())
210}