@@ -23,17 +23,23 @@ static int dir_entry_cmp(const struct dir_entry *e1,
23
23
name ? name : e2 -> name , e1 -> namelen );
24
24
}
25
25
26
- static struct dir_entry * find_dir_entry (struct index_state * istate ,
27
- const char * name , unsigned int namelen )
26
+ static struct dir_entry * find_dir_entry__hash (struct index_state * istate ,
27
+ const char * name , unsigned int namelen , unsigned int hash )
28
28
{
29
29
struct dir_entry key ;
30
- hashmap_entry_init (& key , memihash ( name , namelen ) );
30
+ hashmap_entry_init (& key , hash );
31
31
key .namelen = namelen ;
32
32
return hashmap_get (& istate -> dir_hash , & key , name );
33
33
}
34
34
35
+ static struct dir_entry * find_dir_entry (struct index_state * istate ,
36
+ const char * name , unsigned int namelen )
37
+ {
38
+ return find_dir_entry__hash (istate , name , namelen , memihash (name ,namelen ));
39
+ }
40
+
35
41
static struct dir_entry * hash_dir_entry (struct index_state * istate ,
36
- struct cache_entry * ce , int namelen )
42
+ struct cache_entry * ce , int namelen , struct dir_entry * * p_previous_dir )
37
43
{
38
44
/*
39
45
* Throw each directory component in the hash for quick lookup
@@ -43,6 +49,18 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
43
49
* in index_state.name_hash (as ordinary cache_entries).
44
50
*/
45
51
struct dir_entry * dir ;
52
+ unsigned int hash ;
53
+ int use_precomputed_dir_hash = 0 ;
54
+
55
+ if (ce -> precompute_hash_state & CE_PRECOMPUTE_HASH_STATE__SET ) {
56
+ if (!(ce -> precompute_hash_state & CE_PRECOMPUTE_HASH_STATE__DIR ))
57
+ return NULL ; /* item does not have a parent directory */
58
+ if (namelen == ce_namelen (ce )) {
59
+ /* dir hash only valid for outer-most call (not recursive ones) */
60
+ use_precomputed_dir_hash = 1 ;
61
+ hash = ce -> precompute_hash_dir ;
62
+ }
63
+ }
46
64
47
65
/* get length of parent directory */
48
66
while (namelen > 0 && !is_dir_sep (ce -> name [namelen - 1 ]))
@@ -52,24 +70,43 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
52
70
namelen -- ;
53
71
54
72
/* lookup existing entry for that directory */
55
- dir = find_dir_entry (istate , ce -> name , namelen );
73
+ if (p_previous_dir && * p_previous_dir
74
+ && namelen == (* p_previous_dir )-> namelen
75
+ && memcmp (ce -> name , (* p_previous_dir )-> name , namelen ) == 0 ) {
76
+ /*
77
+ * When our caller is sequentially iterating thru the index,
78
+ * items in the same directory will be sequential, and therefore
79
+ * refer to the same dir_entry.
80
+ */
81
+ dir = * p_previous_dir ;
82
+ } else {
83
+ if (!use_precomputed_dir_hash )
84
+ hash = memihash (ce -> name , namelen );
85
+ dir = find_dir_entry__hash (istate , ce -> name , namelen , hash );
86
+ }
87
+
56
88
if (!dir ) {
57
89
/* not found, create it and add to hash table */
58
90
FLEX_ALLOC_MEM (dir , name , ce -> name , namelen );
59
- hashmap_entry_init (dir , memihash ( ce -> name , namelen ) );
91
+ hashmap_entry_init (dir , hash );
60
92
dir -> namelen = namelen ;
61
93
hashmap_add (& istate -> dir_hash , dir );
62
94
63
95
/* recursively add missing parent directories */
64
- dir -> parent = hash_dir_entry (istate , ce , namelen );
96
+ dir -> parent = hash_dir_entry (istate , ce , namelen , NULL );
65
97
}
98
+
99
+ if (p_previous_dir )
100
+ * p_previous_dir = dir ;
101
+
66
102
return dir ;
67
103
}
68
104
69
- static void add_dir_entry (struct index_state * istate , struct cache_entry * ce )
105
+ static void add_dir_entry (struct index_state * istate , struct cache_entry * ce ,
106
+ struct dir_entry * * p_previous_dir )
70
107
{
71
108
/* Add reference to the directory entry (and parents if 0). */
72
- struct dir_entry * dir = hash_dir_entry (istate , ce , ce_namelen (ce ));
109
+ struct dir_entry * dir = hash_dir_entry (istate , ce , ce_namelen (ce ), p_previous_dir );
73
110
while (dir && !(dir -> nr ++ ))
74
111
dir = dir -> parent ;
75
112
}
@@ -80,7 +117,7 @@ static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)
80
117
* Release reference to the directory entry. If 0, remove and continue
81
118
* with parent directory.
82
119
*/
83
- struct dir_entry * dir = hash_dir_entry (istate , ce , ce_namelen (ce ));
120
+ struct dir_entry * dir = hash_dir_entry (istate , ce , ce_namelen (ce ), NULL );
84
121
while (dir && !(-- dir -> nr )) {
85
122
struct dir_entry * parent = dir -> parent ;
86
123
hashmap_remove (& istate -> dir_hash , dir , NULL );
@@ -89,16 +126,25 @@ static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)
89
126
}
90
127
}
91
128
92
- static void hash_index_entry (struct index_state * istate , struct cache_entry * ce )
129
+ static void hash_index_entry (struct index_state * istate , struct cache_entry * ce ,
130
+ struct dir_entry * * p_previous_dir )
93
131
{
132
+ unsigned int h ;
133
+
94
134
if (ce -> ce_flags & CE_HASHED )
95
135
return ;
96
136
ce -> ce_flags |= CE_HASHED ;
97
- hashmap_entry_init (ce , memihash (ce -> name , ce_namelen (ce )));
137
+
138
+ if (ce -> precompute_hash_state & CE_PRECOMPUTE_HASH_STATE__SET )
139
+ h = ce -> precompute_hash_name ;
140
+ else
141
+ h = memihash (ce -> name , ce_namelen (ce ));
142
+
143
+ hashmap_entry_init (ce , h );
98
144
hashmap_add (& istate -> name_hash , ce );
99
145
100
146
if (ignore_case )
101
- add_dir_entry (istate , ce );
147
+ add_dir_entry (istate , ce , p_previous_dir );
102
148
}
103
149
104
150
static int cache_entry_cmp (const struct cache_entry * ce1 ,
@@ -114,22 +160,24 @@ static int cache_entry_cmp(const struct cache_entry *ce1,
114
160
115
161
static void lazy_init_name_hash (struct index_state * istate )
116
162
{
163
+ struct dir_entry * previous_dir = NULL ;
117
164
int nr ;
118
165
119
166
if (istate -> name_hash_initialized )
120
167
return ;
121
168
hashmap_init (& istate -> name_hash , (hashmap_cmp_fn ) cache_entry_cmp ,
122
169
istate -> cache_nr );
123
- hashmap_init (& istate -> dir_hash , (hashmap_cmp_fn ) dir_entry_cmp , 0 );
170
+ hashmap_init (& istate -> dir_hash , (hashmap_cmp_fn ) dir_entry_cmp ,
171
+ istate -> cache_nr );
124
172
for (nr = 0 ; nr < istate -> cache_nr ; nr ++ )
125
- hash_index_entry (istate , istate -> cache [nr ]);
173
+ hash_index_entry (istate , istate -> cache [nr ], & previous_dir );
126
174
istate -> name_hash_initialized = 1 ;
127
175
}
128
176
129
177
void add_name_hash (struct index_state * istate , struct cache_entry * ce )
130
178
{
131
179
if (istate -> name_hash_initialized )
132
- hash_index_entry (istate , ce );
180
+ hash_index_entry (istate , ce , NULL );
133
181
}
134
182
135
183
void remove_name_hash (struct index_state * istate , struct cache_entry * ce )
@@ -236,3 +284,45 @@ void free_name_hash(struct index_state *istate)
236
284
hashmap_free (& istate -> name_hash , 0 );
237
285
hashmap_free (& istate -> dir_hash , 1 );
238
286
}
287
+
288
+ /*
289
+ * Precompute the hash values for this cache_entry
290
+ * for use in the istate.name_hash and istate.dir_hash.
291
+ *
292
+ * If the item is in the root directory, just compute the
293
+ * hash value (for istate.name_hash) on the full path.
294
+ *
295
+ * If the item is in a subdirectory, first compute the
296
+ * hash value for the immediate parent directory (for
297
+ * istate.dir_hash) and then the hash value for the full
298
+ * path by continuing the computation.
299
+ *
300
+ * Note that these hashes will be used by
301
+ * wt_status_collect_untracked() as it scans the worktree
302
+ * and maps observed paths back to the index (optionally
303
+ * ignoring case). Therefore, we probably only *NEED* to
304
+ * precompute this for non-skip-worktree items (since
305
+ * status should not observe skipped items), but because
306
+ * lazy_init_name_hash() hashes everything, we force it
307
+ * here.
308
+ */
309
+ void precompute_istate_hashes (struct cache_entry * ce )
310
+ {
311
+ int namelen = ce_namelen (ce );
312
+
313
+ while (namelen > 0 && !is_dir_sep (ce -> name [namelen - 1 ]))
314
+ namelen -- ;
315
+
316
+ if (namelen <= 0 ) {
317
+ ce -> precompute_hash_name = memihash (ce -> name , ce_namelen (ce ));
318
+ ce -> precompute_hash_state = CE_PRECOMPUTE_HASH_STATE__SET ;
319
+ } else {
320
+ namelen -- ;
321
+ ce -> precompute_hash_dir = memihash (ce -> name , namelen );
322
+ ce -> precompute_hash_name = memihash_cont (
323
+ ce -> precompute_hash_dir , & ce -> name [namelen ],
324
+ ce_namelen (ce ) - namelen );
325
+ ce -> precompute_hash_state =
326
+ CE_PRECOMPUTE_HASH_STATE__SET | CE_PRECOMPUTE_HASH_STATE__DIR ;
327
+ }
328
+ }
0 commit comments