Skip to content

Commit

Permalink
Added pooled connection library (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregyjames committed Aug 22, 2023
1 parent 788ffad commit 338bea6
Show file tree
Hide file tree
Showing 21 changed files with 9,483 additions and 1 deletion.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions OctaneEngine/.idea/.idea.OctaneEngine.dir/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions OctaneEngine/Collections.Pooled/BitHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.CompilerServices;

namespace Collections.Pooled
{
internal ref struct BitHelper
{
private const int IntSize = sizeof(int) * 8;
private readonly Span<int> _span;

internal BitHelper(Span<int> span, bool clear)
{
if (clear)
{
span.Clear();
}
_span = span;
}

internal void MarkBit(int bitPosition)
{
int bitArrayIndex = bitPosition / IntSize;
if ((uint)bitArrayIndex < (uint)_span.Length)
{
_span[bitArrayIndex] |= (1 << (bitPosition % IntSize));
}
}

internal bool IsMarked(int bitPosition)
{
int bitArrayIndex = bitPosition / IntSize;
return
(uint)bitArrayIndex < (uint)_span.Length &&
(_span[bitArrayIndex] & (1 << (bitPosition % IntSize))) != 0;
}

internal int FindFirstUnmarked(int startPosition = 0)
{
int i = startPosition;
for (int bi = i / IntSize; (uint)bi < (uint)_span.Length; bi = ++i / IntSize)
{
if ((_span[bi] & (1 << (i % IntSize))) == 0)
return i;
}
return -1;
}

internal int FindFirstMarked(int startPosition = 0)
{
int i = startPosition;
for (int bi = i / IntSize; (uint)bi < (uint)_span.Length; bi = ++i / IntSize)
{
if ((_span[bi] & (1 << (i % IntSize))) != 0)
return i;
}
return -1;
}

/// <summary>How many ints must be allocated to represent n bits. Returns (n+31)/32, but avoids overflow.</summary>
internal static int ToIntArrayLength(int n) => n > 0 ? ((n - 1) / IntSize + 1) : 0;
}
}
37 changes: 37 additions & 0 deletions OctaneEngine/Collections.Pooled/ClearMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Collections.Pooled
{
/// <summary>
/// This enum allows control over how data is treated when internal
/// arrays are returned to the ArrayPool. Be careful to understand
/// what each option does before using anything other than the default
/// of Auto.
/// </summary>
public enum ClearMode
{
/// <summary>
/// <para><code>Auto</code> has different behavior depending on the host project's target framework.</para>
/// <para>.NET Core 2.1: Reference types and value types that contain reference types are cleared
/// when the internal arrays are returned to the pool. Value types that do not contain reference
/// types are not cleared when returned to the pool.</para>
/// <para>.NET Standard 2.0: All user types are cleared before returning to the pool, in case they
/// contain reference types.
/// For .NET Standard, Auto and Always have the same behavior.</para>
/// </summary>
Auto = 0,
/// <summary>
/// The <para><code>Always</code> setting has the effect of always clearing user types before returning to the pool.
/// This is the default behavior on .NET Standard.</para><para>You might want to turn this on in a .NET Core project
/// if you were concerned about sensitive data stored in value types leaking to other pars of your application.</para>
/// </summary>
Always = 1,
/// <summary>
/// <para><code>Never</code> will cause pooled collections to never clear user types before returning them to the pool.</para>
/// <para>You might want to use this setting in a .NET Standard project when you know that a particular collection stores
/// only value types and you want the performance benefit of not taking time to reset array items to their default value.</para>
/// <para>Be careful with this setting: if used for a collection that contains reference types, or value types that contain
/// reference types, this setting could cause memory issues by making the garbage collector unable to clean up instances
/// that are still being referenced by arrays sitting in the ArrayPool.</para>
/// </summary>
Never = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo
// object until OnDeserialization is called.

using System.Threading;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace Collections.Pooled
{
internal static partial class HashHelpers
{
private static ConditionalWeakTable<object, SerializationInfo> s_serializationInfoTable;

public static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable
{
get
{
if (s_serializationInfoTable == null)
Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable<object, SerializationInfo>(), null);

return s_serializationInfoTable;
}
}
}
}
93 changes: 93 additions & 0 deletions OctaneEngine/Collections.Pooled/HashHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


using System;
using System.Diagnostics;

namespace Collections.Pooled
{
internal static partial class HashHelpers
{
public const int HashCollisionThreshold = 100;

// This is the maximum prime smaller than Array.MaxArrayLength
public const int MaxPrimeArrayLength = 0x7FEFFFFD;

public const int HashPrime = 101;

// Table of prime numbers to use as hash table sizes.
// A typical resize algorithm would pick the smallest prime number in this array
// that is larger than twice the previous capacity.
// Suppose our Hashtable currently has capacity x and enough elements are added
// such that a resize needs to occur. Resizing first computes 2x then finds the
// first prime in the table greater than 2x, i.e. if primes are ordered
// p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
// Doubling is important for preserving the asymptotic complexity of the
// hashtable operations such as add. Having a prime guarantees that double
// hashing does not lead to infinite loops. IE, your hash function will be
// h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
// We prefer the low computation costs of higher prime numbers over the increased
// memory allocation of a fixed prime number i.e. when right sizing a HashSet.
public static readonly int[] primes = {
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 };

public static bool IsPrime(int candidate)
{
if ((candidate & 1) != 0)
{
int limit = (int)Math.Sqrt(candidate);
for (int divisor = 3; divisor <= limit; divisor += 2)
{
if ((candidate % divisor) == 0)
return false;
}
return true;
}
return (candidate == 2);
}

public static int GetPrime(int min)
{
if (min < 0)
throw new ArgumentException("Cannot get the next prime from a negative number.");

for (int i = 0; i < primes.Length; i++)
{
int prime = primes[i];
if (prime >= min)
return prime;
}

//outside of our predefined table.
//compute the hard way.
for (int i = (min | 1); i < int.MaxValue; i += 2)
{
if (IsPrime(i) && ((i - 1) % HashPrime != 0))
return i;
}
return min;
}

// Returns size of hashtable to grow to.
public static int ExpandPrime(int oldSize)
{
int newSize = 2 * oldSize;

// Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
}

return GetPrime(newSize);
}
}
}
31 changes: 31 additions & 0 deletions OctaneEngine/Collections.Pooled/ICollectionDebugView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Collections.Pooled
{
internal sealed class ICollectionDebugView<T>
{
private readonly ICollection<T> _collection;

public ICollectionDebugView(ICollection<T> collection)
{
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items
{
get
{
T[] items = new T[_collection.Count];
_collection.CopyTo(items, 0);
return items;
}
}
}
}
73 changes: 73 additions & 0 deletions OctaneEngine/Collections.Pooled/IDictionaryDebugView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Collections.Pooled
{
internal sealed class IDictionaryDebugView<K, V>
{
private readonly IDictionary<K, V> _dict;

public IDictionaryDebugView(IDictionary<K, V> dictionary)
{
_dict = dictionary ?? throw new ArgumentNullException(nameof(dictionary));
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<K, V>[] Items
{
get
{
KeyValuePair<K, V>[] items = new KeyValuePair<K, V>[_dict.Count];
_dict.CopyTo(items, 0);
return items;
}
}
}

internal sealed class DictionaryKeyCollectionDebugView<TKey, TValue>
{
private readonly ICollection<TKey> _collection;

public DictionaryKeyCollectionDebugView(ICollection<TKey> collection)
{
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public TKey[] Items
{
get
{
TKey[] items = new TKey[_collection.Count];
_collection.CopyTo(items, 0);
return items;
}
}
}

internal sealed class DictionaryValueCollectionDebugView<TKey, TValue>
{
private readonly ICollection<TValue> _collection;

public DictionaryValueCollectionDebugView(ICollection<TValue> collection)
{
_collection = collection ?? throw new ArgumentNullException(nameof(collection));
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public TValue[] Items
{
get
{
TValue[] items = new TValue[_collection.Count];
_collection.CopyTo(items, 0);
return items;
}
}
}
}
18 changes: 18 additions & 0 deletions OctaneEngine/Collections.Pooled/IReadOnlyPooledList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;

namespace Collections.Pooled
{
/// <summary>
/// Represents a read-only collection of pooled elements that can be accessed by index
/// </summary>
/// <typeparam name="T">The type of elements in the read-only pooled list.</typeparam>

public interface IReadOnlyPooledList<T> : IReadOnlyList<T>
{
/// <summary>
/// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection.
/// </summary>
ReadOnlySpan<T> Span { get; }
}
}
Loading

0 comments on commit 338bea6

Please sign in to comment.