@@ -175,7 +175,8 @@ pub const Manifest = struct {
175
175
hash : HashHelper ,
176
176
manifest_file : ? fs.File ,
177
177
manifest_dirty : bool ,
178
- files : std .ArrayListUnmanaged (File ) = .{},
178
+ /// The key is the resolved_path.
179
+ files : std .StringArrayHashMapUnmanaged (File ) = .{},
179
180
hex_digest : [hex_digest_len ]u8 ,
180
181
181
182
/// Add a file as a dependency of process being cached. When `hit` is
@@ -186,30 +187,36 @@ pub const Manifest = struct {
186
187
/// are allowed to take up in memory. If max_file_size is null, then the contents
187
188
/// will not be loaded into memory.
188
189
///
189
- /// Returns the index of the entry in the `files` array list . You can use it
190
+ /// Returns the index of the entry in `files`. You can use it
190
191
/// to access the contents of the file after calling `hit()` like so:
191
192
///
192
193
/// ```
193
- /// var file_contents = cache_hash.files.items[file_index ].contents.?;
194
+ /// const file_contents = cache_hash.files.entries. items[index ].contents.?;
194
195
/// ```
195
196
pub fn addFile (self : * Manifest , file_path : []const u8 , max_file_size : ? usize ) ! usize {
196
197
assert (self .manifest_file == null );
197
198
198
- try self .files .ensureCapacity (self .cache .gpa , self .files .items .len + 1 );
199
- const resolved_path = try fs .path .resolve (self .cache .gpa , &[_ ][]const u8 {file_path });
200
-
201
- const idx = self .files .items .len ;
202
- self .files .addOneAssumeCapacity ().* = .{
203
- .path = resolved_path ,
204
- .contents = null ,
205
- .max_file_size = max_file_size ,
206
- .stat = undefined ,
207
- .bin_digest = undefined ,
208
- };
209
-
199
+ var index = self .files .count ();
200
+ try self .files .ensureCapacity (self .cache .gpa , index + 1 );
201
+ var resolved_path : []const u8 = try fs .path .resolve (self .cache .gpa , &[_ ][]const u8 {file_path });
202
+ const gop = self .files .getOrPutAssumeCapacity (resolved_path );
203
+ if (gop .found_existing ) {
204
+ self .cache .gpa .free (resolved_path );
205
+ resolved_path = gop .entry .key ;
206
+ // TODO getOrPut should give us the index
207
+ // https://github.com/ziglang/zig/issues/7391
208
+ index = self .files .getIndex (resolved_path ).? ;
209
+ } else {
210
+ gop .entry .value = .{
211
+ .path = resolved_path ,
212
+ .contents = null ,
213
+ .max_file_size = max_file_size ,
214
+ .stat = undefined ,
215
+ .bin_digest = undefined ,
216
+ };
217
+ }
210
218
self .hash .addBytes (resolved_path );
211
-
212
- return idx ;
219
+ return index ;
213
220
}
214
221
215
222
pub fn addOptionalFile (self : * Manifest , optional_file_path : ? []const u8 ) ! void {
@@ -252,7 +259,7 @@ pub const Manifest = struct {
252
259
mem .copy (u8 , & manifest_file_path , & self .hex_digest );
253
260
manifest_file_path [self .hex_digest .len .. ][0.. ext .len ].* = ext .* ;
254
261
255
- if (self .files .items . len != 0 ) {
262
+ if (self .files .count () != 0 ) {
256
263
self .manifest_file = try self .cache .manifest_dir .createFile (& manifest_file_path , .{
257
264
.read = true ,
258
265
.truncate = false ,
@@ -282,23 +289,26 @@ pub const Manifest = struct {
282
289
const file_contents = try self .manifest_file .? .inStream ().readAllAlloc (self .cache .gpa , manifest_file_size_max );
283
290
defer self .cache .gpa .free (file_contents );
284
291
285
- const input_file_count = self .files .items . len ;
292
+ const input_file_count = self .files .count () ;
286
293
var any_file_changed = false ;
287
294
var line_iter = mem .tokenize (file_contents , "\n " );
288
295
var idx : usize = 0 ;
289
296
while (line_iter .next ()) | line | {
290
- defer idx += 1 ;
291
-
292
- const cache_hash_file = if (idx < input_file_count ) & self .files .items [idx ] else blk : {
293
- const new = try self .files .addOne (self .cache .gpa );
294
- new .* = .{
297
+ var new_file : File = undefined ;
298
+ var is_new = false ;
299
+
300
+ const cache_hash_file = if (idx < input_file_count )
301
+ & self .files .entries .items [idx ].value
302
+ else blk : {
303
+ is_new = true ;
304
+ new_file = .{
295
305
.path = null ,
296
306
.contents = null ,
297
307
.max_file_size = null ,
298
308
.stat = undefined ,
299
309
.bin_digest = undefined ,
300
310
};
301
- break :blk new ;
311
+ break :blk & new_file ;
302
312
};
303
313
304
314
var iter = mem .tokenize (line , " " );
@@ -360,6 +370,12 @@ pub const Manifest = struct {
360
370
if (! any_file_changed ) {
361
371
self .hash .hasher .update (& cache_hash_file .bin_digest );
362
372
}
373
+
374
+ if (is_new ) {
375
+ try self .files .putNoClobber (self .cache .gpa , new_file .path .? , new_file );
376
+ }
377
+
378
+ idx += 1 ;
363
379
}
364
380
365
381
if (any_file_changed ) {
@@ -372,7 +388,7 @@ pub const Manifest = struct {
372
388
if (idx < input_file_count ) {
373
389
self .manifest_dirty = true ;
374
390
while (idx < input_file_count ) : (idx += 1 ) {
375
- const ch_file = & self .files .items [idx ];
391
+ const ch_file = & self .files .entries . items [idx ]. value ;
376
392
try self .populateFileHash (ch_file );
377
393
}
378
394
return false ;
@@ -387,13 +403,16 @@ pub const Manifest = struct {
387
403
self .hash .hasher .update (& bin_digest );
388
404
389
405
// Remove files not in the initial hash.
390
- for (self .files .items [input_file_count .. ]) | * file | {
391
- file .deinit (self .cache .gpa );
406
+ // TODO this code can get cleaner and more efficient after
407
+ // https://github.com/ziglang/zig/issues/7391
408
+ while (self .files .count () > input_file_count ) {
409
+ const file_path = self .files .entries .items [self .files .entries .items .len - 1 ].value .path .? ;
410
+ var entry = self .files .remove (file_path ).? ;
411
+ entry .value .deinit (self .cache .gpa );
392
412
}
393
- self .files .shrinkRetainingCapacity (input_file_count );
394
413
395
- for (self .files .items ) | file | {
396
- self .hash .hasher .update (& file .bin_digest );
414
+ for (self .files .entries . items ) | * entry | {
415
+ self .hash .hasher .update (& entry . value .bin_digest );
397
416
}
398
417
}
399
418
@@ -445,21 +464,27 @@ pub const Manifest = struct {
445
464
assert (self .manifest_file != null );
446
465
447
466
const resolved_path = try fs .path .resolve (self .cache .gpa , &[_ ][]const u8 {file_path });
448
- errdefer self .cache .gpa .free (resolved_path );
467
+ var keep_resolved_path = false ;
468
+ defer if (! keep_resolved_path ) self .cache .gpa .free (resolved_path );
449
469
450
- const new_ch_file = try self .files .addOne (self .cache .gpa );
451
- new_ch_file .* = .{
470
+ const gop = try self .files .getOrPut (self .cache .gpa , resolved_path );
471
+ if (gop .found_existing ) {
472
+ return gop .entry .value .contents .? ;
473
+ }
474
+ gop .entry .value = .{
452
475
.path = resolved_path ,
453
476
.max_file_size = max_file_size ,
454
477
.stat = undefined ,
455
478
.bin_digest = undefined ,
456
479
.contents = null ,
457
480
};
458
- errdefer self .files .shrinkRetainingCapacity (self .files .items .len - 1 );
481
+ // TODO after https://github.com/ziglang/zig/issues/7391 this can be replaced with pop()
482
+ errdefer self .files .removeAssertDiscard (resolved_path );
459
483
460
- try self .populateFileHash (new_ch_file );
484
+ try self .populateFileHash (& gop . entry . value );
461
485
462
- return new_ch_file .contents .? ;
486
+ keep_resolved_path = true ;
487
+ return gop .entry .value .contents .? ;
463
488
}
464
489
465
490
/// Add a file as a dependency of process being cached, after the initial hash has been
@@ -470,19 +495,26 @@ pub const Manifest = struct {
470
495
assert (self .manifest_file != null );
471
496
472
497
const resolved_path = try fs .path .resolve (self .cache .gpa , &[_ ][]const u8 {file_path });
473
- errdefer self .cache .gpa .free (resolved_path );
498
+ var keep_resolved_path = false ;
499
+ defer if (! keep_resolved_path ) self .cache .gpa .free (resolved_path );
474
500
475
- const new_ch_file = try self .files .addOne (self .cache .gpa );
476
- new_ch_file .* = .{
501
+ const gop = try self .files .getOrPut (self .cache .gpa , resolved_path );
502
+ if (gop .found_existing ) {
503
+ return ;
504
+ }
505
+ gop .entry .value = .{
477
506
.path = resolved_path ,
478
507
.max_file_size = null ,
479
508
.stat = undefined ,
480
509
.bin_digest = undefined ,
481
510
.contents = null ,
482
511
};
483
- errdefer self .files .shrinkRetainingCapacity (self .files .items .len - 1 );
512
+ // TODO after https://github.com/ziglang/zig/issues/7391 this can be replaced with pop()
513
+ errdefer self .files .removeAssertDiscard (resolved_path );
514
+
515
+ try self .populateFileHash (& gop .entry .value );
484
516
485
- try self . populateFileHash ( new_ch_file ) ;
517
+ keep_resolved_path = true ;
486
518
}
487
519
488
520
pub fn addDepFilePost (self : * Manifest , dir : fs.Dir , dep_file_basename : []const u8 ) ! void {
@@ -549,7 +581,8 @@ pub const Manifest = struct {
549
581
const writer = contents .writer ();
550
582
var encoded_digest : [hex_digest_len ]u8 = undefined ;
551
583
552
- for (self .files .items ) | file | {
584
+ for (self .files .entries .items ) | entry | {
585
+ const file = & entry .value ;
553
586
_ = std .fmt .bufPrint (& encoded_digest , "{x}" , .{file .bin_digest }) catch unreachable ;
554
587
try writer .print ("{d} {d} {d} {s} {s}\n " , .{
555
588
file .stat .size ,
@@ -581,8 +614,8 @@ pub const Manifest = struct {
581
614
if (self .manifest_file ) | file | {
582
615
file .close ();
583
616
}
584
- for (self .files .items ) | * file | {
585
- file .deinit (self .cache .gpa );
617
+ for (self .files .entries . items ) | * file_entry | {
618
+ file_entry . value .deinit (self .cache .gpa );
586
619
}
587
620
self .files .deinit (self .cache .gpa );
588
621
}
@@ -775,7 +808,11 @@ test "check that changing a file makes cache fail" {
775
808
// There should be nothing in the cache
776
809
testing .expectEqual (false , try ch .hit ());
777
810
778
- testing .expect (mem .eql (u8 , original_temp_file_contents , ch .files .items [temp_file_idx ].contents .? ));
811
+ testing .expect (mem .eql (
812
+ u8 ,
813
+ original_temp_file_contents ,
814
+ ch .files .entries .items [temp_file_idx ].value .contents .? ,
815
+ ));
779
816
780
817
digest1 = ch .final ();
781
818
@@ -795,7 +832,7 @@ test "check that changing a file makes cache fail" {
795
832
testing .expectEqual (false , try ch .hit ());
796
833
797
834
// The cache system does not keep the contents of re-hashed input files.
798
- testing .expect (ch .files .items [temp_file_idx ].contents == null );
835
+ testing .expect (ch .files .entries . items [temp_file_idx ]. value .contents == null );
799
836
800
837
digest2 = ch .final ();
801
838
0 commit comments