- /* Calculate how many chunks the resource consists of in its entirety.
- * */
- u64 num_chunks = (resource_uncompressed_size + WIM_CHUNK_SIZE - 1) /
- WIM_CHUNK_SIZE;
- /* As mentioned, the first chunk has no entry in the chunk table. */
- u64 num_chunk_entries = num_chunks - 1;
-
-
- /* The index of the chunk that the read starts at. */
- u64 start_chunk = offset / WIM_CHUNK_SIZE;
- /* The byte offset at which the read starts, within the start chunk. */
- u64 start_chunk_offset = offset % WIM_CHUNK_SIZE;
-
- /* The index of the chunk that contains the last byte of the read. */
- u64 end_chunk = (offset + len - 1) / WIM_CHUNK_SIZE;
- /* The byte offset of the last byte of the read, within the end chunk */
- u64 end_chunk_offset = (offset + len - 1) % WIM_CHUNK_SIZE;
-
- /* Number of chunks that are actually needed to read the requested part
- * of the file. */
- u64 num_needed_chunks = end_chunk - start_chunk + 1;
-
- /* If the end chunk is not the last chunk, an extra chunk entry is
- * needed because we need to know the offset of the chunk after the last
- * chunk read to figure out the size of the last read chunk. */
- if (end_chunk != num_chunks - 1)
- num_needed_chunks++;
-
- /* Allocate the chunk table. It will only contain offsets for the
- * chunks that are actually needed for this read. */
- u64 *chunk_offsets;
- bool chunk_offsets_malloced;
- if (num_needed_chunks < 1000) {
- chunk_offsets = alloca(num_needed_chunks * sizeof(u64));
- chunk_offsets_malloced = false;
- } else {
- chunk_offsets = malloc(num_needed_chunks * sizeof(u64));
- if (!chunk_offsets) {
- ERROR("Failed to allocate chunk table "
- "with %"PRIu64" entries", num_needed_chunks);
- return WIMLIB_ERR_NOMEM;
+ /* Set the implicit offset of the first chunk if it's included
+ * in the needed chunks. */
+ if (start_chunk == 0)
+ chunk_offsets[0] = 0;
+
+ /* Calculate the index of the first needed entry in the chunk
+ * table. */
+ const u64 start_table_idx = (start_chunk == 0) ?
+ 0 : start_chunk - 1;
+
+ /* Calculate the number of entries that need to be read from the
+ * chunk table. */
+ const u64 num_needed_chunk_entries = (start_chunk == 0) ?
+ num_alloc_chunk_entries - 1 : num_alloc_chunk_entries;
+
+ /* Calculate the number of bytes of data that need to be read
+ * from the chunk table. */
+ const size_t chunk_table_needed_size =
+ num_needed_chunk_entries * chunk_entry_size;
+
+ /* Calculate the byte offset, in the WIM file, of the first
+ * chunk table entry to read. Take into account that if the WIM
+ * file is in the special "pipable" format, then the chunk table
+ * is at the end of the resource, not the beginning. */
+ const u64 file_offset_of_needed_chunk_entries =
+ lte->resource_entry.offset
+ + (start_table_idx * chunk_entry_size)
+ + (lte->is_pipable ? (lte->resource_entry.size - chunk_table_size) : 0);
+
+ /* Read the needed chunk table entries into the end of the
+ * chunk_offsets buffer. */
+ void * const chunk_tab_data = (u8*)&chunk_offsets[num_alloc_chunk_entries] -
+ chunk_table_needed_size;
+ ret = full_pread(in_fd, chunk_tab_data, chunk_table_needed_size,
+ file_offset_of_needed_chunk_entries);
+ if (ret)
+ goto read_error;
+
+ /* Now fill in chunk_offsets from the entries we have read in
+ * chunk_tab_data. Careful: chunk_offsets aliases
+ * chunk_tab_data, which breaks C's aliasing rules when we read
+ * 32-bit integers and store 64-bit integers. But since the
+ * operations are safe as long as the compiler doesn't mess with
+ * their order, we use the gcc may_alias extension to tell the
+ * compiler that loads from the 32-bit integers may alias stores
+ * to the 64-bit integers. */
+ {
+ typedef le64 __attribute__((may_alias)) aliased_le64_t;
+ typedef le32 __attribute__((may_alias)) aliased_le32_t;
+ u64 * const chunk_offsets_p = chunk_offsets + (start_chunk == 0);
+ u64 i;
+
+ if (chunk_entry_size == 4) {
+ aliased_le32_t *raw_entries = (aliased_le32_t*)chunk_tab_data;
+ for (i = 0; i < num_needed_chunk_entries; i++)
+ chunk_offsets_p[i] = le32_to_cpu(raw_entries[i]);
+ } else {
+ aliased_le64_t *raw_entries = (aliased_le64_t*)chunk_tab_data;
+ for (i = 0; i < num_needed_chunk_entries; i++)
+ chunk_offsets_p[i] = le64_to_cpu(raw_entries[i]);
+ }