diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj index 3b94db54c2..f5a6c610ac 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Microsoft.Azure.Cosmos.Encryption.Custom.csproj @@ -43,6 +43,7 @@ + diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs deleted file mode 100644 index 5b37ded4fb..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/RentArrayBufferWriter.cs +++ /dev/null @@ -1,211 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------ - -namespace Microsoft.Azure.Cosmos.Encryption.Custom; - -#if NET8_0_OR_GREATER - -using System; -using System.Buffers; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -/// -/// https://gist.github.com/ahsonkhan/c76a1cc4dc7107537c3fdc0079a68b35 -/// Standard ArrayBufferWriter is not using pooled memory -/// -internal class RentArrayBufferWriter : IBufferWriter, IDisposable -{ - private const int MinimumBufferSize = 256; - - private byte[] rentedBuffer; - private int written; - private long committed; - - public RentArrayBufferWriter(int initialCapacity = MinimumBufferSize) - { - if (initialCapacity <= 0) - { - throw new ArgumentException(null, nameof(initialCapacity)); - } - - this.rentedBuffer = ArrayPool.Shared.Rent(initialCapacity); - this.written = 0; - this.committed = 0; - } - - public (byte[], int) WrittenBuffer - { - get - { - this.CheckIfDisposed(); - - return (this.rentedBuffer, this.written); - } - } - - public Memory WrittenMemory - { - get - { - this.CheckIfDisposed(); - - return this.rentedBuffer.AsMemory(0, this.written); - } - } - - public Span WrittenSpan - { - get - { - this.CheckIfDisposed(); - - return this.rentedBuffer.AsSpan(0, this.written); - } - } - - public int BytesWritten - { - get - { - this.CheckIfDisposed(); - - return this.written; - } - } - - public long BytesCommitted - { - get - { - this.CheckIfDisposed(); - - return this.committed; - } - } - - public void Clear() - { - this.CheckIfDisposed(); - - this.ClearHelper(); - } - - private void ClearHelper() - { - this.rentedBuffer.AsSpan(0, this.written).Clear(); - this.written = 0; - } - - public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken = default) - { - this.CheckIfDisposed(); - - ArgumentNullException.ThrowIfNull(stream); - - await stream.WriteAsync(new Memory(this.rentedBuffer, 0, this.written), cancellationToken).ConfigureAwait(false); - this.committed += this.written; - - this.ClearHelper(); - } - - public void CopyTo(Stream stream) - { - this.CheckIfDisposed(); - - ArgumentNullException.ThrowIfNull(stream); - - stream.Write(this.rentedBuffer, 0, this.written); - this.committed += this.written; - - this.ClearHelper(); - } - - public void Advance(int count) - { - this.CheckIfDisposed(); - - ArgumentOutOfRangeException.ThrowIfLessThan(count, 0); - - if (this.written > this.rentedBuffer.Length - count) - { - throw new InvalidOperationException("Cannot advance past the end of the buffer."); - } - - this.written += count; - } - - // Returns the rented buffer back to the pool - public void Dispose() - { - if (this.rentedBuffer == null) - { - return; - } - - ArrayPool.Shared.Return(this.rentedBuffer, clearArray: true); - this.rentedBuffer = null; - this.written = 0; - } - - private void CheckIfDisposed() - { - ObjectDisposedException.ThrowIf(this.rentedBuffer == null, this); - } - - public Memory GetMemory(int sizeHint = 0) - { - this.CheckIfDisposed(); - - ArgumentOutOfRangeException.ThrowIfLessThan(sizeHint, 0); - - this.CheckAndResizeBuffer(sizeHint); - return this.rentedBuffer.AsMemory(this.written); - } - - public Span GetSpan(int sizeHint = 0) - { - this.CheckIfDisposed(); - - ArgumentOutOfRangeException.ThrowIfLessThan(sizeHint, 0); - - this.CheckAndResizeBuffer(sizeHint); - return this.rentedBuffer.AsSpan(this.written); - } - - private void CheckAndResizeBuffer(int sizeHint) - { - Debug.Assert(sizeHint >= 0); - - if (sizeHint == 0) - { - sizeHint = MinimumBufferSize; - } - - int availableSpace = this.rentedBuffer.Length - this.written; - - if (sizeHint > availableSpace) - { - int growBy = sizeHint > this.rentedBuffer.Length ? sizeHint : this.rentedBuffer.Length; - - int newSize = checked(this.rentedBuffer.Length + growBy); - - byte[] oldBuffer = this.rentedBuffer; - - this.rentedBuffer = ArrayPool.Shared.Rent(newSize); - - Debug.Assert(oldBuffer.Length >= this.written); - Debug.Assert(this.rentedBuffer.Length >= this.written); - - oldBuffer.AsSpan(0, this.written).CopyTo(this.rentedBuffer); - ArrayPool.Shared.Return(oldBuffer, clearArray: true); - } - - Debug.Assert(this.rentedBuffer.Length - this.written > 0); - Debug.Assert(this.rentedBuffer.Length - this.written >= sizeHint); - } -} -#endif \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs index a3980ae56a..3679a71908 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/src/Transformation/StreamProcessor.Encryptor.cs @@ -6,17 +6,21 @@ namespace Microsoft.Azure.Cosmos.Encryption.Custom.Transformation { using System; + using System.Buffers; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; + using Microsoft.IO; internal partial class StreamProcessor { private readonly byte[] encryptionPropertiesNameBytes = Encoding.UTF8.GetBytes(Constants.EncryptedInfo); + private readonly RecyclableMemoryStreamManager streamManager = new (); + internal async Task EncryptStreamAsync( Stream inputStream, Stream outputStream, @@ -51,7 +55,7 @@ internal async Task EncryptStreamAsync( Utf8JsonWriter encryptionPayloadWriter = null; string encryptPropertyName = null; - RentArrayBufferWriter bufferWriter = null; + RecyclableMemoryStream bufferWriter = null; while (!isFinalBlock) { @@ -112,8 +116,8 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) case JsonTokenType.StartObject: if (encryptPropertyName != null && encryptionPayloadWriter == null) { - bufferWriter = new RentArrayBufferWriter(); - encryptionPayloadWriter = new Utf8JsonWriter(bufferWriter); + bufferWriter = new RecyclableMemoryStream(this.streamManager); + encryptionPayloadWriter = new Utf8JsonWriter((IBufferWriter)bufferWriter); encryptionPayloadWriter.WriteStartObject(); } else @@ -132,16 +136,17 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) if (reader.CurrentDepth == 1 && encryptionPayloadWriter != null) { currentWriter.Flush(); - (byte[] bytes, int length) = bufferWriter.WrittenBuffer; + byte[] bytes = bufferWriter.GetBuffer(); + int length = (int)bufferWriter.Length; ReadOnlySpan encryptedBytes = TransformEncryptPayload(bytes, length, TypeMarker.Object); writer.WriteBase64StringValue(encryptedBytes); encryptPropertyName = null; #pragma warning disable VSTHRD103 // Call async methods when in an async method - this method cannot be async, Utf8JsonReader is ref struct encryptionPayloadWriter.Dispose(); + bufferWriter.Dispose(); #pragma warning restore VSTHRD103 // Call async methods when in an async method encryptionPayloadWriter = null; - bufferWriter.Dispose(); bufferWriter = null; } @@ -149,8 +154,8 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) case JsonTokenType.StartArray: if (encryptPropertyName != null && encryptionPayloadWriter == null) { - bufferWriter = new RentArrayBufferWriter(); - encryptionPayloadWriter = new Utf8JsonWriter(bufferWriter); + bufferWriter = new RecyclableMemoryStream(this.streamManager); + encryptionPayloadWriter = new Utf8JsonWriter((IBufferWriter)bufferWriter); encryptionPayloadWriter.WriteStartArray(); } else @@ -164,16 +169,17 @@ long TransformEncryptBuffer(ReadOnlySpan buffer) if (reader.CurrentDepth == 1 && encryptionPayloadWriter != null) { currentWriter.Flush(); - (byte[] bytes, int length) = bufferWriter.WrittenBuffer; + byte[] bytes = bufferWriter.GetBuffer(); + int length = (int)bufferWriter.Length; ReadOnlySpan encryptedBytes = TransformEncryptPayload(bytes, length, TypeMarker.Array); writer.WriteBase64StringValue(encryptedBytes); encryptPropertyName = null; #pragma warning disable VSTHRD103 // Call async methods when in an async method - this method cannot be async, Utf8JsonReader is ref struct encryptionPayloadWriter.Dispose(); + bufferWriter.Dispose(); #pragma warning restore VSTHRD103 // Call async methods when in an async method encryptionPayloadWriter = null; - bufferWriter.Dispose(); bufferWriter = null; } diff --git a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md index 691dbf0a5d..31a77807b2 100644 --- a/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md +++ b/Microsoft.Azure.Cosmos.Encryption.Custom/tests/Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests/Readme.md @@ -9,53 +9,53 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15 LaunchCount=2 WarmupCount=10 ``` -| Method | DocumentSizeInKb | CompressionAlgorithm | JsonProcessor | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | -|------------------------ |----------------- |--------------------- |--------------- |------------:|----------:|----------:|------------:|--------:|--------:|--------:|----------:| -| **Encrypt** | **1** | **None** | **Newtonsoft** | **22.53 μs** | **0.511 μs** | **0.733 μs** | **22.29 μs** | **0.1526** | **0.0305** | **-** | **41784 B** | -| EncryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 1 | None | Newtonsoft | 26.31 μs | 0.224 μs | 0.322 μs | 26.23 μs | 0.1526 | 0.0305 | - | 41440 B | -| DecryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **1** | **None** | **Stream** | **12.85 μs** | **0.095 μs** | **0.143 μs** | **12.84 μs** | **0.0610** | **0.0153** | **-** | **17528 B** | -| EncryptToProvidedStream | 1 | None | Stream | 13.00 μs | 0.096 μs | 0.141 μs | 12.98 μs | 0.0458 | 0.0153 | - | 11392 B | -| Decrypt | 1 | None | Stream | 13.01 μs | 0.152 μs | 0.228 μs | 13.05 μs | 0.0458 | 0.0153 | - | 12672 B | -| DecryptToProvidedStream | 1 | None | Stream | 13.48 μs | 0.132 μs | 0.197 μs | 13.45 μs | 0.0458 | 0.0153 | - | 11504 B | -| **Encrypt** | **1** | **Brotli** | **Newtonsoft** | **27.94 μs** | **0.226 μs** | **0.338 μs** | **27.96 μs** | **0.1526** | **0.0305** | **-** | **38064 B** | -| EncryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 1 | Brotli | Newtonsoft | 33.49 μs | 0.910 μs | 1.335 μs | 33.99 μs | 0.1221 | - | - | 41064 B | -| DecryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **1** | **Brotli** | **Stream** | **21.15 μs** | **1.037 μs** | **1.521 μs** | **20.52 μs** | **0.0610** | **0.0305** | **-** | **16584 B** | -| EncryptToProvidedStream | 1 | Brotli | Stream | 20.57 μs | 0.213 μs | 0.292 μs | 20.57 μs | 0.0305 | - | - | 11672 B | -| Decrypt | 1 | Brotli | Stream | 21.14 μs | 2.212 μs | 3.311 μs | 19.46 μs | 0.0305 | - | - | 13216 B | -| DecryptToProvidedStream | 1 | Brotli | Stream | 19.60 μs | 0.439 μs | 0.600 μs | 19.52 μs | 0.0305 | - | - | 12048 B | -| **Encrypt** | **10** | **None** | **Newtonsoft** | **84.82 μs** | **3.002 μs** | **4.208 μs** | **83.32 μs** | **0.6104** | **0.1221** | **-** | **170993 B** | -| EncryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 10 | None | Newtonsoft | 112.98 μs | 15.294 μs | 21.934 μs | 100.38 μs | 0.6104 | 0.1221 | - | 157425 B | -| DecryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **10** | **None** | **Stream** | **39.63 μs** | **0.658 μs** | **0.923 μs** | **39.41 μs** | **0.3052** | **0.0610** | **-** | **82928 B** | -| EncryptToProvidedStream | 10 | None | Stream | 36.59 μs | 0.272 μs | 0.399 μs | 36.57 μs | 0.1221 | - | - | 37048 B | -| Decrypt | 10 | None | Stream | 28.64 μs | 0.378 μs | 0.517 μs | 28.59 μs | 0.1221 | 0.0305 | - | 29520 B | -| DecryptToProvidedStream | 10 | None | Stream | 27.61 μs | 0.237 μs | 0.332 μs | 27.64 μs | 0.0610 | 0.0305 | - | 18416 B | -| **Encrypt** | **10** | **Brotli** | **Newtonsoft** | **115.28 μs** | **3.336 μs** | **4.677 μs** | **113.71 μs** | **0.6104** | **0.1221** | **-** | **168065 B** | -| EncryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 10 | Brotli | Newtonsoft | 118.98 μs | 1.530 μs | 2.195 μs | 118.76 μs | 0.4883 | - | - | 144849 B | -| DecryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **10** | **Brotli** | **Stream** | **90.10 μs** | **3.136 μs** | **4.693 μs** | **88.92 μs** | **0.2441** | **-** | **-** | **63809 B** | -| EncryptToProvidedStream | 10 | Brotli | Stream | 97.27 μs | 1.885 μs | 2.703 μs | 97.35 μs | 0.1221 | - | - | 32465 B | -| Decrypt | 10 | Brotli | Stream | 58.48 μs | 0.956 μs | 1.372 μs | 58.59 μs | 0.1221 | 0.0610 | - | 30064 B | -| DecryptToProvidedStream | 10 | Brotli | Stream | 59.12 μs | 1.160 μs | 1.664 μs | 59.14 μs | 0.0610 | - | - | 18960 B | -| **Encrypt** | **100** | **None** | **Newtonsoft** | **1,199.74 μs** | **42.805 μs** | **64.069 μs** | **1,206.48 μs** | **23.4375** | **21.4844** | **21.4844** | **1677978 B** | -| EncryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 100 | None | Newtonsoft | 1,177.48 μs | 25.746 μs | 38.535 μs | 1,172.04 μs | 17.5781 | 15.6250 | 15.6250 | 1260228 B | -| DecryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **100** | **None** | **Stream** | **636.72 μs** | **31.468 μs** | **47.099 μs** | **630.15 μs** | **16.6016** | **16.6016** | **16.6016** | **678066 B** | -| EncryptToProvidedStream | 100 | None | Stream | 383.33 μs | 7.441 μs | 10.671 μs | 384.69 μs | 4.3945 | 4.3945 | 4.3945 | 230133 B | -| Decrypt | 100 | None | Stream | 384.93 μs | 12.519 μs | 18.738 μs | 383.59 μs | 5.8594 | 5.8594 | 5.8594 | 230753 B | -| DecryptToProvidedStream | 100 | None | Stream | 295.19 μs | 7.094 μs | 10.618 μs | 296.11 μs | 3.4180 | 3.4180 | 3.4180 | 119116 B | -| **Encrypt** | **100** | **Brotli** | **Newtonsoft** | **1,178.06 μs** | **63.246 μs** | **94.664 μs** | **1,152.03 μs** | **13.6719** | **11.7188** | **9.7656** | **1379183 B** | -| EncryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| Decrypt | 100 | Brotli | Newtonsoft | 1,175.01 μs | 41.917 μs | 61.441 μs | 1,156.01 μs | 11.7188 | 9.7656 | 9.7656 | 1124274 B | -| DecryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | -| **Encrypt** | **100** | **Brotli** | **Stream** | **757.11 μs** | **19.549 μs** | **29.260 μs** | **754.55 μs** | **10.7422** | **10.7422** | **10.7422** | **479493 B** | -| EncryptToProvidedStream | 100 | Brotli | Stream | 563.46 μs | 9.960 μs | 14.284 μs | 561.60 μs | 2.9297 | 2.9297 | 2.9297 | 180637 B | -| Decrypt | 100 | Brotli | Stream | 542.34 μs | 14.514 μs | 21.724 μs | 542.04 μs | 6.8359 | 6.8359 | 6.8359 | 231162 B | -| DecryptToProvidedStream | 100 | Brotli | Stream | 463.69 μs | 9.130 μs | 12.800 μs | 460.71 μs | 3.4180 | 3.4180 | 3.4180 | 119506 B | +| Method | DocumentSizeInKb | CompressionAlgorithm | JsonProcessor | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated | +|------------------------ |----------------- |--------------------- |-------------- |------------:|----------:|----------:|------------:|--------:|--------:|--------:|----------:| +| **Encrypt** | **1** | **None** | **Newtonsoft** | **22.51 μs** | **0.393 μs** | **0.576 μs** | **22.63 μs** | **0.1526** | **0.0305** | **-** | **41784 B** | +| EncryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 1 | None | Newtonsoft | 27.10 μs | 0.124 μs | 0.174 μs | 27.07 μs | 0.1526 | 0.0305 | - | 41440 B | +| DecryptToProvidedStream | 1 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **1** | **None** | **Stream** | **12.82 μs** | **0.063 μs** | **0.091 μs** | **12.78 μs** | **0.0610** | **0.0153** | **-** | **17768 B** | +| EncryptToProvidedStream | 1 | None | Stream | 12.86 μs | 0.127 μs | 0.190 μs | 12.86 μs | 0.0458 | 0.0153 | - | 11632 B | +| Decrypt | 1 | None | Stream | 12.90 μs | 0.169 μs | 0.253 μs | 12.89 μs | 0.0458 | 0.0153 | - | 12672 B | +| DecryptToProvidedStream | 1 | None | Stream | 13.60 μs | 0.189 μs | 0.271 μs | 13.58 μs | 0.0458 | 0.0153 | - | 11504 B | +| **Encrypt** | **1** | **Brotli** | **Newtonsoft** | **28.87 μs** | **0.346 μs** | **0.474 μs** | **28.74 μs** | **0.1526** | **0.0305** | **-** | **38064 B** | +| EncryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 1 | Brotli | Newtonsoft | 35.28 μs | 0.905 μs | 1.269 μs | 35.40 μs | 0.1221 | - | - | 41064 B | +| DecryptToProvidedStream | 1 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **1** | **Brotli** | **Stream** | **21.52 μs** | **0.750 μs** | **1.026 μs** | **21.21 μs** | **0.0610** | **0.0305** | **-** | **16824 B** | +| EncryptToProvidedStream | 1 | Brotli | Stream | 20.80 μs | 0.228 μs | 0.312 μs | 20.76 μs | 0.0305 | - | - | 11912 B | +| Decrypt | 1 | Brotli | Stream | 19.55 μs | 0.443 μs | 0.636 μs | 19.33 μs | 0.0305 | - | - | 13216 B | +| DecryptToProvidedStream | 1 | Brotli | Stream | 19.86 μs | 0.192 μs | 0.270 μs | 19.82 μs | 0.0305 | - | - | 12048 B | +| **Encrypt** | **10** | **None** | **Newtonsoft** | **96.62 μs** | **10.278 μs** | **15.384 μs** | **86.34 μs** | **0.6104** | **0.1221** | **-** | **170993 B** | +| EncryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 10 | None | Newtonsoft | 106.98 μs | 3.407 μs | 5.100 μs | 104.40 μs | 0.6104 | 0.1221 | - | 157425 B | +| DecryptToProvidedStream | 10 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **10** | **None** | **Stream** | **39.15 μs** | **0.200 μs** | **0.281 μs** | **39.16 μs** | **0.3052** | **0.0610** | **-** | **83168 B** | +| EncryptToProvidedStream | 10 | None | Stream | 39.27 μs | 2.127 μs | 2.982 μs | 39.00 μs | 0.1221 | - | - | 37288 B | +| Decrypt | 10 | None | Stream | 28.94 μs | 0.369 μs | 0.518 μs | 28.91 μs | 0.0916 | 0.0305 | - | 29520 B | +| DecryptToProvidedStream | 10 | None | Stream | 27.56 μs | 0.167 μs | 0.235 μs | 27.54 μs | 0.0610 | 0.0305 | - | 18416 B | +| **Encrypt** | **10** | **Brotli** | **Newtonsoft** | **116.87 μs** | **0.707 μs** | **0.991 μs** | **116.89 μs** | **0.6104** | **0.1221** | **-** | **168065 B** | +| EncryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 10 | Brotli | Newtonsoft | 144.59 μs | 14.519 μs | 21.282 μs | 139.95 μs | 0.4883 | - | - | 144849 B | +| DecryptToProvidedStream | 10 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **10** | **Brotli** | **Stream** | **91.28 μs** | **3.359 μs** | **5.027 μs** | **89.89 μs** | **0.2441** | **-** | **-** | **64049 B** | +| EncryptToProvidedStream | 10 | Brotli | Stream | 98.61 μs | 1.831 μs | 2.741 μs | 99.15 μs | 0.1221 | - | - | 32705 B | +| Decrypt | 10 | Brotli | Stream | 60.11 μs | 1.366 μs | 2.044 μs | 59.71 μs | 0.1221 | 0.0610 | - | 30064 B | +| DecryptToProvidedStream | 10 | Brotli | Stream | 58.15 μs | 1.689 μs | 2.422 μs | 58.25 μs | - | - | - | 18960 B | +| **Encrypt** | **100** | **None** | **Newtonsoft** | **1,087.44 μs** | **15.865 μs** | **23.254 μs** | **1,085.47 μs** | **21.4844** | **19.5313** | **19.5313** | **1677999 B** | +| EncryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 100 | None | Newtonsoft | 1,124.12 μs | 15.278 μs | 22.395 μs | 1,123.48 μs | 17.5781 | 15.6250 | 15.6250 | 1260236 B | +| DecryptToProvidedStream | 100 | None | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **100** | **None** | **Stream** | **517.26 μs** | **7.106 μs** | **10.636 μs** | **520.35 μs** | **14.6484** | **14.6484** | **14.6484** | **678303 B** | +| EncryptToProvidedStream | 100 | None | Stream | 339.83 μs | 5.149 μs | 7.706 μs | 337.59 μs | 4.3945 | 4.3945 | 4.3945 | 230367 B | +| Decrypt | 100 | None | Stream | 346.38 μs | 10.316 μs | 15.440 μs | 343.34 μs | 6.3477 | 6.3477 | 6.3477 | 230757 B | +| DecryptToProvidedStream | 100 | None | Stream | 280.22 μs | 4.289 μs | 6.420 μs | 278.61 μs | 3.4180 | 3.4180 | 3.4180 | 119111 B | +| **Encrypt** | **100** | **Brotli** | **Newtonsoft** | **1,113.95 μs** | **15.209 μs** | **22.764 μs** | **1,103.81 μs** | **13.6719** | **9.7656** | **9.7656** | **1379180 B** | +| EncryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| Decrypt | 100 | Brotli | Newtonsoft | 1,138.03 μs | 8.340 μs | 12.224 μs | 1,137.53 μs | 11.7188 | 9.7656 | 9.7656 | 1124260 B | +| DecryptToProvidedStream | 100 | Brotli | Newtonsoft | NA | NA | NA | NA | - | - | - | - | +| **Encrypt** | **100** | **Brotli** | **Stream** | **723.60 μs** | **10.132 μs** | **15.165 μs** | **719.90 μs** | **11.7188** | **11.7188** | **11.7188** | **479748 B** | +| EncryptToProvidedStream | 100 | Brotli | Stream | 551.93 μs | 7.420 μs | 10.641 μs | 550.24 μs | 2.9297 | 2.9297 | 2.9297 | 180882 B | +| Decrypt | 100 | Brotli | Stream | 540.31 μs | 12.842 μs | 19.222 μs | 542.34 μs | 6.8359 | 6.8359 | 6.8359 | 231164 B | +| DecryptToProvidedStream | 100 | Brotli | Stream | 452.60 μs | 3.476 μs | 5.203 μs | 452.38 μs | 3.4180 | 3.4180 | 3.4180 | 119509 B |