zig fmt previously would write a temp file, and then either rename it
into place if necessary, or unlink it if nothing was changed. Now zig
fmt renders into a memory buffer, and only writes the temp file and
renames it into place if anything changed.
Based on the performance testing I did this actually did not have much
of an impact, however it's likely that on other operating systems and
other hard drives this could make a big difference.
* `std.fs.Dir.Entry.Kind` is moved to `std.fs.File.Kind`
* `std.fs.File.Stat` gains the `kind` field, so performing a stat() on
a File now tells what kind of file it is. On Windows this only will
distinguish between directories and files.
* rework zig fmt logic so that in the case of opening a file and
discovering it to be a directory, it closes the file descriptor
before re-opening it with O_DIRECTORY, using fewer simultaneous open
file descriptors when walking a directory tree.
* rework zig fmt logic so that it pays attention to the kind of
directory entries, and when it sees a sub-directory it attempts to
open it as a directory rather than a file, reducing the number of
open() syscalls when walking a directory tree.
The original check for a directory was for the `readAllAlloc` so move the check from open to read. This in turn fixes the fmt step in the build script for directories.
* Deleted decls are deleted; unused decls are also detected as deleted.
Cycles are not yet detected.
* Re-analysis is smarter and will not cause a re-analysis of dependants
when only a function body is changed.
The binary file abstraction changed its struct named "Decl" to
"TextBlock" and it now represents an allocated slice of memory in
the .text section. It has two new fields: prev and next, making it
a linked list node. This allows a TextBlock to find its neighbors.
The ElfFile struct now has free_list and last_text_block fields.
Doc comments for free_list are reproduced here:
A list of text blocks that have surplus capacity. This list can have false
positives, as functions grow and shrink over time, only sometimes being added
or removed from the freelist.
A text block has surplus capacity when its overcapacity value is greater than
minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
much extra capacity, that we could fit a small new symbol in it, itself with
ideal_capacity or more.
Ideal capacity is defined by size * alloc_num / alloc_den.
Overcapacity is measured by actual_capacity - ideal_capacity. Note that
overcapacity can be negative. A simple way to have negative overcapacity is to
allocate a fresh text block, which will have ideal capacity, and then grow it
by 1 byte. It will then have -1 overcapacity.
The last_text_block keeps track of the end of the .text section.
Allocation, freeing, and resizing decls are all now more sophisticated,
and participate in the virtual address allocation scheme. There is no
longer the possibility for virtual address collisions.