keramics_compression/
lzvn.rs

1/* Copyright 2024-2025 Joachim Metz <joachim.metz@gmail.com>
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may
5 * obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 * License for the specific language governing permissions and limitations
11 * under the License.
12 */
13
14//! LZVN decompression.
15//!
16//! Provides decompression support for LZVN compressed data.
17
18use keramics_core::ErrorTrace;
19use keramics_core::mediator::{Mediator, MediatorReference};
20
21/// LZVN oppcode type.
22#[derive(Clone, PartialEq)]
23enum LzvnOppcodeType {
24    DistanceLarge,
25    DistanceMedium,
26    DistancePrevious,
27    DistanceSmall,
28    EndOfStream,
29    Invalid,
30    LiteralLarge,
31    LiteralSmall,
32    MatchLarge,
33    MatchSmall,
34    None,
35}
36
37/// Lookup table to map a LZCN oppcode to its type.
38const LZVN_OPPCODE_TYPES: [LzvnOppcodeType; 256] = [
39    LzvnOppcodeType::DistanceSmall,    // 0x00
40    LzvnOppcodeType::DistanceSmall,    // 0x01
41    LzvnOppcodeType::DistanceSmall,    // 0x02
42    LzvnOppcodeType::DistanceSmall,    // 0x03
43    LzvnOppcodeType::DistanceSmall,    // 0x04
44    LzvnOppcodeType::DistanceSmall,    // 0x05
45    LzvnOppcodeType::EndOfStream,      // 0x06
46    LzvnOppcodeType::DistanceLarge,    // 0x07
47    LzvnOppcodeType::DistanceSmall,    // 0x08
48    LzvnOppcodeType::DistanceSmall,    // 0x09
49    LzvnOppcodeType::DistanceSmall,    // 0x0a
50    LzvnOppcodeType::DistanceSmall,    // 0x0b
51    LzvnOppcodeType::DistanceSmall,    // 0x0c
52    LzvnOppcodeType::DistanceSmall,    // 0x0d
53    LzvnOppcodeType::None,             // 0x0e
54    LzvnOppcodeType::DistanceLarge,    // 0x0f
55    LzvnOppcodeType::DistanceSmall,    // 0x10
56    LzvnOppcodeType::DistanceSmall,    // 0x11
57    LzvnOppcodeType::DistanceSmall,    // 0x12
58    LzvnOppcodeType::DistanceSmall,    // 0x13
59    LzvnOppcodeType::DistanceSmall,    // 0x14
60    LzvnOppcodeType::DistanceSmall,    // 0x15
61    LzvnOppcodeType::None,             // 0x16
62    LzvnOppcodeType::DistanceLarge,    // 0x17
63    LzvnOppcodeType::DistanceSmall,    // 0x18
64    LzvnOppcodeType::DistanceSmall,    // 0x19
65    LzvnOppcodeType::DistanceSmall,    // 0x1a
66    LzvnOppcodeType::DistanceSmall,    // 0x1b
67    LzvnOppcodeType::DistanceSmall,    // 0x1c
68    LzvnOppcodeType::DistanceSmall,    // 0x1d
69    LzvnOppcodeType::Invalid,          // 0x1e
70    LzvnOppcodeType::DistanceLarge,    // 0x1f
71    LzvnOppcodeType::DistanceSmall,    // 0x20
72    LzvnOppcodeType::DistanceSmall,    // 0x21
73    LzvnOppcodeType::DistanceSmall,    // 0x22
74    LzvnOppcodeType::DistanceSmall,    // 0x23
75    LzvnOppcodeType::DistanceSmall,    // 0x24
76    LzvnOppcodeType::DistanceSmall,    // 0x25
77    LzvnOppcodeType::Invalid,          // 0x26
78    LzvnOppcodeType::DistanceLarge,    // 0x27
79    LzvnOppcodeType::DistanceSmall,    // 0x28
80    LzvnOppcodeType::DistanceSmall,    // 0x29
81    LzvnOppcodeType::DistanceSmall,    // 0x2a
82    LzvnOppcodeType::DistanceSmall,    // 0x2b
83    LzvnOppcodeType::DistanceSmall,    // 0x2c
84    LzvnOppcodeType::DistanceSmall,    // 0x2d
85    LzvnOppcodeType::Invalid,          // 0x2e
86    LzvnOppcodeType::DistanceLarge,    // 0x2f
87    LzvnOppcodeType::DistanceSmall,    // 0x30
88    LzvnOppcodeType::DistanceSmall,    // 0x31
89    LzvnOppcodeType::DistanceSmall,    // 0x32
90    LzvnOppcodeType::DistanceSmall,    // 0x33
91    LzvnOppcodeType::DistanceSmall,    // 0x34
92    LzvnOppcodeType::DistanceSmall,    // 0x35
93    LzvnOppcodeType::Invalid,          // 0x36
94    LzvnOppcodeType::DistanceLarge,    // 0x37
95    LzvnOppcodeType::DistanceSmall,    // 0x38
96    LzvnOppcodeType::DistanceSmall,    // 0x39
97    LzvnOppcodeType::DistanceSmall,    // 0x3a
98    LzvnOppcodeType::DistanceSmall,    // 0x3b
99    LzvnOppcodeType::DistanceSmall,    // 0x3c
100    LzvnOppcodeType::DistanceSmall,    // 0x3d
101    LzvnOppcodeType::Invalid,          // 0x3e
102    LzvnOppcodeType::DistanceLarge,    // 0x3f
103    LzvnOppcodeType::DistanceSmall,    // 0x40
104    LzvnOppcodeType::DistanceSmall,    // 0x41
105    LzvnOppcodeType::DistanceSmall,    // 0x42
106    LzvnOppcodeType::DistanceSmall,    // 0x43
107    LzvnOppcodeType::DistanceSmall,    // 0x44
108    LzvnOppcodeType::DistanceSmall,    // 0x45
109    LzvnOppcodeType::DistancePrevious, // 0x46
110    LzvnOppcodeType::DistanceLarge,    // 0x47
111    LzvnOppcodeType::DistanceSmall,    // 0x48
112    LzvnOppcodeType::DistanceSmall,    // 0x49
113    LzvnOppcodeType::DistanceSmall,    // 0x4a
114    LzvnOppcodeType::DistanceSmall,    // 0x4b
115    LzvnOppcodeType::DistanceSmall,    // 0x4c
116    LzvnOppcodeType::DistanceSmall,    // 0x4d
117    LzvnOppcodeType::DistancePrevious, // 0x4e
118    LzvnOppcodeType::DistanceLarge,    // 0x4f
119    LzvnOppcodeType::DistanceSmall,    // 0x50
120    LzvnOppcodeType::DistanceSmall,    // 0x51
121    LzvnOppcodeType::DistanceSmall,    // 0x52
122    LzvnOppcodeType::DistanceSmall,    // 0x53
123    LzvnOppcodeType::DistanceSmall,    // 0x54
124    LzvnOppcodeType::DistanceSmall,    // 0x55
125    LzvnOppcodeType::DistancePrevious, // 0x56
126    LzvnOppcodeType::DistanceLarge,    // 0x57
127    LzvnOppcodeType::DistanceSmall,    // 0x58
128    LzvnOppcodeType::DistanceSmall,    // 0x59
129    LzvnOppcodeType::DistanceSmall,    // 0x5a
130    LzvnOppcodeType::DistanceSmall,    // 0x5b
131    LzvnOppcodeType::DistanceSmall,    // 0x5c
132    LzvnOppcodeType::DistanceSmall,    // 0x5d
133    LzvnOppcodeType::DistancePrevious, // 0x5e
134    LzvnOppcodeType::DistanceLarge,    // 0x5f
135    LzvnOppcodeType::DistanceSmall,    // 0x60
136    LzvnOppcodeType::DistanceSmall,    // 0x61
137    LzvnOppcodeType::DistanceSmall,    // 0x62
138    LzvnOppcodeType::DistanceSmall,    // 0x63
139    LzvnOppcodeType::DistanceSmall,    // 0x64
140    LzvnOppcodeType::DistanceSmall,    // 0x65
141    LzvnOppcodeType::DistancePrevious, // 0x66
142    LzvnOppcodeType::DistanceLarge,    // 0x67
143    LzvnOppcodeType::DistanceSmall,    // 0x68
144    LzvnOppcodeType::DistanceSmall,    // 0x69
145    LzvnOppcodeType::DistanceSmall,    // 0x6a
146    LzvnOppcodeType::DistanceSmall,    // 0x6b
147    LzvnOppcodeType::DistanceSmall,    // 0x6c
148    LzvnOppcodeType::DistanceSmall,    // 0x6d
149    LzvnOppcodeType::DistancePrevious, // 0x6e
150    LzvnOppcodeType::DistanceLarge,    // 0x6f
151    LzvnOppcodeType::Invalid,          // 0x70
152    LzvnOppcodeType::Invalid,          // 0x71
153    LzvnOppcodeType::Invalid,          // 0x72
154    LzvnOppcodeType::Invalid,          // 0x73
155    LzvnOppcodeType::Invalid,          // 0x74
156    LzvnOppcodeType::Invalid,          // 0x75
157    LzvnOppcodeType::Invalid,          // 0x76
158    LzvnOppcodeType::Invalid,          // 0x77
159    LzvnOppcodeType::Invalid,          // 0x78
160    LzvnOppcodeType::Invalid,          // 0x79
161    LzvnOppcodeType::Invalid,          // 0x7a
162    LzvnOppcodeType::Invalid,          // 0x7b
163    LzvnOppcodeType::Invalid,          // 0x7c
164    LzvnOppcodeType::Invalid,          // 0x7d
165    LzvnOppcodeType::Invalid,          // 0x7e
166    LzvnOppcodeType::Invalid,          // 0x7f
167    LzvnOppcodeType::DistanceSmall,    // 0x80
168    LzvnOppcodeType::DistanceSmall,    // 0x81
169    LzvnOppcodeType::DistanceSmall,    // 0x82
170    LzvnOppcodeType::DistanceSmall,    // 0x83
171    LzvnOppcodeType::DistanceSmall,    // 0x84
172    LzvnOppcodeType::DistanceSmall,    // 0x85
173    LzvnOppcodeType::DistancePrevious, // 0x86
174    LzvnOppcodeType::DistanceLarge,    // 0x87
175    LzvnOppcodeType::DistanceSmall,    // 0x88
176    LzvnOppcodeType::DistanceSmall,    // 0x89
177    LzvnOppcodeType::DistanceSmall,    // 0x8a
178    LzvnOppcodeType::DistanceSmall,    // 0x8b
179    LzvnOppcodeType::DistanceSmall,    // 0x8c
180    LzvnOppcodeType::DistanceSmall,    // 0x8d
181    LzvnOppcodeType::DistancePrevious, // 0x8e
182    LzvnOppcodeType::DistanceLarge,    // 0x8f
183    LzvnOppcodeType::DistanceSmall,    // 0x90
184    LzvnOppcodeType::DistanceSmall,    // 0x91
185    LzvnOppcodeType::DistanceSmall,    // 0x92
186    LzvnOppcodeType::DistanceSmall,    // 0x93
187    LzvnOppcodeType::DistanceSmall,    // 0x94
188    LzvnOppcodeType::DistanceSmall,    // 0x95
189    LzvnOppcodeType::DistancePrevious, // 0x96
190    LzvnOppcodeType::DistanceLarge,    // 0x97
191    LzvnOppcodeType::DistanceSmall,    // 0x98
192    LzvnOppcodeType::DistanceSmall,    // 0x99
193    LzvnOppcodeType::DistanceSmall,    // 0x9a
194    LzvnOppcodeType::DistanceSmall,    // 0x9b
195    LzvnOppcodeType::DistanceSmall,    // 0x9c
196    LzvnOppcodeType::DistanceSmall,    // 0x9d
197    LzvnOppcodeType::DistancePrevious, // 0x9e
198    LzvnOppcodeType::DistanceLarge,    // 0x9f
199    LzvnOppcodeType::DistanceMedium,   // 0xa0
200    LzvnOppcodeType::DistanceMedium,   // 0xa1
201    LzvnOppcodeType::DistanceMedium,   // 0xa2
202    LzvnOppcodeType::DistanceMedium,   // 0xa3
203    LzvnOppcodeType::DistanceMedium,   // 0xa4
204    LzvnOppcodeType::DistanceMedium,   // 0xa5
205    LzvnOppcodeType::DistanceMedium,   // 0xa6
206    LzvnOppcodeType::DistanceMedium,   // 0xa7
207    LzvnOppcodeType::DistanceMedium,   // 0xa8
208    LzvnOppcodeType::DistanceMedium,   // 0xa9
209    LzvnOppcodeType::DistanceMedium,   // 0xaa
210    LzvnOppcodeType::DistanceMedium,   // 0xab
211    LzvnOppcodeType::DistanceMedium,   // 0xac
212    LzvnOppcodeType::DistanceMedium,   // 0xad
213    LzvnOppcodeType::DistanceMedium,   // 0xae
214    LzvnOppcodeType::DistanceMedium,   // 0xaf
215    LzvnOppcodeType::DistanceMedium,   // 0xb0
216    LzvnOppcodeType::DistanceMedium,   // 0xb1
217    LzvnOppcodeType::DistanceMedium,   // 0xb2
218    LzvnOppcodeType::DistanceMedium,   // 0xb3
219    LzvnOppcodeType::DistanceMedium,   // 0xb4
220    LzvnOppcodeType::DistanceMedium,   // 0xb5
221    LzvnOppcodeType::DistanceMedium,   // 0xb6
222    LzvnOppcodeType::DistanceMedium,   // 0xb7
223    LzvnOppcodeType::DistanceMedium,   // 0xb8
224    LzvnOppcodeType::DistanceMedium,   // 0xb9
225    LzvnOppcodeType::DistanceMedium,   // 0xba
226    LzvnOppcodeType::DistanceMedium,   // 0xbb
227    LzvnOppcodeType::DistanceMedium,   // 0xbc
228    LzvnOppcodeType::DistanceMedium,   // 0xbd
229    LzvnOppcodeType::DistanceMedium,   // 0xbe
230    LzvnOppcodeType::DistanceMedium,   // 0xbf
231    LzvnOppcodeType::DistanceSmall,    // 0xc0
232    LzvnOppcodeType::DistanceSmall,    // 0xc1
233    LzvnOppcodeType::DistanceSmall,    // 0xc2
234    LzvnOppcodeType::DistanceSmall,    // 0xc3
235    LzvnOppcodeType::DistanceSmall,    // 0xc4
236    LzvnOppcodeType::DistanceSmall,    // 0xc5
237    LzvnOppcodeType::DistancePrevious, // 0xc6
238    LzvnOppcodeType::DistanceLarge,    // 0xc7
239    LzvnOppcodeType::DistanceSmall,    // 0xc8
240    LzvnOppcodeType::DistanceSmall,    // 0xc9
241    LzvnOppcodeType::DistanceSmall,    // 0xca
242    LzvnOppcodeType::DistanceSmall,    // 0xcb
243    LzvnOppcodeType::DistanceSmall,    // 0xcc
244    LzvnOppcodeType::DistanceSmall,    // 0xcd
245    LzvnOppcodeType::DistancePrevious, // 0xce
246    LzvnOppcodeType::DistanceLarge,    // 0xcf
247    LzvnOppcodeType::Invalid,          // 0xd0
248    LzvnOppcodeType::Invalid,          // 0xd1
249    LzvnOppcodeType::Invalid,          // 0xd2
250    LzvnOppcodeType::Invalid,          // 0xd3
251    LzvnOppcodeType::Invalid,          // 0xd4
252    LzvnOppcodeType::Invalid,          // 0xd5
253    LzvnOppcodeType::Invalid,          // 0xd6
254    LzvnOppcodeType::Invalid,          // 0xd7
255    LzvnOppcodeType::Invalid,          // 0xd8
256    LzvnOppcodeType::Invalid,          // 0xd9
257    LzvnOppcodeType::Invalid,          // 0xda
258    LzvnOppcodeType::Invalid,          // 0xdb
259    LzvnOppcodeType::Invalid,          // 0xdc
260    LzvnOppcodeType::Invalid,          // 0xdd
261    LzvnOppcodeType::Invalid,          // 0xde
262    LzvnOppcodeType::Invalid,          // 0xdf
263    LzvnOppcodeType::LiteralLarge,     // 0xe0
264    LzvnOppcodeType::LiteralSmall,     // 0xe1
265    LzvnOppcodeType::LiteralSmall,     // 0xe2
266    LzvnOppcodeType::LiteralSmall,     // 0xe3
267    LzvnOppcodeType::LiteralSmall,     // 0xe4
268    LzvnOppcodeType::LiteralSmall,     // 0xe5
269    LzvnOppcodeType::LiteralSmall,     // 0xe6
270    LzvnOppcodeType::LiteralSmall,     // 0xe7
271    LzvnOppcodeType::LiteralSmall,     // 0xe8
272    LzvnOppcodeType::LiteralSmall,     // 0xe9
273    LzvnOppcodeType::LiteralSmall,     // 0xea
274    LzvnOppcodeType::LiteralSmall,     // 0xeb
275    LzvnOppcodeType::LiteralSmall,     // 0xec
276    LzvnOppcodeType::LiteralSmall,     // 0xed
277    LzvnOppcodeType::LiteralSmall,     // 0xee
278    LzvnOppcodeType::LiteralSmall,     // 0xef
279    LzvnOppcodeType::MatchLarge,       // 0xf0
280    LzvnOppcodeType::MatchSmall,       // 0xf1
281    LzvnOppcodeType::MatchSmall,       // 0xf2
282    LzvnOppcodeType::MatchSmall,       // 0xf3
283    LzvnOppcodeType::MatchSmall,       // 0xf4
284    LzvnOppcodeType::MatchSmall,       // 0xf5
285    LzvnOppcodeType::MatchSmall,       // 0xf6
286    LzvnOppcodeType::MatchSmall,       // 0xf7
287    LzvnOppcodeType::MatchSmall,       // 0xf8
288    LzvnOppcodeType::MatchSmall,       // 0xf9
289    LzvnOppcodeType::MatchSmall,       // 0xfa
290    LzvnOppcodeType::MatchSmall,       // 0xfb
291    LzvnOppcodeType::MatchSmall,       // 0xfc
292    LzvnOppcodeType::MatchSmall,       // 0xfd
293    LzvnOppcodeType::MatchSmall,       // 0xfe
294    LzvnOppcodeType::MatchSmall,       // 0xff
295];
296
297/// Context for decompressing LZVN compressed data.
298pub struct LzvnContext {
299    /// Mediator.
300    mediator: MediatorReference,
301
302    /// Uncompressed data size.
303    pub uncompressed_data_size: usize,
304}
305
306impl LzvnContext {
307    /// Creates a new context.
308    pub fn new() -> Self {
309        Self {
310            mediator: Mediator::current(),
311            uncompressed_data_size: 0,
312        }
313    }
314
315    /// Decompress data.
316    pub fn decompress(
317        &mut self,
318        compressed_data: &[u8],
319        uncompressed_data: &mut [u8],
320    ) -> Result<(), ErrorTrace> {
321        let mut compressed_data_offset: usize = 0;
322        let compressed_data_size: usize = compressed_data.len();
323
324        let mut uncompressed_data_offset: usize = 0;
325        let uncompressed_data_size: usize = uncompressed_data.len();
326
327        if self.mediator.debug_output {
328            self.mediator.debug_print(format!("LzvnContext {{\n",));
329        }
330        // Note that distance can be reused by subsequent oppcodes.
331        let mut distance: u16 = 0;
332
333        while compressed_data_offset < compressed_data_size {
334            if uncompressed_data_offset >= uncompressed_data_size {
335                break;
336            }
337            if compressed_data_offset >= compressed_data_size {
338                return Err(keramics_core::error_trace_new!(
339                    "Invalid compressed data value too small"
340                ));
341            }
342            let oppcode: u8 = compressed_data[compressed_data_offset];
343            compressed_data_offset += 1;
344
345            if self.mediator.debug_output {
346                self.mediator
347                    .debug_print(format!("    oppcode: 0x{:02x}\n", oppcode));
348            }
349            let mut literal_size: u16 = 0;
350            let mut match_size: u16 = 0;
351
352            match &LZVN_OPPCODE_TYPES[oppcode as usize] {
353                LzvnOppcodeType::DistanceLarge => {
354                    if 2 > compressed_data_size - compressed_data_offset {
355                        return Err(keramics_core::error_trace_new!(
356                            "Invalid compressed data value too small"
357                        ));
358                    }
359                    let oppcode_value: u8 = compressed_data[compressed_data_offset];
360                    compressed_data_offset += 1;
361
362                    literal_size = (oppcode as u16 & 0xc0) >> 6;
363                    match_size = ((oppcode as u16 & 0x38) >> 3) + 3;
364                    distance = ((compressed_data[compressed_data_offset] as u16) << 8)
365                        | oppcode_value as u16;
366
367                    compressed_data_offset += 1;
368                }
369                LzvnOppcodeType::DistanceMedium => {
370                    if 2 > compressed_data_size - compressed_data_offset {
371                        return Err(keramics_core::error_trace_new!(
372                            "Invalid compressed data value too small"
373                        ));
374                    }
375                    let oppcode_value: u8 = compressed_data[compressed_data_offset];
376                    compressed_data_offset += 1;
377
378                    literal_size = (oppcode as u16 & 0x18) >> 3;
379                    match_size =
380                        (((oppcode as u16 & 0x07) << 2) | (oppcode_value as u16 & 0x03)) + 3;
381                    distance = ((compressed_data[compressed_data_offset] as u16) << 6)
382                        | ((oppcode_value as u16 & 0xfc) >> 2);
383
384                    compressed_data_offset += 1;
385                }
386                LzvnOppcodeType::DistancePrevious => {
387                    literal_size = (oppcode as u16 & 0xc0) >> 6;
388                    match_size = ((oppcode as u16 & 0x38) >> 3) + 3;
389                }
390                LzvnOppcodeType::DistanceSmall => {
391                    if compressed_data_offset >= compressed_data_size {
392                        return Err(keramics_core::error_trace_new!(
393                            "Invalid compressed data value too small"
394                        ));
395                    }
396                    literal_size = (oppcode as u16 & 0xc0) >> 6;
397                    match_size = ((oppcode as u16 & 0x38) >> 3) + 3;
398                    distance = ((oppcode as u16 & 0x07) << 8)
399                        | compressed_data[compressed_data_offset] as u16;
400
401                    compressed_data_offset += 1;
402                }
403                LzvnOppcodeType::LiteralLarge => {
404                    if compressed_data_offset >= compressed_data_size {
405                        return Err(keramics_core::error_trace_new!(
406                            "Invalid compressed data value too small"
407                        ));
408                    }
409                    literal_size = compressed_data[compressed_data_offset] as u16 + 16;
410
411                    compressed_data_offset += 1;
412                }
413                LzvnOppcodeType::LiteralSmall => {
414                    literal_size = oppcode as u16 & 0x0f;
415                }
416                LzvnOppcodeType::MatchLarge => {
417                    if compressed_data_offset >= compressed_data_size {
418                        return Err(keramics_core::error_trace_new!(
419                            "Invalid compressed data value too small"
420                        ));
421                    }
422                    match_size = compressed_data[compressed_data_offset] as u16 + 16;
423
424                    compressed_data_offset += 1;
425                }
426                LzvnOppcodeType::MatchSmall => {
427                    match_size = oppcode as u16 & 0x0f;
428                }
429                LzvnOppcodeType::EndOfStream => {
430                    break;
431                }
432                LzvnOppcodeType::None => {}
433                LzvnOppcodeType::Invalid => {
434                    return Err(keramics_core::error_trace_new!(format!(
435                        "Invalid oppcode: 0x{:02x}",
436                        oppcode
437                    )));
438                }
439            };
440            if self.mediator.debug_output {
441                self.mediator
442                    .debug_print(format!("    literal_size: {}\n", literal_size));
443                self.mediator
444                    .debug_print(format!("    match_size: {}\n", match_size));
445                self.mediator
446                    .debug_print(format!("    distance: {}\n", distance));
447            }
448            if literal_size > 0 {
449                if literal_size as usize > compressed_data_size - compressed_data_offset {
450                    return Err(keramics_core::error_trace_new!(
451                        "Literal size value exceeds compressed data size"
452                    ));
453                }
454                if literal_size as usize > uncompressed_data_size - uncompressed_data_offset {
455                    return Err(keramics_core::error_trace_new!(
456                        "Literal size value exceeds uncompressed data size"
457                    ));
458                }
459                let compressed_data_end_offset: usize =
460                    compressed_data_offset + literal_size as usize;
461                let uncompressed_data_end_offset: usize =
462                    uncompressed_data_offset + literal_size as usize;
463
464                if self.mediator.debug_output {
465                    self.mediator.debug_print(format!("    literal data:\n"));
466                    self.mediator.debug_print_data(
467                        &compressed_data[compressed_data_offset..compressed_data_end_offset],
468                        true,
469                    );
470                }
471                uncompressed_data[uncompressed_data_offset..uncompressed_data_end_offset]
472                    .copy_from_slice(
473                        &compressed_data[compressed_data_offset..compressed_data_end_offset],
474                    );
475
476                compressed_data_offset = compressed_data_end_offset;
477                uncompressed_data_offset = uncompressed_data_end_offset;
478            }
479            if match_size > 0 {
480                if distance as usize > uncompressed_data_offset {
481                    return Err(keramics_core::error_trace_new!(
482                        "Invalid distance value exceeds uncompressed data offset"
483                    ));
484                }
485                if match_size as usize > uncompressed_data_size - uncompressed_data_offset {
486                    return Err(keramics_core::error_trace_new!(
487                        "Invalid match size value exceeds uncompressed data size"
488                    ));
489                }
490                let match_offset: usize = uncompressed_data_offset - distance as usize;
491                let mut match_end_offset: usize = match_offset;
492
493                for _ in 0..match_size {
494                    uncompressed_data[uncompressed_data_offset] =
495                        uncompressed_data[match_end_offset];
496
497                    match_end_offset += 1;
498                    uncompressed_data_offset += 1;
499                }
500                if self.mediator.debug_output {
501                    self.mediator
502                        .debug_print(format!("    match offset: {}\n", match_offset));
503                    self.mediator.debug_print(format!("    match data:\n"));
504                    self.mediator
505                        .debug_print_data(&uncompressed_data[match_offset..match_end_offset], true);
506                }
507            }
508        }
509        if self.mediator.debug_output {
510            self.mediator.debug_print(format!("}}\n\n",));
511        }
512        self.uncompressed_data_size = uncompressed_data_offset;
513
514        Ok(())
515    }
516}
517
518#[cfg(test)]
519mod tests {
520    use super::*;
521
522    #[test]
523    fn test_decompress() -> Result<(), ErrorTrace> {
524        let test_data: [u8; 29] = [
525            0xe0, 0x03, 0x4d, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65,
526            0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
527            0x00,
528        ];
529        let mut test_context: LzvnContext = LzvnContext::new();
530
531        let mut uncompressed_data: Vec<u8> = vec![0; 19];
532        test_context.decompress(&test_data, &mut uncompressed_data)?;
533        assert_eq!(test_context.uncompressed_data_size, 19);
534
535        let expected_data: [u8; 19] = [
536            0x4d, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20,
537            0x66, 0x69, 0x6c, 0x65, 0x0a,
538        ];
539        assert_eq!(uncompressed_data, expected_data);
540
541        Ok(())
542    }
543}