/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/block/writer.rs
Line | Count | Source |
1 | | //! Composable structures to handle writing an image. |
2 | | |
3 | | use std::{fmt::Debug, io::Seek, iter::Peekable, ops::Not}; |
4 | | |
5 | | use smallvec::alloc::collections::BTreeMap; |
6 | | |
7 | | use crate::{ |
8 | | block::{chunk::Chunk, UncompressedBlock}, |
9 | | error::{usize_to_u64, Error, Result, UnitResult}, |
10 | | io::{Data, Tracking, Write}, |
11 | | meta::{attribute::LineOrder, Headers, MetaData, OffsetTables}, |
12 | | }; |
13 | | |
14 | | /// Write an exr file by writing one chunk after another in a closure. |
15 | | /// In the closure, you are provided a chunk writer, which should be used to |
16 | | /// write all the chunks. Assumes the your write destination is buffered. |
17 | 78 | pub fn write_chunks_with<W: Write + Seek>( |
18 | 78 | buffered_write: W, |
19 | 78 | headers: Headers, |
20 | 78 | pedantic: bool, |
21 | 78 | write_chunks: impl FnOnce(MetaData, &mut ChunkWriter<W>) -> UnitResult, |
22 | 78 | ) -> UnitResult { |
23 | | // this closure approach ensures that after writing all chunks, the file is |
24 | | // always completed and checked and flushed |
25 | 78 | let (meta, mut writer) = ChunkWriter::new_for_buffered(buffered_write, headers, pedantic)?; |
26 | 78 | write_chunks(meta, &mut writer)?; |
27 | 78 | writer.complete_meta_data() |
28 | 78 | } Unexecuted instantiation: exr::block::writer::write_chunks_with::<_, _> Unexecuted instantiation: exr::block::writer::write_chunks_with::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}>Unexecuted instantiation: exr::block::writer::write_chunks_with::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}>Unexecuted instantiation: exr::block::writer::write_chunks_with::<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}>exr::block::writer::write_chunks_with::<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}>Line | Count | Source | 17 | 78 | pub fn write_chunks_with<W: Write + Seek>( | 18 | 78 | buffered_write: W, | 19 | 78 | headers: Headers, | 20 | 78 | pedantic: bool, | 21 | 78 | write_chunks: impl FnOnce(MetaData, &mut ChunkWriter<W>) -> UnitResult, | 22 | 78 | ) -> UnitResult { | 23 | | // this closure approach ensures that after writing all chunks, the file is | 24 | | // always completed and checked and flushed | 25 | 78 | let (meta, mut writer) = ChunkWriter::new_for_buffered(buffered_write, headers, pedantic)?; | 26 | 78 | write_chunks(meta, &mut writer)?; | 27 | 78 | writer.complete_meta_data() | 28 | 78 | } |
|
29 | | |
30 | | /// Can consume compressed pixel chunks, writing them a file. |
31 | | /// Use `sequential_blocks_compressor` or `parallel_blocks_compressor` to |
32 | | /// compress your data, or use `compress_all_blocks_sequential` or |
33 | | /// `compress_all_blocks_parallel`. Use `on_progress` to obtain a new writer |
34 | | /// that triggers a callback for each block. |
35 | | // #[must_use] |
36 | | #[derive(Debug)] |
37 | | #[must_use] |
38 | | pub struct ChunkWriter<W> { |
39 | | header_count: usize, |
40 | | byte_writer: Tracking<W>, |
41 | | chunk_indices_byte_location: std::ops::Range<usize>, |
42 | | chunk_indices_increasing_y: OffsetTables, |
43 | | chunk_count: usize, // TODO compose? |
44 | | } |
45 | | |
46 | | /// A new writer that triggers a callback |
47 | | /// for each block written to the inner writer. |
48 | | #[derive(Debug)] |
49 | | #[must_use] |
50 | | pub struct OnProgressChunkWriter<'w, W, F> { |
51 | | chunk_writer: &'w mut W, |
52 | | written_chunks: usize, |
53 | | on_progress: F, |
54 | | } |
55 | | |
56 | | /// Write chunks to a byte destination. |
57 | | /// Then write each chunk with `writer.write_chunk(chunk)`. |
58 | | pub trait ChunksWriter: Sized { |
59 | | /// The total number of chunks that the complete file will contain. |
60 | | fn total_chunks_count(&self) -> usize; |
61 | | |
62 | | /// Any more calls will result in an error and have no effect. |
63 | | /// If writing results in an error, the file and the writer |
64 | | /// may remain in an invalid state and should not be used further. |
65 | | /// Errors when the chunk at this index was already written. |
66 | | fn write_chunk(&mut self, index_in_header_increasing_y: usize, chunk: Chunk) -> UnitResult; |
67 | | |
68 | | /// Obtain a new writer that calls the specified closure for each block that |
69 | | /// is written to this writer. |
70 | 78 | fn on_progress<F>(&mut self, on_progress: F) -> OnProgressChunkWriter<'_, Self, F> |
71 | 78 | where |
72 | 78 | F: FnMut(f64), |
73 | | { |
74 | 78 | OnProgressChunkWriter { |
75 | 78 | chunk_writer: self, |
76 | 78 | written_chunks: 0, |
77 | 78 | on_progress, |
78 | 78 | } |
79 | 78 | } Unexecuted instantiation: <_ as exr::block::writer::ChunksWriter>::on_progress::<_> Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::on_progress::<fn(f64)> <exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::on_progress::<fn(f64)> Line | Count | Source | 70 | 78 | fn on_progress<F>(&mut self, on_progress: F) -> OnProgressChunkWriter<'_, Self, F> | 71 | 78 | where | 72 | 78 | F: FnMut(f64), | 73 | | { | 74 | 78 | OnProgressChunkWriter { | 75 | 78 | chunk_writer: self, | 76 | 78 | written_chunks: 0, | 77 | 78 | on_progress, | 78 | 78 | } | 79 | 78 | } |
|
80 | | |
81 | | /// Obtain a new writer that can compress blocks to chunks, which are then |
82 | | /// passed to this writer. |
83 | 0 | fn sequential_blocks_compressor<'w>( |
84 | 0 | &'w mut self, |
85 | 0 | meta: &'w MetaData, |
86 | 0 | ) -> SequentialBlocksCompressor<'w, Self> { |
87 | 0 | SequentialBlocksCompressor::new(meta, self) |
88 | 0 | } Unexecuted instantiation: <_ as exr::block::writer::ChunksWriter>::sequential_blocks_compressor Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::sequential_blocks_compressor Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::sequential_blocks_compressor |
89 | | |
90 | | #[cfg(feature = "rayon")] |
91 | | /// Obtain a new writer that can compress blocks to chunks on multiple |
92 | | /// threads, which are then passed to this writer. Returns none if the |
93 | | /// sequential compressor should be used instead (thread pool creation |
94 | | /// failure or too large performance overhead). |
95 | 78 | fn parallel_blocks_compressor<'w>( |
96 | 78 | &'w mut self, |
97 | 78 | meta: &'w MetaData, |
98 | 78 | ) -> Option<ParallelBlocksCompressor<'w, Self>> { |
99 | 78 | ParallelBlocksCompressor::new(meta, self) |
100 | 78 | } Unexecuted instantiation: <_ as exr::block::writer::ChunksWriter>::parallel_blocks_compressor Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::parallel_blocks_compressor <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::parallel_blocks_compressor Line | Count | Source | 95 | 78 | fn parallel_blocks_compressor<'w>( | 96 | 78 | &'w mut self, | 97 | 78 | meta: &'w MetaData, | 98 | 78 | ) -> Option<ParallelBlocksCompressor<'w, Self>> { | 99 | 78 | ParallelBlocksCompressor::new(meta, self) | 100 | 78 | } |
|
101 | | |
102 | | /// Compresses all blocks to the file. |
103 | | /// The index of the block must be in increasing line order within the |
104 | | /// header. Obtain iterator with `MetaData::collect_ordered_blocks(...)` |
105 | | /// or similar methods. |
106 | 0 | fn compress_all_blocks_sequential( |
107 | 0 | mut self, |
108 | 0 | meta: &MetaData, |
109 | 0 | blocks: impl Iterator<Item = (usize, UncompressedBlock)>, |
110 | 0 | ) -> UnitResult { |
111 | 0 | let mut writer = self.sequential_blocks_compressor(meta); |
112 | | |
113 | | // TODO check block order if line order is not unspecified! |
114 | 0 | for (index_in_header_increasing_y, block) in blocks { |
115 | 0 | writer.compress_block(index_in_header_increasing_y, block)?; |
116 | | } |
117 | | |
118 | | // TODO debug_assert_eq!(self.is_complete()); |
119 | 0 | Ok(()) |
120 | 0 | } Unexecuted instantiation: <_ as exr::block::writer::ChunksWriter>::compress_all_blocks_sequential::<_> Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_sequential::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_sequential::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_sequential::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_sequential::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>> |
121 | | |
122 | | #[cfg(feature = "rayon")] |
123 | | /// Compresses all blocks to the file. |
124 | | /// The index of the block must be in increasing line order within the |
125 | | /// header. Obtain iterator with `MetaData::collect_ordered_blocks(...)` |
126 | | /// or similar methods. Will fallback to sequential processing where |
127 | | /// threads are not available, or where it would not speed up the process. |
128 | 78 | fn compress_all_blocks_parallel( |
129 | 78 | mut self, |
130 | 78 | meta: &MetaData, |
131 | 78 | blocks: impl Iterator<Item = (usize, UncompressedBlock)>, |
132 | 78 | ) -> UnitResult { |
133 | 78 | let mut parallel_writer = match self.parallel_blocks_compressor(meta) { |
134 | 0 | None => return self.compress_all_blocks_sequential(meta, blocks), |
135 | 78 | Some(writer) => writer, |
136 | | }; |
137 | | |
138 | | // TODO check block order if line order is not unspecified! |
139 | 722k | for (index_in_header_increasing_y, block) in blocks { |
140 | 722k | parallel_writer.add_block_to_compression_queue(index_in_header_increasing_y, block)?; |
141 | | } |
142 | | |
143 | | // TODO debug_assert_eq!(self.is_complete()); |
144 | 78 | Ok(()) |
145 | 78 | } Unexecuted instantiation: <_ as exr::block::writer::ChunksWriter>::compress_all_blocks_parallel::<_> Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_parallel::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_parallel::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_parallel::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>><exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::compress_all_blocks_parallel::<core::iter::adapters::map::Map<core::iter::adapters::flatten::FlatMap<core::iter::adapters::enumerate::Enumerate<core::slice::iter::Iter<exr::meta::header::Header>>, core::iter::adapters::map::Map<alloc::boxed::Box<dyn core::iter::traits::iterator::Iterator<Item = (usize, exr::meta::TileIndices)> + core::marker::Send>, exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}>, exr::block::enumerate_ordered_header_block_indices::{closure#0}>, <exr::meta::MetaData>::collect_ordered_blocks<<exr::meta::MetaData>::collect_ordered_block_data<<exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}::{closure#0}>::{closure#0}>::{closure#0}>>Line | Count | Source | 128 | 78 | fn compress_all_blocks_parallel( | 129 | 78 | mut self, | 130 | 78 | meta: &MetaData, | 131 | 78 | blocks: impl Iterator<Item = (usize, UncompressedBlock)>, | 132 | 78 | ) -> UnitResult { | 133 | 78 | let mut parallel_writer = match self.parallel_blocks_compressor(meta) { | 134 | 0 | None => return self.compress_all_blocks_sequential(meta, blocks), | 135 | 78 | Some(writer) => writer, | 136 | | }; | 137 | | | 138 | | // TODO check block order if line order is not unspecified! | 139 | 722k | for (index_in_header_increasing_y, block) in blocks { | 140 | 722k | parallel_writer.add_block_to_compression_queue(index_in_header_increasing_y, block)?; | 141 | | } | 142 | | | 143 | | // TODO debug_assert_eq!(self.is_complete()); | 144 | 78 | Ok(()) | 145 | 78 | } |
|
146 | | } |
147 | | |
148 | | impl<W> ChunksWriter for ChunkWriter<W> |
149 | | where |
150 | | W: Write + Seek, |
151 | | { |
152 | | /// The total number of chunks that the complete file will contain. |
153 | 1.44M | fn total_chunks_count(&self) -> usize { |
154 | 1.44M | self.chunk_count |
155 | 1.44M | } Unexecuted instantiation: <exr::block::writer::ChunkWriter<_> as exr::block::writer::ChunksWriter>::total_chunks_count Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::total_chunks_count <exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::total_chunks_count Line | Count | Source | 153 | 1.44M | fn total_chunks_count(&self) -> usize { | 154 | 1.44M | self.chunk_count | 155 | 1.44M | } |
|
156 | | |
157 | | /// Any more calls will result in an error and have no effect. |
158 | | /// If writing results in an error, the file and the writer |
159 | | /// may remain in an invalid state and should not be used further. |
160 | | /// Errors when the chunk at this index was already written. |
161 | 722k | fn write_chunk(&mut self, index_in_header_increasing_y: usize, chunk: Chunk) -> UnitResult { |
162 | 722k | let header_chunk_indices = &mut self.chunk_indices_increasing_y[chunk.layer_index]; |
163 | | |
164 | 722k | if index_in_header_increasing_y >= header_chunk_indices.len() { |
165 | 0 | return Err(Error::invalid(format!( |
166 | 0 | "chunk index {} exceeds maximum {} for layer {}", |
167 | 0 | index_in_header_increasing_y, |
168 | 0 | header_chunk_indices.len().saturating_sub(1), |
169 | 0 | chunk.layer_index |
170 | 0 | ))); |
171 | 722k | } |
172 | | |
173 | 722k | let chunk_index_slot = &mut header_chunk_indices[index_in_header_increasing_y]; |
174 | 722k | if *chunk_index_slot != 0 { |
175 | 0 | return Err(Error::invalid(format!( |
176 | 0 | "chunk at index {} is already written", |
177 | 0 | index_in_header_increasing_y |
178 | 0 | ))); |
179 | 722k | } |
180 | | |
181 | 722k | *chunk_index_slot = usize_to_u64(self.byte_writer.byte_position(), "seek position")?; |
182 | 722k | chunk.write(&mut self.byte_writer, self.header_count)?; |
183 | 722k | Ok(()) |
184 | 722k | } Unexecuted instantiation: <exr::block::writer::ChunkWriter<_> as exr::block::writer::ChunksWriter>::write_chunk Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::write_chunk <exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>> as exr::block::writer::ChunksWriter>::write_chunk Line | Count | Source | 161 | 722k | fn write_chunk(&mut self, index_in_header_increasing_y: usize, chunk: Chunk) -> UnitResult { | 162 | 722k | let header_chunk_indices = &mut self.chunk_indices_increasing_y[chunk.layer_index]; | 163 | | | 164 | 722k | if index_in_header_increasing_y >= header_chunk_indices.len() { | 165 | 0 | return Err(Error::invalid(format!( | 166 | 0 | "chunk index {} exceeds maximum {} for layer {}", | 167 | 0 | index_in_header_increasing_y, | 168 | 0 | header_chunk_indices.len().saturating_sub(1), | 169 | 0 | chunk.layer_index | 170 | 0 | ))); | 171 | 722k | } | 172 | | | 173 | 722k | let chunk_index_slot = &mut header_chunk_indices[index_in_header_increasing_y]; | 174 | 722k | if *chunk_index_slot != 0 { | 175 | 0 | return Err(Error::invalid(format!( | 176 | 0 | "chunk at index {} is already written", | 177 | 0 | index_in_header_increasing_y | 178 | 0 | ))); | 179 | 722k | } | 180 | | | 181 | 722k | *chunk_index_slot = usize_to_u64(self.byte_writer.byte_position(), "seek position")?; | 182 | 722k | chunk.write(&mut self.byte_writer, self.header_count)?; | 183 | 722k | Ok(()) | 184 | 722k | } |
|
185 | | } |
186 | | |
187 | | impl<W> ChunkWriter<W> |
188 | | where |
189 | | W: Write + Seek, |
190 | | { |
191 | | // -- the following functions are private, because they must be called in a |
192 | | // strict order -- |
193 | | |
194 | | /// Writes the meta data and zeroed offset tables as a placeholder. |
195 | 78 | fn new_for_buffered( |
196 | 78 | buffered_byte_writer: W, |
197 | 78 | headers: Headers, |
198 | 78 | pedantic: bool, |
199 | 78 | ) -> Result<(MetaData, Self)> { |
200 | 78 | let mut write = Tracking::new(buffered_byte_writer); |
201 | 78 | let requirements = |
202 | 78 | MetaData::write_validating_to_buffered(&mut write, headers.as_slice(), pedantic)?; |
203 | | |
204 | | // TODO: use increasing line order where possible, but this requires us to know |
205 | | // whether we want to be parallel right now |
206 | | // // if non-parallel compression, we always use increasing order anyways |
207 | | // if !parallel || !has_compression { |
208 | | // for header in &mut headers { |
209 | | // if header.line_order == LineOrder::Unspecified { |
210 | | // header.line_order = LineOrder::Increasing; |
211 | | // } |
212 | | // } |
213 | | // } |
214 | | |
215 | 78 | let offset_table_size: usize = headers.iter().map(|header| header.chunk_count).sum(); |
216 | | |
217 | 78 | let offset_table_start_byte = write.byte_position(); |
218 | 78 | let offset_table_end_byte = write.byte_position() + offset_table_size * u64::BYTE_SIZE; |
219 | | |
220 | | // skip offset tables, filling with 0, will be updated after the last chunk has |
221 | | // been written |
222 | 78 | write.seek_write_to(offset_table_end_byte)?; |
223 | | |
224 | 78 | let header_count = headers.len(); |
225 | 78 | let chunk_indices_increasing_y = |
226 | 78 | headers.iter().map(|header| vec![0_u64; header.chunk_count]).collect(); Unexecuted instantiation: <exr::block::writer::ChunkWriter<_>>::new_for_buffered::{closure#1}Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new_for_buffered::{closure#1}<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>>::new_for_buffered::{closure#1}Line | Count | Source | 226 | 78 | headers.iter().map(|header| vec![0_u64; header.chunk_count]).collect(); |
|
227 | | |
228 | 78 | let meta_data = MetaData { |
229 | 78 | requirements, |
230 | 78 | headers, |
231 | 78 | }; |
232 | | |
233 | 78 | Ok(( |
234 | 78 | meta_data, |
235 | 78 | ChunkWriter { |
236 | 78 | header_count, |
237 | 78 | byte_writer: write, |
238 | 78 | chunk_count: offset_table_size, |
239 | 78 | chunk_indices_byte_location: offset_table_start_byte..offset_table_end_byte, |
240 | 78 | chunk_indices_increasing_y, |
241 | 78 | }, |
242 | 78 | )) |
243 | 78 | } Unexecuted instantiation: <exr::block::writer::ChunkWriter<_>>::new_for_buffered Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new_for_buffered <exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>>::new_for_buffered Line | Count | Source | 195 | 78 | fn new_for_buffered( | 196 | 78 | buffered_byte_writer: W, | 197 | 78 | headers: Headers, | 198 | 78 | pedantic: bool, | 199 | 78 | ) -> Result<(MetaData, Self)> { | 200 | 78 | let mut write = Tracking::new(buffered_byte_writer); | 201 | 78 | let requirements = | 202 | 78 | MetaData::write_validating_to_buffered(&mut write, headers.as_slice(), pedantic)?; | 203 | | | 204 | | // TODO: use increasing line order where possible, but this requires us to know | 205 | | // whether we want to be parallel right now | 206 | | // // if non-parallel compression, we always use increasing order anyways | 207 | | // if !parallel || !has_compression { | 208 | | // for header in &mut headers { | 209 | | // if header.line_order == LineOrder::Unspecified { | 210 | | // header.line_order = LineOrder::Increasing; | 211 | | // } | 212 | | // } | 213 | | // } | 214 | | | 215 | 78 | let offset_table_size: usize = headers.iter().map(|header| header.chunk_count).sum(); | 216 | | | 217 | 78 | let offset_table_start_byte = write.byte_position(); | 218 | 78 | let offset_table_end_byte = write.byte_position() + offset_table_size * u64::BYTE_SIZE; | 219 | | | 220 | | // skip offset tables, filling with 0, will be updated after the last chunk has | 221 | | // been written | 222 | 78 | write.seek_write_to(offset_table_end_byte)?; | 223 | | | 224 | 78 | let header_count = headers.len(); | 225 | 78 | let chunk_indices_increasing_y = | 226 | 78 | headers.iter().map(|header| vec![0_u64; header.chunk_count]).collect(); | 227 | | | 228 | 78 | let meta_data = MetaData { | 229 | 78 | requirements, | 230 | 78 | headers, | 231 | 78 | }; | 232 | | | 233 | 78 | Ok(( | 234 | 78 | meta_data, | 235 | 78 | ChunkWriter { | 236 | 78 | header_count, | 237 | 78 | byte_writer: write, | 238 | 78 | chunk_count: offset_table_size, | 239 | 78 | chunk_indices_byte_location: offset_table_start_byte..offset_table_end_byte, | 240 | 78 | chunk_indices_increasing_y, | 241 | 78 | }, | 242 | 78 | )) | 243 | 78 | } |
|
244 | | |
245 | | /// Seek back to the meta data, write offset tables, and flush the byte |
246 | | /// writer. Leaves the writer seeked to the middle of the file, |
247 | | /// therefore we drop it. |
248 | 78 | fn complete_meta_data(mut self) -> UnitResult { |
249 | 722k | if self.chunk_indices_increasing_y.iter().flatten().any(|&index| index == 0) {Unexecuted instantiation: <exr::block::writer::ChunkWriter<_>>::complete_meta_data::{closure#0}Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::complete_meta_data::{closure#0}<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>>::complete_meta_data::{closure#0}Line | Count | Source | 249 | 722k | if self.chunk_indices_increasing_y.iter().flatten().any(|&index| index == 0) { |
|
250 | 0 | return Err(Error::invalid("some chunks are not written yet")); |
251 | 78 | } |
252 | | |
253 | | // write all offset tables |
254 | 78 | debug_assert_ne!( |
255 | 0 | self.byte_writer.byte_position(), |
256 | | self.chunk_indices_byte_location.end, |
257 | 0 | "offset table has already been updated" |
258 | | ); |
259 | 78 | self.byte_writer.seek_write_to(self.chunk_indices_byte_location.start)?; |
260 | | |
261 | 156 | for table in self.chunk_indices_increasing_y { |
262 | 78 | u64::write_slice_le(&mut self.byte_writer, table.as_slice())?; |
263 | | } |
264 | | |
265 | 78 | self.byte_writer.flush()?; // make sure we catch all (possibly delayed) io errors before returning |
266 | 78 | Ok(()) |
267 | 78 | } Unexecuted instantiation: <exr::block::writer::ChunkWriter<_>>::complete_meta_data Unexecuted instantiation: <exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::complete_meta_data <exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>>::complete_meta_data Line | Count | Source | 248 | 78 | fn complete_meta_data(mut self) -> UnitResult { | 249 | 78 | if self.chunk_indices_increasing_y.iter().flatten().any(|&index| index == 0) { | 250 | 0 | return Err(Error::invalid("some chunks are not written yet")); | 251 | 78 | } | 252 | | | 253 | | // write all offset tables | 254 | 78 | debug_assert_ne!( | 255 | 0 | self.byte_writer.byte_position(), | 256 | | self.chunk_indices_byte_location.end, | 257 | 0 | "offset table has already been updated" | 258 | | ); | 259 | 78 | self.byte_writer.seek_write_to(self.chunk_indices_byte_location.start)?; | 260 | | | 261 | 156 | for table in self.chunk_indices_increasing_y { | 262 | 78 | u64::write_slice_le(&mut self.byte_writer, table.as_slice())?; | 263 | | } | 264 | | | 265 | 78 | self.byte_writer.flush()?; // make sure we catch all (possibly delayed) io errors before returning | 266 | 78 | Ok(()) | 267 | 78 | } |
|
268 | | } |
269 | | |
270 | | impl<'w, W, F> ChunksWriter for OnProgressChunkWriter<'w, W, F> |
271 | | where |
272 | | W: 'w + ChunksWriter, |
273 | | F: FnMut(f64), |
274 | | { |
275 | 1.44M | fn total_chunks_count(&self) -> usize { |
276 | 1.44M | self.chunk_writer.total_chunks_count() |
277 | 1.44M | } Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<_, _> as exr::block::writer::ChunksWriter>::total_chunks_count Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::total_chunks_count <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::total_chunks_count Line | Count | Source | 275 | 1.44M | fn total_chunks_count(&self) -> usize { | 276 | 1.44M | self.chunk_writer.total_chunks_count() | 277 | 1.44M | } |
|
278 | | |
279 | 722k | fn write_chunk(&mut self, index_in_header_increasing_y: usize, chunk: Chunk) -> UnitResult { |
280 | 722k | let total_chunks = self.total_chunks_count(); |
281 | 722k | let on_progress = &mut self.on_progress; |
282 | | |
283 | | // guarantee on_progress being called with 0 once |
284 | 722k | if self.written_chunks == 0 { |
285 | 78 | on_progress(0.0); |
286 | 722k | } |
287 | | |
288 | 722k | self.chunk_writer.write_chunk(index_in_header_increasing_y, chunk)?; |
289 | | |
290 | 722k | self.written_chunks += 1; |
291 | | |
292 | 722k | on_progress({ |
293 | | // guarantee finishing with progress 1.0 for last block at least once, float |
294 | | // division might slightly differ from 1.0 |
295 | 722k | if self.written_chunks == total_chunks { |
296 | 78 | 1.0 |
297 | | } else { |
298 | 722k | self.written_chunks as f64 / total_chunks as f64 |
299 | | } |
300 | | }); |
301 | | |
302 | 722k | Ok(()) |
303 | 722k | } Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<_, _> as exr::block::writer::ChunksWriter>::write_chunk Unexecuted instantiation: <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::write_chunk <exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)> as exr::block::writer::ChunksWriter>::write_chunk Line | Count | Source | 279 | 722k | fn write_chunk(&mut self, index_in_header_increasing_y: usize, chunk: Chunk) -> UnitResult { | 280 | 722k | let total_chunks = self.total_chunks_count(); | 281 | 722k | let on_progress = &mut self.on_progress; | 282 | | | 283 | | // guarantee on_progress being called with 0 once | 284 | 722k | if self.written_chunks == 0 { | 285 | 78 | on_progress(0.0); | 286 | 722k | } | 287 | | | 288 | 722k | self.chunk_writer.write_chunk(index_in_header_increasing_y, chunk)?; | 289 | | | 290 | 722k | self.written_chunks += 1; | 291 | | | 292 | 722k | on_progress({ | 293 | | // guarantee finishing with progress 1.0 for last block at least once, float | 294 | | // division might slightly differ from 1.0 | 295 | 722k | if self.written_chunks == total_chunks { | 296 | 78 | 1.0 | 297 | | } else { | 298 | 722k | self.written_chunks as f64 / total_chunks as f64 | 299 | | } | 300 | | }); | 301 | | | 302 | 722k | Ok(()) | 303 | 722k | } |
|
304 | | } |
305 | | |
306 | | /// Write blocks that appear in any order and reorder them before writing. |
307 | | #[derive(Debug)] |
308 | | #[must_use] |
309 | | pub struct SortedBlocksWriter<'w, W> { |
310 | | chunk_writer: &'w mut W, |
311 | | pending_chunks: BTreeMap<usize, (usize, Chunk)>, |
312 | | unwritten_chunk_indices: Peekable<std::ops::Range<usize>>, |
313 | | requires_sorting: bool, // using this instead of Option, because of borrowing |
314 | | } |
315 | | |
316 | | impl<'w, W> SortedBlocksWriter<'w, W> |
317 | | where |
318 | | W: ChunksWriter, |
319 | | { |
320 | | /// New sorting writer. Returns `None` if sorting is not required. |
321 | 78 | pub fn new(meta_data: &MetaData, chunk_writer: &'w mut W) -> SortedBlocksWriter<'w, W> { |
322 | 78 | let requires_sorting = |
323 | 78 | meta_data.headers.iter().any(|header| header.line_order != LineOrder::Unspecified); Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<_>>::new::{closure#0}Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}<exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}Line | Count | Source | 323 | 78 | meta_data.headers.iter().any(|header| header.line_order != LineOrder::Unspecified); |
|
324 | | |
325 | 78 | let total_chunk_count = chunk_writer.total_chunks_count(); |
326 | | |
327 | 78 | SortedBlocksWriter { |
328 | 78 | pending_chunks: BTreeMap::new(), |
329 | 78 | unwritten_chunk_indices: (0..total_chunk_count).peekable(), |
330 | 78 | requires_sorting, |
331 | 78 | chunk_writer, |
332 | 78 | } |
333 | 78 | } Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<_>>::new Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new Line | Count | Source | 321 | 78 | pub fn new(meta_data: &MetaData, chunk_writer: &'w mut W) -> SortedBlocksWriter<'w, W> { | 322 | 78 | let requires_sorting = | 323 | 78 | meta_data.headers.iter().any(|header| header.line_order != LineOrder::Unspecified); | 324 | | | 325 | 78 | let total_chunk_count = chunk_writer.total_chunks_count(); | 326 | | | 327 | 78 | SortedBlocksWriter { | 328 | 78 | pending_chunks: BTreeMap::new(), | 329 | 78 | unwritten_chunk_indices: (0..total_chunk_count).peekable(), | 330 | 78 | requires_sorting, | 331 | 78 | chunk_writer, | 332 | 78 | } | 333 | 78 | } |
|
334 | | |
335 | | /// Write the chunk or stash it. In the closure, write all chunks that can |
336 | | /// be written now. |
337 | 722k | pub fn write_or_stash_chunk( |
338 | 722k | &mut self, |
339 | 722k | chunk_index_in_file: usize, |
340 | 722k | chunk_y_index: usize, |
341 | 722k | chunk: Chunk, |
342 | 722k | ) -> UnitResult { |
343 | 722k | if self.requires_sorting.not() { |
344 | 722k | return self.chunk_writer.write_chunk(chunk_y_index, chunk); |
345 | 0 | } |
346 | | |
347 | | // write this chunk now if possible |
348 | 0 | if self.unwritten_chunk_indices.peek() == Some(&chunk_index_in_file) { |
349 | 0 | self.chunk_writer.write_chunk(chunk_y_index, chunk)?; |
350 | 0 | self.unwritten_chunk_indices.next().expect("peeked chunk index is missing"); |
351 | | |
352 | | // write all pending blocks that are immediate successors of this block |
353 | 0 | while let Some((next_chunk_y_index, next_chunk)) = self |
354 | 0 | .unwritten_chunk_indices |
355 | 0 | .peek() |
356 | 0 | .cloned() |
357 | 0 | .and_then(|id| self.pending_chunks.remove(&id)) Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<_>>::write_or_stash_chunk::{closure#0}Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::write_or_stash_chunk::{closure#0}Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::write_or_stash_chunk::{closure#0} |
358 | | { |
359 | 0 | self.chunk_writer.write_chunk(next_chunk_y_index, next_chunk)?; |
360 | 0 | self.unwritten_chunk_indices.next().expect("peeked chunk index is missing"); |
361 | | } |
362 | 0 | } else { |
363 | 0 | // the argument block is not to be written now, |
364 | 0 | // and all the pending blocks are not next up either, |
365 | 0 | // so just stash this block |
366 | 0 | self.pending_chunks.insert(chunk_index_in_file, (chunk_y_index, chunk)); |
367 | 0 | } |
368 | | |
369 | 0 | Ok(()) |
370 | 722k | } Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<_>>::write_or_stash_chunk Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::write_or_stash_chunk <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::write_or_stash_chunk Line | Count | Source | 337 | 722k | pub fn write_or_stash_chunk( | 338 | 722k | &mut self, | 339 | 722k | chunk_index_in_file: usize, | 340 | 722k | chunk_y_index: usize, | 341 | 722k | chunk: Chunk, | 342 | 722k | ) -> UnitResult { | 343 | 722k | if self.requires_sorting.not() { | 344 | 722k | return self.chunk_writer.write_chunk(chunk_y_index, chunk); | 345 | 0 | } | 346 | | | 347 | | // write this chunk now if possible | 348 | 0 | if self.unwritten_chunk_indices.peek() == Some(&chunk_index_in_file) { | 349 | 0 | self.chunk_writer.write_chunk(chunk_y_index, chunk)?; | 350 | 0 | self.unwritten_chunk_indices.next().expect("peeked chunk index is missing"); | 351 | | | 352 | | // write all pending blocks that are immediate successors of this block | 353 | 0 | while let Some((next_chunk_y_index, next_chunk)) = self | 354 | 0 | .unwritten_chunk_indices | 355 | 0 | .peek() | 356 | 0 | .cloned() | 357 | 0 | .and_then(|id| self.pending_chunks.remove(&id)) | 358 | | { | 359 | 0 | self.chunk_writer.write_chunk(next_chunk_y_index, next_chunk)?; | 360 | 0 | self.unwritten_chunk_indices.next().expect("peeked chunk index is missing"); | 361 | | } | 362 | 0 | } else { | 363 | 0 | // the argument block is not to be written now, | 364 | 0 | // and all the pending blocks are not next up either, | 365 | 0 | // so just stash this block | 366 | 0 | self.pending_chunks.insert(chunk_index_in_file, (chunk_y_index, chunk)); | 367 | 0 | } | 368 | | | 369 | 0 | Ok(()) | 370 | 722k | } |
|
371 | | |
372 | | /// Where the chunks will be written to. |
373 | 722k | pub fn inner_chunks_writer(&self) -> &W { |
374 | 722k | &self.chunk_writer |
375 | 722k | } Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<_>>::inner_chunks_writer Unexecuted instantiation: <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::inner_chunks_writer <exr::block::writer::SortedBlocksWriter<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::inner_chunks_writer Line | Count | Source | 373 | 722k | pub fn inner_chunks_writer(&self) -> &W { | 374 | 722k | &self.chunk_writer | 375 | 722k | } |
|
376 | | } |
377 | | |
378 | | /// Compress blocks to a chunk writer in this thread. |
379 | | #[derive(Debug)] |
380 | | #[must_use] |
381 | | pub struct SequentialBlocksCompressor<'w, W> { |
382 | | meta: &'w MetaData, |
383 | | chunks_writer: &'w mut W, |
384 | | } |
385 | | |
386 | | impl<'w, W> SequentialBlocksCompressor<'w, W> |
387 | | where |
388 | | W: 'w + ChunksWriter, |
389 | | { |
390 | | /// New blocks writer. |
391 | 0 | pub fn new(meta: &'w MetaData, chunks_writer: &'w mut W) -> Self { |
392 | 0 | Self { |
393 | 0 | meta, |
394 | 0 | chunks_writer, |
395 | 0 | } |
396 | 0 | } Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<_>>::new Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new |
397 | | |
398 | | /// This is where the compressed blocks are written to. |
399 | 0 | pub fn inner_chunks_writer(&'w self) -> &'w W { |
400 | 0 | self.chunks_writer |
401 | 0 | } |
402 | | |
403 | | /// Compress a single block immediately. The index of the block must be in |
404 | | /// increasing line order. |
405 | 0 | pub fn compress_block( |
406 | 0 | &mut self, |
407 | 0 | index_in_header_increasing_y: usize, |
408 | 0 | block: UncompressedBlock, |
409 | 0 | ) -> UnitResult { |
410 | 0 | self.chunks_writer |
411 | 0 | .write_chunk(index_in_header_increasing_y, block.compress_to_chunk(&self.meta.headers)?) |
412 | 0 | } Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<_>>::compress_block Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::compress_block Unexecuted instantiation: <exr::block::writer::SequentialBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::compress_block |
413 | | } |
414 | | |
415 | | /// Compress blocks to a chunk writer with multiple threads. |
416 | | #[cfg(feature = "rayon")] |
417 | | #[derive(Debug)] |
418 | | #[must_use] |
419 | | pub struct ParallelBlocksCompressor<'w, W> { |
420 | | meta: &'w MetaData, |
421 | | sorted_writer: SortedBlocksWriter<'w, W>, |
422 | | |
423 | | sender: std::sync::mpsc::Sender<Result<(usize, usize, Chunk)>>, |
424 | | receiver: std::sync::mpsc::Receiver<Result<(usize, usize, Chunk)>>, |
425 | | pool: rayon_core::ThreadPool, |
426 | | |
427 | | currently_compressing_count: usize, |
428 | | written_chunk_count: usize, // used to check for last chunk |
429 | | max_threads: usize, |
430 | | next_incoming_chunk_index: usize, // used to remember original chunk order |
431 | | } |
432 | | |
433 | | #[cfg(feature = "rayon")] |
434 | | impl<'w, W> ParallelBlocksCompressor<'w, W> |
435 | | where |
436 | | W: 'w + ChunksWriter, |
437 | | { |
438 | | /// New blocks writer. Returns none if sequential compression should be |
439 | | /// used. Use `new_with_thread_pool` to customize the threadpool. |
440 | 78 | pub fn new(meta: &'w MetaData, chunks_writer: &'w mut W) -> Option<Self> { |
441 | 78 | Self::new_with_thread_pool(meta, chunks_writer, || { |
442 | 78 | rayon_core::ThreadPoolBuilder::new() |
443 | 2.49k | .thread_name(|index| format!("OpenEXR Block Compressor Thread #{}", index))Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::new::{closure#0}::{closure#0}Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}::{closure#0}<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}::{closure#0}Line | Count | Source | 443 | 2.49k | .thread_name(|index| format!("OpenEXR Block Compressor Thread #{}", index)) |
|
444 | 78 | .build() |
445 | 78 | }) Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::new::{closure#0}Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}Line | Count | Source | 441 | 78 | Self::new_with_thread_pool(meta, chunks_writer, || { | 442 | 78 | rayon_core::ThreadPoolBuilder::new() | 443 | 78 | .thread_name(|index| format!("OpenEXR Block Compressor Thread #{}", index)) | 444 | 78 | .build() | 445 | 78 | }) |
|
446 | 78 | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::new Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new Line | Count | Source | 440 | 78 | pub fn new(meta: &'w MetaData, chunks_writer: &'w mut W) -> Option<Self> { | 441 | 78 | Self::new_with_thread_pool(meta, chunks_writer, || { | 442 | | rayon_core::ThreadPoolBuilder::new() | 443 | | .thread_name(|index| format!("OpenEXR Block Compressor Thread #{}", index)) | 444 | | .build() | 445 | | }) | 446 | 78 | } |
|
447 | | |
448 | | /// New blocks writer. Returns none if sequential compression should be |
449 | | /// used. |
450 | 78 | pub fn new_with_thread_pool<CreatePool>( |
451 | 78 | meta: &'w MetaData, |
452 | 78 | chunks_writer: &'w mut W, |
453 | 78 | try_create_thread_pool: CreatePool, |
454 | 78 | ) -> Option<Self> |
455 | 78 | where |
456 | 78 | CreatePool: |
457 | 78 | FnOnce() |
458 | 78 | -> std::result::Result<rayon_core::ThreadPool, rayon_core::ThreadPoolBuildError>, |
459 | | { |
460 | | use crate::compression::Compression; |
461 | | |
462 | 78 | let is_entirely_uncompressed = |
463 | 78 | meta.headers.iter().all(|head| head.compression == Compression::Uncompressed); Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::new_with_thread_pool::<_>::{closure#0}Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new_with_thread_pool::<<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}>::{closure#0}<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new_with_thread_pool::<<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}>::{closure#0}Line | Count | Source | 463 | 78 | meta.headers.iter().all(|head| head.compression == Compression::Uncompressed); |
|
464 | | |
465 | 78 | if is_entirely_uncompressed { |
466 | 0 | return None; |
467 | 78 | } |
468 | | |
469 | | // in case thread pool creation fails (for example on WASM currently), |
470 | | // we revert to sequential compression |
471 | 78 | let pool = match try_create_thread_pool() { |
472 | 78 | Ok(pool) => pool, |
473 | | |
474 | | // TODO print warning? |
475 | 0 | Err(_) => return None, |
476 | | }; |
477 | | |
478 | 78 | let max_threads = |
479 | 78 | pool.current_num_threads().max(1).min(chunks_writer.total_chunks_count()) + 2; // ca one block for each thread at all times |
480 | 78 | let (send, recv) = std::sync::mpsc::channel(); // TODO bounded channel simplifies logic? |
481 | | |
482 | 78 | Some(Self { |
483 | 78 | sorted_writer: SortedBlocksWriter::new(meta, chunks_writer), |
484 | 78 | next_incoming_chunk_index: 0, |
485 | 78 | currently_compressing_count: 0, |
486 | 78 | written_chunk_count: 0, |
487 | 78 | sender: send, |
488 | 78 | receiver: recv, |
489 | 78 | max_threads, |
490 | 78 | pool, |
491 | 78 | meta, |
492 | 78 | }) |
493 | 78 | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::new_with_thread_pool::<_> Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new_with_thread_pool::<<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}><exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new_with_thread_pool::<<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::new::{closure#0}>Line | Count | Source | 450 | 78 | pub fn new_with_thread_pool<CreatePool>( | 451 | 78 | meta: &'w MetaData, | 452 | 78 | chunks_writer: &'w mut W, | 453 | 78 | try_create_thread_pool: CreatePool, | 454 | 78 | ) -> Option<Self> | 455 | 78 | where | 456 | 78 | CreatePool: | 457 | 78 | FnOnce() | 458 | 78 | -> std::result::Result<rayon_core::ThreadPool, rayon_core::ThreadPoolBuildError>, | 459 | | { | 460 | | use crate::compression::Compression; | 461 | | | 462 | 78 | let is_entirely_uncompressed = | 463 | 78 | meta.headers.iter().all(|head| head.compression == Compression::Uncompressed); | 464 | | | 465 | 78 | if is_entirely_uncompressed { | 466 | 0 | return None; | 467 | 78 | } | 468 | | | 469 | | // in case thread pool creation fails (for example on WASM currently), | 470 | | // we revert to sequential compression | 471 | 78 | let pool = match try_create_thread_pool() { | 472 | 78 | Ok(pool) => pool, | 473 | | | 474 | | // TODO print warning? | 475 | 0 | Err(_) => return None, | 476 | | }; | 477 | | | 478 | 78 | let max_threads = | 479 | 78 | pool.current_num_threads().max(1).min(chunks_writer.total_chunks_count()) + 2; // ca one block for each thread at all times | 480 | 78 | let (send, recv) = std::sync::mpsc::channel(); // TODO bounded channel simplifies logic? | 481 | | | 482 | 78 | Some(Self { | 483 | 78 | sorted_writer: SortedBlocksWriter::new(meta, chunks_writer), | 484 | 78 | next_incoming_chunk_index: 0, | 485 | 78 | currently_compressing_count: 0, | 486 | 78 | written_chunk_count: 0, | 487 | 78 | sender: send, | 488 | 78 | receiver: recv, | 489 | 78 | max_threads, | 490 | 78 | pool, | 491 | 78 | meta, | 492 | 78 | }) | 493 | 78 | } |
|
494 | | |
495 | | /// This is where the compressed blocks are written to. |
496 | 722k | pub fn inner_chunks_writer(&'w self) -> &'w W { |
497 | 722k | self.sorted_writer.inner_chunks_writer() |
498 | 722k | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::inner_chunks_writer Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::inner_chunks_writer <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::inner_chunks_writer Line | Count | Source | 496 | 722k | pub fn inner_chunks_writer(&'w self) -> &'w W { | 497 | 722k | self.sorted_writer.inner_chunks_writer() | 498 | 722k | } |
|
499 | | |
500 | | // private, as may underflow counter in release mode |
501 | 722k | fn write_next_queued_chunk(&mut self) -> UnitResult { |
502 | 722k | debug_assert!( |
503 | 0 | self.currently_compressing_count > 0, |
504 | | "cannot wait for chunks as there are none left" |
505 | | ); |
506 | | |
507 | 722k | let some_compressed_chunk = self.receiver.recv().expect("cannot receive compressed block"); |
508 | | |
509 | 722k | self.currently_compressing_count -= 1; |
510 | 722k | let (chunk_file_index, chunk_y_index, chunk) = some_compressed_chunk?; |
511 | 722k | self.sorted_writer.write_or_stash_chunk(chunk_file_index, chunk_y_index, chunk)?; |
512 | | |
513 | 722k | self.written_chunk_count += 1; |
514 | 722k | Ok(()) |
515 | 722k | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::write_next_queued_chunk Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::write_next_queued_chunk <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::write_next_queued_chunk Line | Count | Source | 501 | 722k | fn write_next_queued_chunk(&mut self) -> UnitResult { | 502 | 722k | debug_assert!( | 503 | 0 | self.currently_compressing_count > 0, | 504 | | "cannot wait for chunks as there are none left" | 505 | | ); | 506 | | | 507 | 722k | let some_compressed_chunk = self.receiver.recv().expect("cannot receive compressed block"); | 508 | | | 509 | 722k | self.currently_compressing_count -= 1; | 510 | 722k | let (chunk_file_index, chunk_y_index, chunk) = some_compressed_chunk?; | 511 | 722k | self.sorted_writer.write_or_stash_chunk(chunk_file_index, chunk_y_index, chunk)?; | 512 | | | 513 | 722k | self.written_chunk_count += 1; | 514 | 722k | Ok(()) | 515 | 722k | } |
|
516 | | |
517 | | /// Wait until all currently compressing chunks in the compressor have been |
518 | | /// written. |
519 | 78 | pub fn write_all_queued_chunks(&mut self) -> UnitResult { |
520 | 2.01k | while self.currently_compressing_count > 0 { |
521 | 1.93k | self.write_next_queued_chunk()?; |
522 | | } |
523 | | |
524 | 78 | debug_assert_eq!(self.currently_compressing_count, 0, "counter does not match block count"); |
525 | 78 | Ok(()) |
526 | 78 | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::write_all_queued_chunks Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::write_all_queued_chunks <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::write_all_queued_chunks Line | Count | Source | 519 | 78 | pub fn write_all_queued_chunks(&mut self) -> UnitResult { | 520 | 2.01k | while self.currently_compressing_count > 0 { | 521 | 1.93k | self.write_next_queued_chunk()?; | 522 | | } | 523 | | | 524 | 78 | debug_assert_eq!(self.currently_compressing_count, 0, "counter does not match block count"); | 525 | 78 | Ok(()) | 526 | 78 | } |
|
527 | | |
528 | | /// Add a single block to the compressor queue. The index of the block must |
529 | | /// be in increasing line order. When calling this function for the last |
530 | | /// block, this method waits until all the blocks have been written. |
531 | | /// This only works when you write as many blocks as the image expects, |
532 | | /// otherwise you can use `wait_for_all_remaining_chunks`. Waits for a |
533 | | /// block from the queue to be written, if the queue already has enough |
534 | | /// items. |
535 | 722k | pub fn add_block_to_compression_queue( |
536 | 722k | &mut self, |
537 | 722k | index_in_header_increasing_y: usize, |
538 | 722k | block: UncompressedBlock, |
539 | 722k | ) -> UnitResult { |
540 | | // if pipe is full, block to wait for a slot to free up |
541 | 722k | if self.currently_compressing_count >= self.max_threads { |
542 | 720k | self.write_next_queued_chunk()?; |
543 | 1.93k | } |
544 | | |
545 | | // add the argument chunk to the compression queueue |
546 | 722k | let index_in_file = self.next_incoming_chunk_index; |
547 | 722k | let sender = self.sender.clone(); |
548 | 722k | let meta = self.meta.clone(); |
549 | | |
550 | 722k | self.pool.spawn(move || { |
551 | 722k | let compressed_or_err = block.compress_to_chunk(&meta.headers); |
552 | | |
553 | | // by now, decompressing could have failed in another thread. |
554 | | // the error is then already handled, so we simply |
555 | | // don't send the decompressed block and do nothing |
556 | | let _ = |
557 | 722k | sender.send(compressed_or_err.map(move |compressed| { |
558 | 722k | (index_in_file, index_in_header_increasing_y, compressed) |
559 | 722k | })); Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::add_block_to_compression_queue::{closure#0}::{closure#0}Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue::{closure#0}::{closure#0}<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue::{closure#0}::{closure#0}Line | Count | Source | 557 | 722k | sender.send(compressed_or_err.map(move |compressed| { | 558 | 722k | (index_in_file, index_in_header_increasing_y, compressed) | 559 | 722k | })); |
|
560 | 722k | }); Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::add_block_to_compression_queue::{closure#0}Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue::{closure#0}<exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue::{closure#0}Line | Count | Source | 550 | 722k | self.pool.spawn(move || { | 551 | 722k | let compressed_or_err = block.compress_to_chunk(&meta.headers); | 552 | | | 553 | | // by now, decompressing could have failed in another thread. | 554 | | // the error is then already handled, so we simply | 555 | | // don't send the decompressed block and do nothing | 556 | | let _ = | 557 | 722k | sender.send(compressed_or_err.map(move |compressed| { | 558 | | (index_in_file, index_in_header_increasing_y, compressed) | 559 | | })); | 560 | 722k | }); |
|
561 | | |
562 | 722k | self.currently_compressing_count += 1; |
563 | 722k | self.next_incoming_chunk_index += 1; |
564 | | |
565 | | // if this is the last chunk, wait for all chunks to complete before returning |
566 | 722k | if self.written_chunk_count + self.currently_compressing_count |
567 | 722k | == self.inner_chunks_writer().total_chunks_count() |
568 | | { |
569 | 78 | self.write_all_queued_chunks()?; |
570 | 78 | debug_assert_eq!( |
571 | | self.written_chunk_count, |
572 | 0 | self.inner_chunks_writer().total_chunks_count(), |
573 | 0 | "written chunk count mismatch" |
574 | | ); |
575 | 722k | } |
576 | | |
577 | 722k | Ok(()) |
578 | 722k | } Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<_>>::add_block_to_compression_queue Unexecuted instantiation: <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue <exr::block::writer::ParallelBlocksCompressor<exr::block::writer::OnProgressChunkWriter<exr::block::writer::ChunkWriter<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>, fn(f64)>>>::add_block_to_compression_queue Line | Count | Source | 535 | 722k | pub fn add_block_to_compression_queue( | 536 | 722k | &mut self, | 537 | 722k | index_in_header_increasing_y: usize, | 538 | 722k | block: UncompressedBlock, | 539 | 722k | ) -> UnitResult { | 540 | | // if pipe is full, block to wait for a slot to free up | 541 | 722k | if self.currently_compressing_count >= self.max_threads { | 542 | 720k | self.write_next_queued_chunk()?; | 543 | 1.93k | } | 544 | | | 545 | | // add the argument chunk to the compression queueue | 546 | 722k | let index_in_file = self.next_incoming_chunk_index; | 547 | 722k | let sender = self.sender.clone(); | 548 | 722k | let meta = self.meta.clone(); | 549 | | | 550 | 722k | self.pool.spawn(move || { | 551 | | let compressed_or_err = block.compress_to_chunk(&meta.headers); | 552 | | | 553 | | // by now, decompressing could have failed in another thread. | 554 | | // the error is then already handled, so we simply | 555 | | // don't send the decompressed block and do nothing | 556 | | let _ = | 557 | | sender.send(compressed_or_err.map(move |compressed| { | 558 | | (index_in_file, index_in_header_increasing_y, compressed) | 559 | | })); | 560 | | }); | 561 | | | 562 | 722k | self.currently_compressing_count += 1; | 563 | 722k | self.next_incoming_chunk_index += 1; | 564 | | | 565 | | // if this is the last chunk, wait for all chunks to complete before returning | 566 | 722k | if self.written_chunk_count + self.currently_compressing_count | 567 | 722k | == self.inner_chunks_writer().total_chunks_count() | 568 | | { | 569 | 78 | self.write_all_queued_chunks()?; | 570 | 78 | debug_assert_eq!( | 571 | | self.written_chunk_count, | 572 | 0 | self.inner_chunks_writer().total_chunks_count(), | 573 | 0 | "written chunk count mismatch" | 574 | | ); | 575 | 722k | } | 576 | | | 577 | 722k | Ok(()) | 578 | 722k | } |
|
579 | | } |