building_blocks_storage/array/
compression.rs

1use crate::{Array, Compressed, Compression, FromBytesCompression};
2
3use building_blocks_core::prelude::*;
4
5/// A compression algorithm for arrays that avoid the overhead of serialization but ignores endianness and therefore isn't
6/// portable.
7#[derive(Clone, Copy, Debug)]
8pub struct FastArrayCompression<N, C> {
9    pub channels_compression: C,
10    marker: std::marker::PhantomData<N>,
11}
12
13impl<N, C> FastArrayCompression<N, C> {
14    pub fn new(channels_compression: C) -> Self {
15        Self {
16            channels_compression,
17            marker: Default::default(),
18        }
19    }
20
21    pub fn channels_compression(&self) -> &C {
22        &self.channels_compression
23    }
24}
25
26impl<N, C, B> FromBytesCompression<B> for FastArrayCompression<N, C>
27where
28    C: FromBytesCompression<B>,
29{
30    fn from_bytes_compression(bytes_compression: B) -> Self {
31        Self::new(C::from_bytes_compression(bytes_compression))
32    }
33}
34
35/// A compressed `Array` that decompresses quickly but only on the same platform where it was compressed.
36#[derive(Clone)]
37pub struct FastCompressedArray<N, C>
38where
39    C: Compression,
40{
41    compressed_channels: C::CompressedData,
42    extent: ExtentN<N>,
43}
44
45impl<N, C> FastCompressedArray<N, C>
46where
47    C: Compression,
48{
49    pub fn compressed_channels(&self) -> &C::CompressedData {
50        &self.compressed_channels
51    }
52
53    pub fn extent(&self) -> &ExtentN<N> {
54        &self.extent
55    }
56
57    pub fn into_parts(self) -> (C::CompressedData, ExtentN<N>) {
58        (self.compressed_channels, self.extent)
59    }
60}
61
62impl<N, C> Compression for FastArrayCompression<N, C>
63where
64    PointN<N>: Copy,
65    C: Compression,
66{
67    type Data = Array<N, C::Data>;
68    type CompressedData = FastCompressedArray<N, C>;
69
70    fn compress(&self, data: &Self::Data) -> Compressed<Self> {
71        let compressed_channels = self.channels_compression.compress(data.channels()).take();
72
73        Compressed::new(FastCompressedArray {
74            compressed_channels,
75            extent: data.extent,
76        })
77    }
78
79    fn decompress(compressed: &Self::CompressedData) -> Self::Data {
80        Array::new(
81            compressed.extent,
82            C::decompress(&compressed.compressed_channels),
83        )
84    }
85}
86
87pub mod multichannel_aliases {
88    use super::*;
89    use crate::array::channels::multichannel::multichannel_aliases::*;
90
91    pub type FastArrayCompressionNx1<N, By, A> =
92        FastArrayCompression<N, FastChannelsCompression1<By, A>>;
93    pub type FastArrayCompressionNx2<N, By, A, B> =
94        FastArrayCompression<N, FastChannelsCompression2<By, A, B>>;
95    pub type FastArrayCompressionNx3<N, By, A, B, C> =
96        FastArrayCompression<N, FastChannelsCompression3<By, A, B, C>>;
97    pub type FastArrayCompressionNx4<N, By, A, B, C, D> =
98        FastArrayCompression<N, FastChannelsCompression4<By, A, B, C, D>>;
99    pub type FastArrayCompressionNx5<N, By, A, B, C, D, E> =
100        FastArrayCompression<N, FastChannelsCompression5<By, A, B, C, D, E>>;
101    pub type FastArrayCompressionNx6<N, By, A, B, C, D, E, F> =
102        FastArrayCompression<N, FastChannelsCompression6<By, A, B, C, D, E, F>>;
103}
104
105pub use multichannel_aliases::*;
106
107// ████████╗███████╗███████╗████████╗
108// ╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝
109//    ██║   █████╗  ███████╗   ██║
110//    ██║   ██╔══╝  ╚════██║   ██║
111//    ██║   ███████╗███████║   ██║
112//    ╚═╝   ╚══════╝╚══════╝   ╚═╝
113
114#[cfg(test)]
115mod test {
116    use super::*;
117    use crate::{Array3x1, BytesCompression};
118
119    use crate::test_utilities::sphere_bit_array;
120    use utilities::test::test_print;
121
122    #[cfg(feature = "lz4")]
123    use crate::Lz4;
124    #[cfg(feature = "snap")]
125    use crate::Snappy;
126
127    #[cfg(feature = "snap")]
128    #[test]
129    fn sphere_array_compression_rate_snappy() {
130        sphere_array_compression_rate(Snappy, 32);
131        sphere_array_compression_rate(Snappy, 64);
132        sphere_array_compression_rate(Snappy, 128);
133    }
134
135    #[cfg(feature = "snap")]
136    #[test]
137    fn homogeneous_array_compression_rate_snappy() {
138        homogeneous_array_compression_rate(Snappy, 32);
139        homogeneous_array_compression_rate(Snappy, 64);
140        homogeneous_array_compression_rate(Snappy, 128);
141    }
142
143    #[cfg(feature = "lz4")]
144    #[test]
145    fn sphere_array_compression_rate_lz4() {
146        sphere_array_compression_rate(Lz4 { level: 10 }, 32);
147        sphere_array_compression_rate(Lz4 { level: 10 }, 64);
148        sphere_array_compression_rate(Lz4 { level: 10 }, 128);
149    }
150
151    #[cfg(feature = "lz4")]
152    #[test]
153    fn homogeneous_array_compression_rate_lz4() {
154        homogeneous_array_compression_rate(Lz4 { level: 10 }, 32);
155        homogeneous_array_compression_rate(Lz4 { level: 10 }, 64);
156        homogeneous_array_compression_rate(Lz4 { level: 10 }, 128);
157    }
158
159    fn homogeneous_array_compression_rate<B: BytesCompression>(compression: B, side_length: i32) {
160        let extent = Extent3i::from_min_and_shape(Point3i::ZERO, Point3i::fill(side_length));
161        let array = Array3x1::fill_with(extent, |_p| 0u16);
162        array_compression_rate(&array, compression);
163    }
164
165    fn sphere_array_compression_rate<B: BytesCompression>(compression: B, side_length: i32) {
166        let array = sphere_bit_array(side_length, 1u16, 0u16).0;
167        array_compression_rate(&array, compression);
168    }
169
170    fn array_compression_rate<B: BytesCompression>(array: &Array3x1<u16>, bytes_compression: B) {
171        let source_size_bytes = array.extent().num_points() * 2;
172
173        let compression = FastArrayCompressionNx1::from_bytes_compression(bytes_compression);
174
175        let compressed_array = compression.compress(array).take();
176
177        let compressed_size_bytes = compressed_array
178            .compressed_channels()
179            .compressed_bytes()
180            .len();
181
182        test_print(&format!(
183            "source = {} bytes, compressed = {} bytes; rate = {:.1}%\n",
184            source_size_bytes,
185            compressed_size_bytes,
186            100.0 * (compressed_size_bytes as f32 / source_size_bytes as f32)
187        ));
188    }
189}