Remove allocation for ZSTs - #92
Conversation
emilio
left a comment
There was a problem hiding this comment.
Curious, can you elaborate on what's the rationale for this? It seems somewhat complicated and having an array of ZSTs is already pretty edgecasey.
In general, I'd rather keep the differences between the gecko-ffi path and the other as small as possible.
|
Deliberately constructing a Vec of ZSTs is very niche, but it's actually pretty common when generics comes into play, deeply nested in data structures. This lib might be a bridge for Gecko but it's used a lot outside of that space by other people, and removing allocations for ZSTs is basically free. Well I'm that someone lol And for keeping the same paths, it could be enabled on both since ZSTs should'nt be passed to C++ anyway |
|
Humm, tests are going to fail for "const_new", I can't make |
|
I think we should just bump the MSRV, see #93. Can you rebase atop? |
56c9eb0 to
4edd4dd
Compare
|
@emilio done |
emilio
left a comment
There was a problem hiding this comment.
Thanks, mostly nits, looks good with this!
Baptistemontan
left a comment
There was a problem hiding this comment.
Alright I removed a good chunk of unnecessary zst checks (either unneeded or checked afterward anyway).
I think this should wait for #94 to merge, there might be some conflicts as those two PRs both adresses ZSTs
emilio
left a comment
There was a problem hiding this comment.
Last pass, I promise. Fairly minor things :)
| /// len must be != 0, this uses the `NonNull` to store a length, so the length must be stored offset by one. | ||
| /// This function expect the len to be already shifted | ||
| #[inline(always)] | ||
| const unsafe fn len_to_ptr_unchecked<T: Sized>(len: usize) -> NonNull<T> { |
There was a problem hiding this comment.
Worth a debug_assert!(len != 0); at least.
There was a problem hiding this comment.
technically already checked by NonZero::new_unchecked but it would probably help with backtrace and stronger code documentation
| // `Drop` impl, trippng an assertion along that code path causes a | ||
| // double panic. We duplicate the assertion here so that it is | ||
| // testable, | ||
|
|
| /// | ||
| /// // Only true **without** the gecko-ffi feature! | ||
| /// // assert_eq!(vec_units.capacity(), usize::MAX); | ||
| /// // assert_eq!(vec_units.capacity(), usize::MAX - 1); |
There was a problem hiding this comment.
Ok, I enabled this in the other PR so will need a trivial-ish rebase.
There was a problem hiding this comment.
no sure what you mean by that
| debug_assert!( | ||
| len <= MAX_CAP, | ||
| "invalid set_len(usize::MAX) on ZST ThinVec (max cap is usize::MAX - 1)" | ||
| ); |
There was a problem hiding this comment.
I guess this assert isn't super useful, because len + 1 would debug-assert anyway. But fine to keep.
| fn is_singleton(&self) -> bool { | ||
| unsafe { self.ptr.as_ptr() as *const Header == &EMPTY_HEADER } | ||
| // could technicaly remove this branch | ||
| // but there is a 1/2^64 chance of the number of ZST being equal to &EMPTY_HEADER |
There was a problem hiding this comment.
I'd remove this comment, as given that chance you can't remove the branch, right? :)
| /// # Safety | ||
| /// | ||
| /// This function drop and deallocates the inner values of the `ThinVec`, | ||
| /// invariants are therefore brokens and the value must be considered dropped and should not be accessed again. |
| impl<T> MallocShallowSizeOf for ThinVec<T> { | ||
| fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { | ||
| if self.capacity() == 0 || self.uses_stack_allocated_buffer() { | ||
| if self.capacity() == 0 || self.uses_stack_allocated_buffer() || Self::is_zst() { |
There was a problem hiding this comment.
This could be simplified to if !self.has_allocation() {
…place inside the drop impl
527496d to
127a785
Compare
|
FWIW I don't think that the
https://doc.rust-lang.org/stable/reference/types/numeric.html#r-type.numeric.int.size.isize |
|
Thanks for the fix and for bearing with me btw! :) |
| /// **NOTE**: unlike `Vec`, `ThinVec` **MUST** allocate once to keep track of non-zero | ||
| /// lengths. As such, we cannot provide the same guarantees about ThinVecs | ||
| /// of ZSTs not allocating. However the allocation never needs to be resized | ||
| /// to add more ZSTs, since the underlying array is still length 0. |
There was a problem hiding this comment.
@emilio not sure what to do with this paragraph, now that ZSTs are simply not allowed with gecko-ffi, and don't allocate without the feature the whole thing is wrong.
what about
/// **NOTE**: like `Vec`, `ThinVec` does'nt allocate for ZSTs and store the length inline,
/// but creating a `ThinVec` of ZSTs is not allowed if the "gecko-ffi" feature is enabled.
No problem ! |
|
ahem, there's still some docs I was updating lol |
|
Err, sorry! Can you send another PR for those? |
sure, give me a moment |
Store the length as a
NonZero<usize>inside theNonNull<Header>, the pointer is actually never read for ZSTs, when one is needed just create a dangling one.The only problem is that now the length is off by one (as it can't be 0), this makes the maximum capacity
usize::MAX - 1instead, this might be a breaking change ?Did'nt enabled those optimizations for
feature = "gecko-ffi", as it can't be turned intonsTArray, tho from my understanding ZSTs are undefined in C++ so it would be UB to turn a ThinVec of ZSTs into a nsTArray anyway, but I prefer to get confirmation first