initial
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Stanislav Denisov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NetStack.Buffers {
|
||||
public abstract class ArrayPool<T> {
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
private static ArrayPool<T> s_sharedInstance = null;
|
||||
#else
|
||||
private static volatile ArrayPool<T> s_sharedInstance = null;
|
||||
#endif
|
||||
|
||||
public static ArrayPool<T> Shared {
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
[MethodImpl(256)]
|
||||
get {
|
||||
return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated();
|
||||
}
|
||||
#else
|
||||
[MethodImpl(256)]
|
||||
get {
|
||||
return s_sharedInstance ?? EnsureSharedCreated();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma warning disable 420
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ArrayPool<T> EnsureSharedCreated() {
|
||||
Interlocked.CompareExchange(ref s_sharedInstance, Create(), null);
|
||||
|
||||
return s_sharedInstance;
|
||||
}
|
||||
|
||||
public static ArrayPool<T> Create() {
|
||||
return new DefaultArrayPool<T>();
|
||||
}
|
||||
|
||||
public static ArrayPool<T> Create(int maxArrayLength, int maxArraysPerBucket) {
|
||||
return new DefaultArrayPool<T>(maxArrayLength, maxArraysPerBucket);
|
||||
}
|
||||
|
||||
public abstract T[] Rent(int minimumLength);
|
||||
|
||||
public abstract void Return(T[] array, bool clearArray = false);
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Stanislav Denisov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
using UnityEngine;
|
||||
#endif
|
||||
|
||||
namespace NetStack.Buffers {
|
||||
internal sealed class ArrayPoolEventSource {
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
internal static readonly ArrayPoolEventSource EventLog = new ArrayPoolEventSource();
|
||||
|
||||
internal enum BufferAllocatedReason : int {
|
||||
Pooled,
|
||||
OverMaximumSize,
|
||||
PoolExhausted
|
||||
}
|
||||
|
||||
internal void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) {
|
||||
var message = "Buffer allocated (Buffer ID: " + bufferId + ", Buffer size: " + bufferSize + ", Pool ID: " + poolId + ", Bucket ID: " + bucketId + ", Reason: " + reason + ")";
|
||||
|
||||
if (reason == BufferAllocatedReason.Pooled)
|
||||
Log.Info("Buffers", message);
|
||||
else
|
||||
Log.Warning("Buffers", message);
|
||||
}
|
||||
|
||||
internal void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) {
|
||||
Log.Info("Buffers", "Buffer rented (Buffer ID: " + bufferId + ", Buffer size: " + bufferSize + ", Pool ID: " + poolId + ", Bucket ID: " + bucketId + ")");
|
||||
}
|
||||
|
||||
internal void BufferReturned(int bufferId, int bufferSize, int poolId) {
|
||||
Log.Info("Buffers", "Buffer returned (Buffer ID: " + bufferId + ", Buffer size: " + bufferSize + ", Pool ID: " + poolId + ")");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static class Log {
|
||||
private static string Output(string module, string message) {
|
||||
return DateTime.Now.ToString("[HH:mm:ss]") + " [NetStack." + module + "] " + message;
|
||||
}
|
||||
|
||||
public static void Info(string module, string message) {
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
Debug.Log(Output(module, message));
|
||||
#else
|
||||
Console.WriteLine(Output(module, message));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Warning(string module, string message) {
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
Debug.LogWarning(Output(module, message));
|
||||
#else
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine(Output(module, message));
|
||||
Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Stanislav Denisov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace NetStack.Buffers {
|
||||
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T> {
|
||||
private const int DefaultMaxArrayLength = 1024 * 1024;
|
||||
private const int DefaultMaxNumberOfArraysPerBucket = 50;
|
||||
private static T[] s_emptyArray;
|
||||
private readonly Bucket[] _buckets;
|
||||
|
||||
internal DefaultArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) { }
|
||||
|
||||
internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket) {
|
||||
if (maxArrayLength <= 0)
|
||||
throw new ArgumentOutOfRangeException("maxArrayLength");
|
||||
|
||||
if (maxArraysPerBucket <= 0)
|
||||
throw new ArgumentOutOfRangeException("maxArraysPerBucket");
|
||||
|
||||
const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000;
|
||||
|
||||
if (maxArrayLength > MaximumArrayLength)
|
||||
maxArrayLength = MaximumArrayLength;
|
||||
else if (maxArrayLength < MinimumArrayLength)
|
||||
maxArrayLength = MinimumArrayLength;
|
||||
|
||||
int poolId = Id;
|
||||
int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength);
|
||||
var buckets = new Bucket[maxBuckets + 1];
|
||||
|
||||
for (int i = 0; i < buckets.Length; i++) {
|
||||
buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId);
|
||||
}
|
||||
|
||||
_buckets = buckets;
|
||||
}
|
||||
|
||||
private int Id {
|
||||
get {
|
||||
return GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public override T[] Rent(int minimumLength) {
|
||||
if (minimumLength < 0)
|
||||
throw new ArgumentOutOfRangeException("minimumLength");
|
||||
else if (minimumLength == 0)
|
||||
return s_emptyArray ?? (s_emptyArray = new T[0]);
|
||||
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
var log = ArrayPoolEventSource.EventLog;
|
||||
#endif
|
||||
|
||||
T[] buffer = null;
|
||||
int index = Utilities.SelectBucketIndex(minimumLength);
|
||||
|
||||
if (index < _buckets.Length) {
|
||||
const int MaxBucketsToTry = 2;
|
||||
|
||||
int i = index;
|
||||
|
||||
do {
|
||||
buffer = _buckets[i].Rent();
|
||||
|
||||
if (buffer != null) {
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id);
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
while (++i < _buckets.Length && i != index + MaxBucketsToTry);
|
||||
|
||||
buffer = new T[_buckets[index]._bufferLength];
|
||||
} else {
|
||||
buffer = new T[minimumLength];
|
||||
}
|
||||
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
int bufferId = buffer.GetHashCode(), bucketId = -1;
|
||||
|
||||
log.BufferRented(bufferId, buffer.Length, Id, bucketId);
|
||||
log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, index >= _buckets.Length ? ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted);
|
||||
#endif
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public override void Return(T[] array, bool clearArray = false) {
|
||||
if (array == null)
|
||||
throw new ArgumentNullException("array");
|
||||
else if (array.Length == 0)
|
||||
return;
|
||||
|
||||
int bucket = Utilities.SelectBucketIndex(array.Length);
|
||||
|
||||
if (bucket < _buckets.Length) {
|
||||
if (clearArray)
|
||||
Array.Clear(array, 0, array.Length);
|
||||
|
||||
_buckets[bucket].Return(array);
|
||||
}
|
||||
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
var log = ArrayPoolEventSource.EventLog;
|
||||
|
||||
log.BufferReturned(array.GetHashCode(), array.Length, Id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Stanislav Denisov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace NetStack.Buffers {
|
||||
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T> {
|
||||
private sealed class Bucket {
|
||||
internal readonly int _bufferLength;
|
||||
private readonly T[][] _buffers;
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
private readonly int _poolId;
|
||||
#endif
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
private SpinLock _lock;
|
||||
#else
|
||||
private object _lock;
|
||||
#endif
|
||||
private int _index;
|
||||
|
||||
internal Bucket(int bufferLength, int numberOfBuffers, int poolId) {
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
_lock = new SpinLock();
|
||||
#else
|
||||
_lock = new Object();
|
||||
#endif
|
||||
_buffers = new T[numberOfBuffers][];
|
||||
_bufferLength = bufferLength;
|
||||
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
_poolId = poolId;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
internal int Id => GetHashCode();
|
||||
#else
|
||||
internal int Id {
|
||||
get {
|
||||
return GetHashCode();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal T[] Rent() {
|
||||
T[][] buffers = _buffers;
|
||||
T[] buffer = null;
|
||||
bool allocateBuffer = false;
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
bool lockTaken = false;
|
||||
|
||||
try {
|
||||
_lock.Enter(ref lockTaken);
|
||||
|
||||
if (_index < buffers.Length) {
|
||||
buffer = buffers[_index];
|
||||
buffers[_index++] = null;
|
||||
allocateBuffer = buffer == null;
|
||||
}
|
||||
}
|
||||
|
||||
finally {
|
||||
if (lockTaken)
|
||||
_lock.Exit(false);
|
||||
}
|
||||
#else
|
||||
try {
|
||||
Monitor.Enter(_lock);
|
||||
|
||||
if (_index < buffers.Length) {
|
||||
buffer = buffers[_index];
|
||||
buffers[_index++] = null;
|
||||
allocateBuffer = buffer == null;
|
||||
}
|
||||
}
|
||||
|
||||
finally {
|
||||
Monitor.Exit(_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (allocateBuffer) {
|
||||
buffer = new T[_bufferLength];
|
||||
|
||||
#if NETSTACK_BUFFERS_LOG
|
||||
var log = ArrayPoolEventSource.EventLog;
|
||||
|
||||
log.BufferAllocated(buffer.GetHashCode(), _bufferLength, _poolId, Id, ArrayPoolEventSource.BufferAllocatedReason.Pooled);
|
||||
#endif
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
internal void Return(T[] array) {
|
||||
if (array.Length != _bufferLength)
|
||||
throw new ArgumentException("BufferNotFromPool", "array");
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
bool lockTaken = false;
|
||||
|
||||
try {
|
||||
_lock.Enter(ref lockTaken);
|
||||
|
||||
if (_index != 0)
|
||||
_buffers[--_index] = array;
|
||||
|
||||
}
|
||||
|
||||
finally {
|
||||
if (lockTaken)
|
||||
_lock.Exit(false);
|
||||
}
|
||||
#else
|
||||
try {
|
||||
Monitor.Enter(_lock);
|
||||
|
||||
if (_index != 0)
|
||||
_buffers[--_index] = array;
|
||||
}
|
||||
|
||||
finally {
|
||||
Monitor.Exit(_lock);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Stanislav Denisov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
using UnityEngine.Assertions;
|
||||
#endif
|
||||
|
||||
namespace NetStack.Buffers {
|
||||
internal static class Utilities {
|
||||
[MethodImpl(256)]
|
||||
internal static int SelectBucketIndex(int bufferSize) {
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
Assert.IsTrue(bufferSize > 0);
|
||||
#else
|
||||
Debug.Assert(bufferSize > 0);
|
||||
#endif
|
||||
|
||||
uint bitsRemaining = ((uint)bufferSize - 1) >> 4;
|
||||
int poolIndex = 0;
|
||||
|
||||
if (bitsRemaining > 0xFFFF) {
|
||||
bitsRemaining >>= 16;
|
||||
poolIndex = 16;
|
||||
}
|
||||
|
||||
if (bitsRemaining > 0xFF) {
|
||||
bitsRemaining >>= 8;
|
||||
poolIndex += 8;
|
||||
}
|
||||
|
||||
if (bitsRemaining > 0xF) {
|
||||
bitsRemaining >>= 4;
|
||||
poolIndex += 4;
|
||||
}
|
||||
|
||||
if (bitsRemaining > 0x3) {
|
||||
bitsRemaining >>= 2;
|
||||
poolIndex += 2;
|
||||
}
|
||||
|
||||
if (bitsRemaining > 0x1) {
|
||||
bitsRemaining >>= 1;
|
||||
poolIndex += 1;
|
||||
}
|
||||
|
||||
return poolIndex + (int)bitsRemaining;
|
||||
}
|
||||
|
||||
[MethodImpl(256)]
|
||||
internal static int GetMaxSizeForBucket(int binIndex) {
|
||||
int maxSize = 16 << binIndex;
|
||||
|
||||
#if ENABLE_MONO || ENABLE_IL2CPP
|
||||
Assert.IsTrue(maxSize >= 0);
|
||||
#else
|
||||
Debug.Assert(maxSize >= 0);
|
||||
#endif
|
||||
|
||||
return maxSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user