add sizehint! for `first` and make append!/prepend! safer (#51903)
First we add an optional API parameter for `sizehint!` that controls
whether it is for `push!` (default) or `pushfirst!`. Secondly, we make
the offset zero when using `sizehint!` to shrink an array from the end,
or the trailing size zero when using it to shring from the beginning.
Then we replace the prior implementations of `prepend!` and `append!`
with ones that are safe even if the iterator changes length during the
operation or if convert fails. The result of `prepend!` may be in an
undefined order (because of the `reverse!` call) in the presence of
concurrent modifications or errors, but at least all of the elements
will be present and valid afterwards.
Replaces and closes #49905
Replaces and closes #47391
Fixes #15868
Benchmarks show that repeated `push!` performance (with sizehint) is
nearly equivalent to the old append performance:
```
julia> @benchmark append!(x, 1:1000) setup=x=Vector{Float64}(undef,0)
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
Range (min … max): 1.027 μs … 72.871 μs ┊ GC (min … max): 0.00% … 94.57%
Time (median): 1.465 μs ┊ GC (median): 0.00%
Time (mean ± σ): 1.663 μs ± 1.832 μs ┊ GC (mean ± σ): 6.20% ± 5.67%
▂▃▅▆█▇▇▆▄▂▁
▂▁▁▂▂▂▂▃▄▅▇█████████████▇▆▅▅▅▅▅▅▄▅▄▅▅▅▆▇███▆▅▄▃▃▂▂▂▂▂▂▂▂▂▂ ▄
1.03 μs Histogram: frequency by time 2.31 μs <
Memory estimate: 19.69 KiB, allocs estimate: 0.
julia> @benchmark append!(x, 1:1000) setup=x=Vector{Int}(undef,0)
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
Range (min … max): 851.900 ns … 76.757 μs ┊ GC (min … max): 0.00% … 91.59%
Time (median): 1.181 μs ┊ GC (median): 0.00%
Time (mean ± σ): 1.543 μs ± 1.972 μs ┊ GC (mean ± σ): 6.75% ± 5.75%
▆█▇▃
▂▃██████▇▅▅▄▅▅▃▂▂▂▃▃▃▂▃▃▃▂▂▂▂▂▁▂▁▂▁▂▂▂▁▁▂▂▁▁▁▁▁▁▁▂▂▂▃▃▃▃▂▂▂▂ ▃
852 ns Histogram: frequency by time 4.07 μs <
Memory estimate: 19.69 KiB, allocs estimate: 0.
```
Co-authored-by: Sukera <Seelengrab@users.noreply.github.com>
Co-authored-by: MasonProtter <mason.protter@icloud.com>