1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use super::Blob;
use brotli::{enc::BrotliEncoderParams, BrotliCompress, BrotliDecompress};
use flate2::{
	bufread::{GzDecoder, GzEncoder},
	Compression,
};
use std::io::{Cursor, Read};

use clap::ValueEnum;
use enumset::EnumSetType;

#[derive(Debug, EnumSetType, ValueEnum)]
pub enum Precompression {
	Uncompressed,
	Gzip,
	Brotli,
}

#[allow(dead_code)]
pub fn compress(data: Blob, precompression: &Precompression) -> Blob {
	match precompression {
		Precompression::Uncompressed => data,
		Precompression::Gzip => compress_gzip(data),
		Precompression::Brotli => compress_brotli(data),
	}
}

pub fn decompress(data: Blob, precompression: &Precompression) -> Blob {
	match precompression {
		Precompression::Uncompressed => data,
		Precompression::Gzip => decompress_gzip(data),
		Precompression::Brotli => decompress_brotli(data),
	}
}

pub fn compress_gzip(data: Blob) -> Blob {
	let mut result: Vec<u8> = Vec::new();
	GzEncoder::new(data.as_slice(), Compression::best())
		.read_to_end(&mut result)
		.expect("Error in compress_gzip");

	Blob::from_vec(result)
}

pub fn decompress_gzip(data: Blob) -> Blob {
	let mut result: Vec<u8> = Vec::new();
	GzDecoder::new(data.as_slice())
		.read_to_end(&mut result)
		.expect("Error in decompress_gzip");

	Blob::from_vec(result)
}

pub fn compress_brotli(data: Blob) -> Blob {
	let params = BrotliEncoderParams {
		quality: 11,
		size_hint: data.len(),
		..Default::default()
	};
	let mut cursor = Cursor::new(data.as_slice());
	let mut result: Vec<u8> = Vec::new();
	BrotliCompress(&mut cursor, &mut result, &params).expect("Error in compress_brotli");

	Blob::from_vec(result)
}

pub fn decompress_brotli(data: Blob) -> Blob {
	let mut cursor = Cursor::new(data.as_slice());
	let mut result: Vec<u8> = Vec::new();
	BrotliDecompress(&mut cursor, &mut result).expect("Error in decompress_brotli");

	Blob::from_vec(result)
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn verify_brotli() {
		let data1 = random_data(100000);
		let data2 = decompress_brotli(compress_brotli(data1.clone()));
		assert_eq!(data1, data2);
	}

	#[test]
	fn verify_gzip() {
		let data1 = random_data(100000);
		let data2 = decompress_gzip(compress_gzip(data1.clone()));
		assert_eq!(data1, data2);
	}

	fn random_data(size: usize) -> Blob {
		let mut vec: Vec<u8> = Vec::new();
		vec.resize(size, 0);
		(0..size).for_each(|i| {
			vec[i] = (((i as f64 + 1.78123).cos() * 6_513_814_013_423.454).fract() * 256f64) as u8;
		});

		Blob::from_vec(vec)
	}
}