From 001fe6c8e2ba55ee34b34dbcfe6aee605dbcf6d0 Mon Sep 17 00:00:00 2001 From: Matt Edmondson Date: Mon, 24 Aug 2026 01:30:55 +1000 Subject: [PATCH] docs: correct the decompress and decrypt cancellation remarks [patch] The remarks on `TryDecompressAsync` and `TryDecryptAsync` shipped in 2.2.0 claiming that cancellation "still flushes the trailer" or "still flushes the final block". That is false on those paths. The decompressing and decrypting streams wrap the source, not the destination, and are constructed with `leaveOpen: true`, so disposing them writes nothing to the destination. The destination only ever receives the output of `CopyToAsync`. The advice to treat a partial destination as invalid was right, but the reason given was wrong. State the real one: raw decompressed bytes and recovered plaintext carry no structure of their own, so a truncated destination cannot be told apart from a complete one by inspecting it. The matching remarks on the compress and encrypt paths are correct and are left alone. There, the stream does wrap the destination and disposal does write the trailer or final cipher block. Documentation only. No behavior change, and no test asserted the wrong claim. --- .../BrotliCompressionProvider.cs | 10 ++++++---- .../DeflateCompressionProvider.cs | 10 ++++++---- .../GzipCompressionProvider.cs | 10 ++++++---- .../ZLibCompressionProvider.cs | 10 ++++++---- .../AesEncryptionProvider.cs | 10 ++++++---- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/Essentials.CompressionProviders.Brotli/BrotliCompressionProvider.cs b/Essentials.CompressionProviders.Brotli/BrotliCompressionProvider.cs index 527ef79..2769bc2 100644 --- a/Essentials.CompressionProviders.Brotli/BrotliCompressionProvider.cs +++ b/Essentials.CompressionProviders.Brotli/BrotliCompressionProvider.cs @@ -246,10 +246,12 @@ public bool TryDecompress(Stream compressedData, Stream destination) /// Tries to decompress the data from the stream and write the result to the destination, asynchronously. /// /// - /// Genuinely asynchronous: no thread is held for the duration. - /// If this throws or returns false, the destination may hold a partial result. Cancellation still - /// flushes the trailer, so a truncated destination cannot be distinguished from a complete - /// decompression. Treat the destination as invalid unless the method returns true. + /// Genuinely asynchronous: no thread is held for the duration. The decompression stream wraps the + /// source rather than the destination, so disposing it writes nothing to the destination. + /// If this throws or returns false, the destination may hold a partial result. Decompressed bytes + /// carry no structure of their own, so a truncated destination cannot be distinguished from a + /// complete decompression by inspecting it. Treat the destination as invalid unless the method + /// returns true. /// /// The compressed data to decompress. /// The destination to write the decompressed data to. diff --git a/Essentials.CompressionProviders.Deflate/DeflateCompressionProvider.cs b/Essentials.CompressionProviders.Deflate/DeflateCompressionProvider.cs index a12df1a..365bc7a 100644 --- a/Essentials.CompressionProviders.Deflate/DeflateCompressionProvider.cs +++ b/Essentials.CompressionProviders.Deflate/DeflateCompressionProvider.cs @@ -246,10 +246,12 @@ public bool TryDecompress(Stream compressedData, Stream destination) /// Tries to decompress the data from the stream and write the result to the destination, asynchronously. /// /// - /// Genuinely asynchronous: no thread is held for the duration. - /// If this throws or returns false, the destination may hold a partial result. Cancellation still - /// flushes the trailer, so a truncated destination cannot be distinguished from a complete - /// decompression. Treat the destination as invalid unless the method returns true. + /// Genuinely asynchronous: no thread is held for the duration. The decompression stream wraps the + /// source rather than the destination, so disposing it writes nothing to the destination. + /// If this throws or returns false, the destination may hold a partial result. Decompressed bytes + /// carry no structure of their own, so a truncated destination cannot be distinguished from a + /// complete decompression by inspecting it. Treat the destination as invalid unless the method + /// returns true. /// /// The compressed data to decompress. /// The destination to write the decompressed data to. diff --git a/Essentials.CompressionProviders.Gzip/GzipCompressionProvider.cs b/Essentials.CompressionProviders.Gzip/GzipCompressionProvider.cs index 44f4f2a..468b7e1 100644 --- a/Essentials.CompressionProviders.Gzip/GzipCompressionProvider.cs +++ b/Essentials.CompressionProviders.Gzip/GzipCompressionProvider.cs @@ -247,10 +247,12 @@ public bool TryDecompress(Stream compressedData, Stream destination) /// Tries to decompress the data from the stream and write the result to the destination, asynchronously. /// /// - /// Genuinely asynchronous: no thread is held for the duration. - /// If this throws or returns false, the destination may hold a partial result. Cancellation still - /// flushes the trailer, so a truncated destination cannot be distinguished from a complete - /// decompression. Treat the destination as invalid unless the method returns true. + /// Genuinely asynchronous: no thread is held for the duration. The decompression stream wraps the + /// source rather than the destination, so disposing it writes nothing to the destination. + /// If this throws or returns false, the destination may hold a partial result. Decompressed bytes + /// carry no structure of their own, so a truncated destination cannot be distinguished from a + /// complete decompression by inspecting it. Treat the destination as invalid unless the method + /// returns true. /// /// The compressed data to decompress. /// The destination to write the decompressed data to. diff --git a/Essentials.CompressionProviders.ZLib/ZLibCompressionProvider.cs b/Essentials.CompressionProviders.ZLib/ZLibCompressionProvider.cs index 3f973e8..66a2d86 100644 --- a/Essentials.CompressionProviders.ZLib/ZLibCompressionProvider.cs +++ b/Essentials.CompressionProviders.ZLib/ZLibCompressionProvider.cs @@ -246,10 +246,12 @@ public bool TryDecompress(Stream compressedData, Stream destination) /// Tries to decompress the data from the stream and write the result to the destination, asynchronously. /// /// - /// Genuinely asynchronous: no thread is held for the duration. - /// If this throws or returns false, the destination may hold a partial result. Cancellation still - /// flushes the trailer, so a truncated destination cannot be distinguished from a complete - /// decompression. Treat the destination as invalid unless the method returns true. + /// Genuinely asynchronous: no thread is held for the duration. The decompression stream wraps the + /// source rather than the destination, so disposing it writes nothing to the destination. + /// If this throws or returns false, the destination may hold a partial result. Decompressed bytes + /// carry no structure of their own, so a truncated destination cannot be distinguished from a + /// complete decompression by inspecting it. Treat the destination as invalid unless the method + /// returns true. /// /// The compressed data to decompress. /// The destination to write the decompressed data to. diff --git a/Essentials.EncryptionProviders.Aes/AesEncryptionProvider.cs b/Essentials.EncryptionProviders.Aes/AesEncryptionProvider.cs index 9fd5da9..0af94be 100644 --- a/Essentials.EncryptionProviders.Aes/AesEncryptionProvider.cs +++ b/Essentials.EncryptionProviders.Aes/AesEncryptionProvider.cs @@ -290,10 +290,12 @@ public bool TryDecrypt(Stream data, ReadOnlySpan key, ReadOnlySpan i /// Tries to decrypt the data from the stream and write the result to the destination, asynchronously. /// /// - /// Genuinely asynchronous: no thread is held for the duration. - /// If this throws or returns false, the destination may hold a partial result. Cancellation still - /// flushes the final block, so a truncated destination cannot be distinguished from a complete - /// decryption. Treat the destination as invalid unless the method returns true. + /// Genuinely asynchronous: no thread is held for the duration. The decrypting stream wraps the + /// source rather than the destination, so disposing it writes nothing to the destination. + /// If this throws or returns false, the destination may hold a partial result. Recovered plaintext + /// carries no structure of its own, so a truncated destination cannot be distinguished from a + /// complete decryption by inspecting it. Treat the destination as invalid unless the method + /// returns true. /// /// The data to decrypt. /// The key to use for decryption.