File: src\libraries\Common\src\System\Security\Cryptography\CryptoPool.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers;
using System.Diagnostics;
 
namespace System.Security.Cryptography
{
    internal static class CryptoPool
    {
        internal const int ClearAll = -1;
 
        internal static byte[] Rent(int minimumLength) => ArrayPool<byte>.Shared.Rent(minimumLength);
 
        internal static void Return(ArraySegment<byte> arraySegment, int clearSize = ClearAll)
        {
            Debug.Assert(arraySegment.Array != null);
            Debug.Assert(arraySegment.Offset == 0);
 
            Return(arraySegment.Array, clearSize == ClearAll ? arraySegment.Count : clearSize);
        }
 
        internal static void Return(byte[] array, int clearSize = ClearAll)
        {
            Debug.Assert(clearSize <= array.Length);
            bool clearWholeArray = clearSize < 0;
 
            if (!clearWholeArray && clearSize != 0)
            {
#if (NET || NETSTANDARD2_1) && !CP_NO_ZEROMEMORY
                CryptographicOperations.ZeroMemory(array.AsSpan(0, clearSize));
#else
                Array.Clear(array, 0, clearSize);
#endif
            }
 
            ArrayPool<byte>.Shared.Return(array, clearWholeArray);
        }
    }
 
    internal ref struct CryptoPoolLease : IDisposable
    {
        private byte[]? _rented;
        private bool _skipClear;
 
        internal Span<byte> Span { get; private set; }
 
        internal readonly bool IsRented => _rented is not null;
 
        public void Dispose()
        {
            Return();
        }
 
        internal void Return()
        {
            Return(_skipClear ? 0 : Span.Length);
        }
 
        private void Return(int clearSize)
        {
            if (_rented is not null)
            {
                CryptoPool.Return(_rented, clearSize);
                _rented = null;
            }
            else if (!_skipClear && clearSize > 0)
            {
                Span<byte> toClear = Span.Slice(0, clearSize);
 
#if (NET || NETSTANDARD2_1) && !CP_NO_ZEROMEMORY
                CryptographicOperations.ZeroMemory(toClear);
#else
                toClear.Clear();
#endif
            }
 
            Span = default;
        }
 
        internal static CryptoPoolLease Rent(int length, bool skipClear = false)
        {
            byte[] rented = CryptoPool.Rent(length);
 
            return new CryptoPoolLease
            {
                _rented = rented,
                _skipClear = skipClear,
                Span = new Span<byte>(rented, 0, length)
            };
        }
 
        internal static CryptoPoolLease RentConditionally(
            int length,
            Span<byte> currentBuffer,
            bool skipClear = false,
            bool skipClearIfNotRented = false)
        {
            return RentConditionally(
                length,
                currentBuffer,
                out _,
                skipClear,
                skipClearIfNotRented);
        }
 
        internal static CryptoPoolLease RentConditionally(
            int length,
            Span<byte> currentBuffer,
            out bool rented,
            bool skipClear = false,
            bool skipClearIfNotRented = false)
        {
            if (currentBuffer.Length >= length)
            {
                rented = false;
 
                return new CryptoPoolLease
                {
                    _rented = null,
                    _skipClear = skipClearIfNotRented || skipClear,
                    Span = currentBuffer.Slice(0, length)
                };
            }
 
            rented = true;
            return Rent(length, skipClear);
        }
    }
}