Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Make allocations when decoding fallible #974

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ParkMyCar
Copy link

This PR makes any allocations that occur in the decode path fallible, without any public API changes.

The changes are specifically scoped to repeated fields, Vec<u8>, and String.

  • Repeated fields, before pushing onto the Vec<...> of values, we now call Vec::try_reserve(1) and map the possible TryReserveError to a DecodeError. Reserving for a single element is what Vec already does so there shouldn't be a performance impact, and DecodeError is already opaque so this doesn't change the public API surface.
  • Vec<u8> and String, these types reserve space as part of the sealed::BytesAdapter::repliace_with trait. This trait method was updated to return a Result<(), TryReserveError> and now will fail if we can't reserve enough space. Given this is a sealed trait there are no public API changes.

In addition to making allocations fallible, I also changed the merge impl of Vec<u8> to use bytes::merge_one_copy like String does, this should result in strictly less allocations.

Why make this change?

Previously users had no way to guard against OOMs in decoding. You could try to make a guess based on the size of the encoded message, but this fairly inaccurate because it would be very difficult (impossible?) to account for things like the ammortized growth of a Vec. Even if you did guess based on the size of the encoded message, there is no way you could account for multiple messages being decoded in parallel in individual tasks, e.g. handling multiple network requests in parallel.

What about the encoding path?

A similar change is not needed for the encoding path because you can already guard against OOMs by using the encoded_len() to allocate a buffer yourself guarding against OOMs, and then encode into this newly allocated buffer.

@ParkMyCar ParkMyCar changed the title feat: Make allocations fallible feat: Make allocations when decoding fallible Jan 26, 2024
Copy link
Collaborator

@caspermeijn caspermeijn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I don't need this myself, I am open to supporting this. However, the current PR is not maintainable over the long term, as it doesn't prove that all allocations are fallible. To ease the work of the maintainer, the test suite should fail if an infallible allocation is added in the future.

Please add a test that proves the guarantee that decoding will not crash due to memory scarcity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants