1use keramics_core::ErrorTrace;
19use keramics_core::mediator::{Mediator, MediatorReference};
20
21#[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
37const LZVN_OPPCODE_TYPES: [LzvnOppcodeType; 256] = [
39 LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::EndOfStream, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::None, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::None, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceMedium, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistanceSmall, LzvnOppcodeType::DistancePrevious, LzvnOppcodeType::DistanceLarge, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::Invalid, LzvnOppcodeType::LiteralLarge, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::LiteralSmall, LzvnOppcodeType::MatchLarge, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, LzvnOppcodeType::MatchSmall, ];
296
297pub struct LzvnContext {
299 mediator: MediatorReference,
301
302 pub uncompressed_data_size: usize,
304}
305
306impl LzvnContext {
307 pub fn new() -> Self {
309 Self {
310 mediator: Mediator::current(),
311 uncompressed_data_size: 0,
312 }
313 }
314
315 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 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}