- int (*decompress)(const void *, unsigned, void *, unsigned);
- /* Set the appropriate decompress function. */
- if (resource_ctype == WIMLIB_COMPRESSION_TYPE_LZX)
- decompress = wimlib_lzx_decompress;
- else
- decompress = wimlib_xpress_decompress;
-
- /* The structure of a compressed resource consists of a table of chunk
- * offsets followed by the chunks themselves. Each chunk consists of
- * compressed data, and there is one chunk for each WIM_CHUNK_SIZE =
- * 32768 bytes of the uncompressed file, with the last chunk having any
- * remaining bytes.
- *
- * The chunk offsets are measured relative to the end of the chunk
- * table. The first chunk is omitted from the table in the WIM file
- * because its offset is implicitly given by the fact that it directly
- * follows the chunk table and therefore must have an offset of 0.
- */
+ /* Get valid decompressor. */
+ if (ctype == rdesc->wim->decompressor_ctype &&
+ chunk_size == rdesc->wim->decompressor_max_block_size)
+ {
+ /* Cached decompressor. */
+ decompressor = rdesc->wim->decompressor;
+ rdesc->wim->decompressor_ctype = WIMLIB_COMPRESSION_TYPE_NONE;
+ rdesc->wim->decompressor = NULL;
+ } else {
+ ret = wimlib_create_decompressor(ctype, chunk_size,
+ &decompressor);
+ if (ret) {
+ if (ret != WIMLIB_ERR_NOMEM)
+ errno = EINVAL;
+ goto out_free_memory;
+ }
+ }
+
+ const u32 chunk_order = fls32(chunk_size);
+
+ /* Calculate the total number of chunks the resource is divided into. */
+ const u64 num_chunks = (rdesc->uncompressed_size + chunk_size - 1) >> chunk_order;
+
+ /* Calculate the 0-based indices of the first and last chunks containing
+ * data that needs to be passed to the callback. */
+ const u64 first_needed_chunk = first_offset >> chunk_order;
+ const u64 last_needed_chunk = last_offset >> chunk_order;
+
+ /* Calculate the 0-based index of the first chunk that actually needs to
+ * be read. This is normally first_needed_chunk, but for pipe reads we
+ * must always start from the 0th chunk. */
+ const u64 read_start_chunk = (is_pipe_read ? 0 : first_needed_chunk);
+
+ /* Calculate the number of chunk offsets that are needed for the chunks
+ * being read. */
+ const u64 num_needed_chunk_offsets =
+ last_needed_chunk - read_start_chunk + 1 +
+ (last_needed_chunk < num_chunks - 1);
+
+ /* Calculate the number of entries in the chunk table. Normally, it's
+ * one less than the number of chunks, since the first chunk has no
+ * entry. But in the alternate chunk table format, the chunk entries
+ * contain chunk sizes, not offsets, and there is one per chunk. */
+ const u64 num_chunk_entries = (alt_chunk_table ? num_chunks : num_chunks - 1);
+
+ /* Set the size of each chunk table entry based on the resource's
+ * uncompressed size. */
+ const u64 chunk_entry_size = get_chunk_entry_size(rdesc->uncompressed_size,
+ alt_chunk_table);
+
+ /* Calculate the size of the chunk table in bytes. */
+ const u64 chunk_table_size = num_chunk_entries * chunk_entry_size;
+
+ /* Calculate the size of the chunk table in bytes, including the header
+ * in the case of the alternate chunk table format. */
+ const u64 chunk_table_full_size =
+ (alt_chunk_table) ? chunk_table_size + sizeof(struct alt_chunk_table_header_disk)
+ : chunk_table_size;
+
+ if (!is_pipe_read) {
+ /* Read the needed chunk table entries into memory and use them
+ * to initialize the chunk_offsets array. */
+
+ u64 first_chunk_entry_to_read;
+ u64 last_chunk_entry_to_read;
+
+ if (alt_chunk_table) {
+ /* The alternate chunk table contains chunk sizes, not
+ * offsets, so we always must read all preceding entries
+ * in order to determine offsets. */
+ first_chunk_entry_to_read = 0;
+ last_chunk_entry_to_read = last_needed_chunk;
+ } else {
+ /* Here we must account for the fact that the first
+ * chunk has no explicit chunk table entry. */