+ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
+ add_untracked_cache(istate);
+}
+
+static void tweak_split_index(struct index_state *istate)
+{
+ switch (git_config_get_split_index()) {
+ case -1: /* unset: do nothing */
+ break;
+ case 0: /* false */
+ remove_split_index(istate);
+ break;
+ case 1: /* true */
+ add_split_index(istate);
+ break;
+ default: /* unknown value: do nothing */
+ break;
+ }
+}
+
+static void post_read_index_from(struct index_state *istate)
+{
+ check_ce_order(istate);
+ tweak_untracked_cache(istate);
+ tweak_split_index(istate);
+ tweak_fsmonitor(istate);
+}
+
+static size_t estimate_cache_size_from_compressed(unsigned int entries)
+{
+ return entries * (sizeof(struct cache_entry) + CACHE_ENTRY_PATH_LENGTH);
+}
+
+static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
+{
+ long per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry);
+
+ /*
+ * Account for potential alignment differences.
+ */
+ per_entry += align_padding_size(per_entry, 0);
+ return ondisk_size + entries * per_entry;
+}
+
+struct index_entry_offset
+{
+ /* starting byte offset into index file, count of index entries in this block */
+ int offset, nr;
+};
+
+struct index_entry_offset_table
+{
+ int nr;
+ struct index_entry_offset entries[FLEX_ARRAY];
+};
+
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset);
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);
+
+static size_t read_eoie_extension(const char *mmap, size_t mmap_size);
+static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);
+
+struct load_index_extensions
+{
+ pthread_t pthread;
+ struct index_state *istate;
+ const char *mmap;
+ size_t mmap_size;
+ unsigned long src_offset;
+};
+
+static void *load_index_extensions(void *_data)
+{
+ struct load_index_extensions *p = _data;
+ unsigned long src_offset = p->src_offset;
+
+ while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ uint32_t extsize = get_be32(p->mmap + src_offset + 4);
+ if (read_index_extension(p->istate,
+ p->mmap + src_offset,
+ p->mmap + src_offset + 8,
+ extsize) < 0) {
+ munmap((void *)p->mmap, p->mmap_size);
+ die(_("index file corrupt"));
+ }
+ src_offset += 8;
+ src_offset += extsize;
+ }
+
+ return NULL;
+}
+
+/*
+ * A helper function that will load the specified range of cache entries
+ * from the memory mapped file and add them to the given index.
+ */
+static unsigned long load_cache_entry_block(struct index_state *istate,
+ struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
+ unsigned long start_offset, const struct cache_entry *previous_ce)
+{
+ int i;
+ unsigned long src_offset = start_offset;
+
+ for (i = offset; i < offset + nr; i++) {
+ struct ondisk_cache_entry *disk_ce;
+ struct cache_entry *ce;
+ unsigned long consumed;
+
+ disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
+ ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+ set_index_entry(istate, i, ce);
+
+ src_offset += consumed;
+ previous_ce = ce;
+ }
+ return src_offset - start_offset;
+}
+
+static unsigned long load_all_cache_entries(struct index_state *istate,
+ const char *mmap, size_t mmap_size, unsigned long src_offset)
+{
+ unsigned long consumed;
+
+ istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
+ if (istate->version == 4) {
+ mem_pool_init(istate->ce_mem_pool,
+ estimate_cache_size_from_compressed(istate->cache_nr));
+ } else {
+ mem_pool_init(istate->ce_mem_pool,
+ estimate_cache_size(mmap_size, istate->cache_nr));
+ }
+
+ consumed = load_cache_entry_block(istate, istate->ce_mem_pool,
+ 0, istate->cache_nr, mmap, src_offset, NULL);
+ return consumed;
+}
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to online_cpus() threads, and we want
+ * to have at least 10000 cache entries per thread for it to
+ * be worth starting a thread.
+ */
+
+#define THREAD_COST (10000)
+
+struct load_cache_entries_thread_data
+{
+ pthread_t pthread;
+ struct index_state *istate;
+ struct mem_pool *ce_mem_pool;
+ int offset;
+ const char *mmap;
+ struct index_entry_offset_table *ieot;
+ int ieot_start; /* starting index into the ieot array */
+ int ieot_blocks; /* count of ieot entries to process */
+ unsigned long consumed; /* return # of bytes in index file processed */
+};
+
+/*
+ * A thread proc to run the load_cache_entries() computation
+ * across multiple background threads.
+ */
+static void *load_cache_entries_thread(void *_data)
+{
+ struct load_cache_entries_thread_data *p = _data;
+ int i;
+
+ /* iterate across all ieot blocks assigned to this thread */
+ for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) {
+ p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool,
+ p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL);
+ p->offset += p->ieot->entries[i].nr;
+ }
+ return NULL;
+}
+
+static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
+ int nr_threads, struct index_entry_offset_table *ieot)
+{
+ int i, offset, ieot_blocks, ieot_start, err;
+ struct load_cache_entries_thread_data *data;
+ unsigned long consumed = 0;
+
+ /* a little sanity checking */
+ if (istate->name_hash_initialized)
+ BUG("the name hash isn't thread safe");
+
+ istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
+ mem_pool_init(istate->ce_mem_pool, 0);
+
+ /* ensure we have no more threads than we have blocks to process */
+ if (nr_threads > ieot->nr)
+ nr_threads = ieot->nr;
+ data = xcalloc(nr_threads, sizeof(*data));
+
+ offset = ieot_start = 0;
+ ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads);
+ for (i = 0; i < nr_threads; i++) {
+ struct load_cache_entries_thread_data *p = &data[i];
+ int nr, j;
+
+ if (ieot_start + ieot_blocks > ieot->nr)
+ ieot_blocks = ieot->nr - ieot_start;
+
+ p->istate = istate;
+ p->offset = offset;
+ p->mmap = mmap;
+ p->ieot = ieot;
+ p->ieot_start = ieot_start;
+ p->ieot_blocks = ieot_blocks;
+
+ /* create a mem_pool for each thread */
+ nr = 0;
+ for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
+ nr += p->ieot->entries[j].nr;
+ p->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool));
+ if (istate->version == 4) {
+ mem_pool_init(p->ce_mem_pool,
+ estimate_cache_size_from_compressed(nr));
+ } else {
+ mem_pool_init(p->ce_mem_pool,
+ estimate_cache_size(mmap_size, nr));
+ }
+
+ err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p);
+ if (err)
+ die(_("unable to create load_cache_entries thread: %s"), strerror(err));
+
+ /* increment by the number of cache entries in the ieot block being processed */
+ for (j = 0; j < ieot_blocks; j++)
+ offset += ieot->entries[ieot_start + j].nr;
+ ieot_start += ieot_blocks;
+ }
+
+ for (i = 0; i < nr_threads; i++) {
+ struct load_cache_entries_thread_data *p = &data[i];
+
+ err = pthread_join(p->pthread, NULL);
+ if (err)
+ die(_("unable to join load_cache_entries thread: %s"), strerror(err));
+ mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool);
+ consumed += p->consumed;
+ }
+
+ free(data);
+
+ return consumed;
+}
+
+/* remember to discard_cache() before reading a different cache! */
+int do_read_index(struct index_state *istate, const char *path, int must_exist)
+{
+ int fd;
+ struct stat st;
+ unsigned long src_offset;
+ const struct cache_header *hdr;
+ const char *mmap;
+ size_t mmap_size;
+ struct load_index_extensions p;
+ size_t extension_offset = 0;
+ int nr_threads, cpus;
+ struct index_entry_offset_table *ieot = NULL;
+
+ if (istate->initialized)
+ return istate->cache_nr;
+
+ istate->timestamp.sec = 0;
+ istate->timestamp.nsec = 0;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ if (!must_exist && errno == ENOENT)
+ return 0;
+ die_errno(_("%s: index file open failed"), path);
+ }
+
+ if (fstat(fd, &st))
+ die_errno(_("%s: cannot stat the open index"), path);