Normalize all existing files to LF
This commit is contained in:
@@ -1,194 +1,194 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BidirectionalDictionary"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond>
|
||||
where TFirst: notnull
|
||||
where TSecond: notnull
|
||||
{
|
||||
|
||||
private readonly Dictionary<TFirst, TSecond> _forward;
|
||||
private readonly Dictionary<TSecond, TFirst> _reverse;
|
||||
|
||||
public BidirectionalDictionary()
|
||||
{
|
||||
_forward = [];
|
||||
_reverse = [];
|
||||
}
|
||||
|
||||
public TFirst this[TSecond key]
|
||||
{
|
||||
get => _reverse.TryGetValue(key, out TFirst value) ?
|
||||
value :
|
||||
throw new ArgumentException($"Cannot find key {key} in dictionary.");
|
||||
set
|
||||
{
|
||||
if (_reverse.TryGetValue(key, out TFirst origin))
|
||||
{
|
||||
_reverse[key] = value;
|
||||
_ = _forward.Remove(origin);
|
||||
} else
|
||||
{
|
||||
_reverse.Add(key, value);
|
||||
}
|
||||
_forward.Add(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
public TSecond this[TFirst key]
|
||||
{
|
||||
get => _forward.TryGetValue(key, out TSecond value) ?
|
||||
value :
|
||||
throw new ArgumentException($"Cannot find key {key} in dictionary.");
|
||||
set
|
||||
{
|
||||
if (_forward.TryGetValue(key, out TSecond origin))
|
||||
{
|
||||
_forward[key] = value;
|
||||
_ = _reverse.Remove(origin);
|
||||
} else
|
||||
{
|
||||
_forward.Add(key, value);
|
||||
}
|
||||
_reverse.Add(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public ICollection<TFirst> FirstIndex => _forward.Keys;
|
||||
|
||||
public ICollection<TSecond> SecondIndex => _reverse.Keys;
|
||||
|
||||
public int Count => _forward.Count;
|
||||
|
||||
public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>(
|
||||
_forward);
|
||||
|
||||
public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>(
|
||||
_reverse);
|
||||
|
||||
public void Add(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) {
|
||||
throw new ArgumentException("Duplicate key or value.");
|
||||
}
|
||||
_forward.Add(item.Key, item.Value);
|
||||
_reverse.Add(item.Value, item.Key);
|
||||
}
|
||||
|
||||
public void Add(TFirst key, TSecond value)
|
||||
{
|
||||
if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) {
|
||||
throw new ArgumentException("Duplicate key or value.");
|
||||
}
|
||||
_forward.Add(key, value);
|
||||
_reverse.Add(value, key);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_forward.Clear();
|
||||
_reverse.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key);
|
||||
}
|
||||
|
||||
public bool Contains(TSecond key)
|
||||
{
|
||||
return _reverse.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Contains(TFirst key)
|
||||
{
|
||||
return _forward.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null) {
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length) {
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
}
|
||||
if (array.Length - arrayIndex < _forward.Count) {
|
||||
throw new ArgumentException("The array is too small to copy the elements.");
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
|
||||
array[arrayIndex++] = pair;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator()
|
||||
{
|
||||
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
|
||||
yield return pair;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TFirst key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
if (_forward.Remove(item.Key)) {
|
||||
return _reverse.Remove(item.Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TFirst key, out TSecond value)
|
||||
{
|
||||
return _forward.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TSecond key, out TFirst value)
|
||||
{
|
||||
return _reverse.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BidirectionalDictionary"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public sealed class BidirectionalDictionary<TFirst, TSecond> : IBidirectionalDictionary<TFirst, TSecond>
|
||||
where TFirst: notnull
|
||||
where TSecond: notnull
|
||||
{
|
||||
|
||||
private readonly Dictionary<TFirst, TSecond> _forward;
|
||||
private readonly Dictionary<TSecond, TFirst> _reverse;
|
||||
|
||||
public BidirectionalDictionary()
|
||||
{
|
||||
_forward = [];
|
||||
_reverse = [];
|
||||
}
|
||||
|
||||
public TFirst this[TSecond key]
|
||||
{
|
||||
get => _reverse.TryGetValue(key, out TFirst value) ?
|
||||
value :
|
||||
throw new ArgumentException($"Cannot find key {key} in dictionary.");
|
||||
set
|
||||
{
|
||||
if (_reverse.TryGetValue(key, out TFirst origin))
|
||||
{
|
||||
_reverse[key] = value;
|
||||
_ = _forward.Remove(origin);
|
||||
} else
|
||||
{
|
||||
_reverse.Add(key, value);
|
||||
}
|
||||
_forward.Add(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
public TSecond this[TFirst key]
|
||||
{
|
||||
get => _forward.TryGetValue(key, out TSecond value) ?
|
||||
value :
|
||||
throw new ArgumentException($"Cannot find key {key} in dictionary.");
|
||||
set
|
||||
{
|
||||
if (_forward.TryGetValue(key, out TSecond origin))
|
||||
{
|
||||
_forward[key] = value;
|
||||
_ = _reverse.Remove(origin);
|
||||
} else
|
||||
{
|
||||
_forward.Add(key, value);
|
||||
}
|
||||
_reverse.Add(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public ICollection<TFirst> FirstIndex => _forward.Keys;
|
||||
|
||||
public ICollection<TSecond> SecondIndex => _reverse.Keys;
|
||||
|
||||
public int Count => _forward.Count;
|
||||
|
||||
public IReadOnlyDictionary<TFirst, TSecond> Forward => new ReadOnlyDictionary<TFirst, TSecond>(
|
||||
_forward);
|
||||
|
||||
public IReadOnlyDictionary<TSecond, TFirst> Reverse => new ReadOnlyDictionary<TSecond, TFirst>(
|
||||
_reverse);
|
||||
|
||||
public void Add(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
if (_forward.ContainsKey(item.Key) || _reverse.ContainsKey(item.Value)) {
|
||||
throw new ArgumentException("Duplicate key or value.");
|
||||
}
|
||||
_forward.Add(item.Key, item.Value);
|
||||
_reverse.Add(item.Value, item.Key);
|
||||
}
|
||||
|
||||
public void Add(TFirst key, TSecond value)
|
||||
{
|
||||
if (_forward.ContainsKey(key) || _reverse.ContainsKey(value)) {
|
||||
throw new ArgumentException("Duplicate key or value.");
|
||||
}
|
||||
_forward.Add(key, value);
|
||||
_reverse.Add(value, key);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_forward.Clear();
|
||||
_reverse.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
return _forward.Contains(item) && _reverse.TryGetValue(item.Value, out TFirst var) && var!.Equals(item.Key);
|
||||
}
|
||||
|
||||
public bool Contains(TSecond key)
|
||||
{
|
||||
return _reverse.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Contains(TFirst key)
|
||||
{
|
||||
return _forward.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TFirst, TSecond>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null) {
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length) {
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
|
||||
}
|
||||
if (array.Length - arrayIndex < _forward.Count) {
|
||||
throw new ArgumentException("The array is too small to copy the elements.");
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
|
||||
array[arrayIndex++] = pair;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TFirst, TSecond>> GetEnumerator()
|
||||
{
|
||||
foreach (KeyValuePair<TFirst, TSecond> pair in _forward) {
|
||||
yield return pair;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TFirst key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TFirst, TSecond> item)
|
||||
{
|
||||
if (_forward.Remove(item.Key)) {
|
||||
return _reverse.Remove(item.Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TFirst key, out TSecond value)
|
||||
{
|
||||
return _forward.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TSecond key, out TFirst value)
|
||||
{
|
||||
return _reverse.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,92 +1,92 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedBag"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public sealed class CapacityFixedBag<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly T[] _buffer;
|
||||
|
||||
public CapacityFixedBag(int capacity)
|
||||
{
|
||||
_buffer = new T[capacity];
|
||||
}
|
||||
|
||||
public int Count { get; set; }
|
||||
|
||||
public int Capacity => _buffer.Length;
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count >= _buffer.Length) {
|
||||
throw new InvalidOperationException("Bag capacity exceeded.");
|
||||
}
|
||||
_buffer[Count] = item;
|
||||
Count++;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < Count; i++) {
|
||||
yield return _buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(T item)
|
||||
{
|
||||
int index = Array.IndexOf(_buffer, item, 0, Count);
|
||||
if (index >= 0)
|
||||
{
|
||||
_buffer[index] = _buffer[Count - 1];
|
||||
_buffer[Count - 1] = default!;
|
||||
Count--;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index >= 0)
|
||||
{
|
||||
_buffer[index] = _buffer[Count - 1];
|
||||
_buffer[Count - 1] = default!;
|
||||
Count--;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedBag"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public sealed class CapacityFixedBag<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly T[] _buffer;
|
||||
|
||||
public CapacityFixedBag(int capacity)
|
||||
{
|
||||
_buffer = new T[capacity];
|
||||
}
|
||||
|
||||
public int Count { get; set; }
|
||||
|
||||
public int Capacity => _buffer.Length;
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count >= _buffer.Length) {
|
||||
throw new InvalidOperationException("Bag capacity exceeded.");
|
||||
}
|
||||
_buffer[Count] = item;
|
||||
Count++;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < Count; i++) {
|
||||
yield return _buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(T item)
|
||||
{
|
||||
int index = Array.IndexOf(_buffer, item, 0, Count);
|
||||
if (index >= 0)
|
||||
{
|
||||
_buffer[index] = _buffer[Count - 1];
|
||||
_buffer[Count - 1] = default!;
|
||||
Count--;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index >= 0)
|
||||
{
|
||||
_buffer[index] = _buffer[Count - 1];
|
||||
_buffer[Count - 1] = default!;
|
||||
Count--;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,176 +1,176 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedList"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public static class CapacityFixedListBuilder
|
||||
{
|
||||
|
||||
internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
return new CapacityFixedList<T>(values);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))]
|
||||
public class CapacityFixedList<T> : IList<T>
|
||||
{
|
||||
|
||||
private T[] _values;
|
||||
private readonly int _capacity;
|
||||
private int _index;
|
||||
|
||||
public CapacityFixedList(int size)
|
||||
{
|
||||
_values = new T[size];
|
||||
_capacity = size;
|
||||
}
|
||||
|
||||
public CapacityFixedList(ReadOnlySpan<T> values)
|
||||
{
|
||||
_values = values.ToArray();
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index));
|
||||
set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public int Count => _index + 1;
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (_index < _capacity - 1)
|
||||
{
|
||||
_index++;
|
||||
this[_index] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_values = new T[_capacity];
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if (item.Equals(_values[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
_values.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i <= _index; i++) {
|
||||
yield return _values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
T v;
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if ((v = _values[i]) != null && v.Equals(item)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
T v;
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if ((v = _values[i]) != null && v.Equals(item))
|
||||
{
|
||||
_values[i] = _values[_index];
|
||||
_index--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RemoveAll(Predicate<T> predicate)
|
||||
{
|
||||
for (int i = _index; i > 0; i--)
|
||||
{
|
||||
if (predicate(_values[i]))
|
||||
{
|
||||
_values[i] = _values[_index];
|
||||
_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index <= _index)
|
||||
{
|
||||
_values[index] = _values[_index];
|
||||
_index--;
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _values, newSize);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
yield return GetEnumerator();
|
||||
}
|
||||
|
||||
void IList<T>.Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
}//------------------------------------- End of Class ------------------------------------------
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedList"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public static class CapacityFixedListBuilder
|
||||
{
|
||||
|
||||
internal static CapacityFixedList<T> Create<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
return new CapacityFixedList<T>(values);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CollectionBuilder(typeof(CapacityFixedListBuilder), nameof(CapacityFixedListBuilder.Create))]
|
||||
public class CapacityFixedList<T> : IList<T>
|
||||
{
|
||||
|
||||
private T[] _values;
|
||||
private readonly int _capacity;
|
||||
private int _index;
|
||||
|
||||
public CapacityFixedList(int size)
|
||||
{
|
||||
_values = new T[size];
|
||||
_capacity = size;
|
||||
}
|
||||
|
||||
public CapacityFixedList(ReadOnlySpan<T> values)
|
||||
{
|
||||
_values = values.ToArray();
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => index < _index ? _values[index] : throw new ArgumentOutOfRangeException(nameof(index));
|
||||
set => _values[index] = index < _index ? value : throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public int Count => _index + 1;
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
if (_index < _capacity - 1)
|
||||
{
|
||||
_index++;
|
||||
this[_index] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_values = new T[_capacity];
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if (item.Equals(_values[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
_values.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i <= _index; i++) {
|
||||
yield return _values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
T v;
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if ((v = _values[i]) != null && v.Equals(item)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
T v;
|
||||
for (int i = 0; i <= _index; i++)
|
||||
{
|
||||
if ((v = _values[i]) != null && v.Equals(item))
|
||||
{
|
||||
_values[i] = _values[_index];
|
||||
_index--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RemoveAll(Predicate<T> predicate)
|
||||
{
|
||||
for (int i = _index; i > 0; i--)
|
||||
{
|
||||
if (predicate(_values[i]))
|
||||
{
|
||||
_values[i] = _values[_index];
|
||||
_index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index <= _index)
|
||||
{
|
||||
_values[index] = _values[_index];
|
||||
_index--;
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _values, newSize);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
yield return GetEnumerator();
|
||||
}
|
||||
|
||||
void IList<T>.Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
}//------------------------------------- End of Class ------------------------------------------
|
||||
}
|
||||
|
||||
@@ -1,174 +1,174 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
[CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))]
|
||||
public class CapacityFixedStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly T[] _values;
|
||||
|
||||
// _index points to the next free slot (top empty position). Therefore Count == _index.
|
||||
private int _index;
|
||||
|
||||
public CapacityFixedStack(int size)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(size);
|
||||
_values = new T[size];
|
||||
_index = 0; // empty stack
|
||||
}
|
||||
|
||||
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
|
||||
|
||||
public bool IsEmpty => Volatile.Read(ref _index) == 0;
|
||||
|
||||
public int Count => Volatile.Read(ref _index);
|
||||
|
||||
public int Capacity => _values.Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<T> AsSpan()
|
||||
{
|
||||
return _values.AsSpan(0, _index);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_index > 0)
|
||||
{
|
||||
Array.Clear(_values, 0, _index);
|
||||
_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = _index - 1; i >= 0; i--) {
|
||||
yield return _values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (_index == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
return _values[_index - 1];
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
if (_index == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
_index--;
|
||||
T item = _values[_index];
|
||||
_values[_index] = default!; // Clear reference for GC
|
||||
return item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T[] PopRange(int count)
|
||||
{
|
||||
if (count <= 0 || _index == 0) {
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
int n = Math.Min(count, _index);
|
||||
int start = _index - n;
|
||||
T[] ret = new T[n];
|
||||
Array.Copy(_values, start, ret, 0, n);
|
||||
Array.Clear(_values, start, n);
|
||||
_index -= n;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
if (_index >= _values.Length) {
|
||||
throw new InvalidOperationException("Stack is full.");
|
||||
}
|
||||
_values[_index] = item;
|
||||
_index++;
|
||||
}
|
||||
|
||||
public void PushRange(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.Length == 0) {
|
||||
return;
|
||||
}
|
||||
if (_index + span.Length > _values.Length) {
|
||||
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
|
||||
}
|
||||
span.CopyTo(_values.AsSpan(_index, span.Length));
|
||||
_index += span.Length;
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
if (_index == 0)
|
||||
{
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
_index--;
|
||||
value = _values[_index];
|
||||
_values[_index] = default!;
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Span<T> GetValueLayout()
|
||||
{
|
||||
return _values.AsSpan();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CapacityFixedStackBuilder
|
||||
{
|
||||
|
||||
public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
CapacityFixedStack<T> stack = new(values.Length);
|
||||
stack.PushRange(values); // Push in order
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CapacityFixedStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
[CollectionBuilder(typeof(CapacityFixedStackBuilder), nameof(CapacityFixedStackBuilder.Create))]
|
||||
public class CapacityFixedStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly T[] _values;
|
||||
|
||||
// _index points to the next free slot (top empty position). Therefore Count == _index.
|
||||
private int _index;
|
||||
|
||||
public CapacityFixedStack(int size)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(size);
|
||||
_values = new T[size];
|
||||
_index = 0; // empty stack
|
||||
}
|
||||
|
||||
public T? First => Volatile.Read(ref _index) > 0 ? _values[0] : default;
|
||||
|
||||
public bool IsEmpty => Volatile.Read(ref _index) == 0;
|
||||
|
||||
public int Count => Volatile.Read(ref _index);
|
||||
|
||||
public int Capacity => _values.Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<T> AsSpan()
|
||||
{
|
||||
return _values.AsSpan(0, _index);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_index > 0)
|
||||
{
|
||||
Array.Clear(_values, 0, _index);
|
||||
_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = _index - 1; i >= 0; i--) {
|
||||
yield return _values[i];
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (_index == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
return _values[_index - 1];
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
if (_index == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
_index--;
|
||||
T item = _values[_index];
|
||||
_values[_index] = default!; // Clear reference for GC
|
||||
return item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T[] PopRange(int count)
|
||||
{
|
||||
if (count <= 0 || _index == 0) {
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
int n = Math.Min(count, _index);
|
||||
int start = _index - n;
|
||||
T[] ret = new T[n];
|
||||
Array.Copy(_values, start, ret, 0, n);
|
||||
Array.Clear(_values, start, n);
|
||||
_index -= n;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
if (_index >= _values.Length) {
|
||||
throw new InvalidOperationException("Stack is full.");
|
||||
}
|
||||
_values[_index] = item;
|
||||
_index++;
|
||||
}
|
||||
|
||||
public void PushRange(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.Length == 0) {
|
||||
return;
|
||||
}
|
||||
if (_index + span.Length > _values.Length) {
|
||||
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
|
||||
}
|
||||
span.CopyTo(_values.AsSpan(_index, span.Length));
|
||||
_index += span.Length;
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
if (_index == 0)
|
||||
{
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
_index--;
|
||||
value = _values[_index];
|
||||
_values[_index] = default!;
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Span<T> GetValueLayout()
|
||||
{
|
||||
return _values.AsSpan();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class CapacityFixedStackBuilder
|
||||
{
|
||||
|
||||
public static CapacityFixedStack<T> Create<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
CapacityFixedStack<T> stack = new(values.Length);
|
||||
stack.PushRange(values); // Push in order
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,287 +1,287 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ConcurrentCapacityFixedStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
|
||||
/// operations. - No linked list; only array slots - Uses CAS on a shared top index to
|
||||
/// reserve/release slots - Per-slot state flag to safely publish/consume values under races
|
||||
/// </summary>
|
||||
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly int[] _states; // 0 = empty, 1 = full
|
||||
private readonly T[] _values;
|
||||
private readonly int _capacity;
|
||||
|
||||
// Number of full slots (observable count)
|
||||
private int _count;
|
||||
|
||||
// Next push position (0.._capacity). Only modified via CAS.
|
||||
private int _top;
|
||||
|
||||
public ConcurrentCapacityFixedStack(int capacity)
|
||||
{
|
||||
if (capacity <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
_values = new T[capacity];
|
||||
_states = new int[capacity];
|
||||
_top = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public bool IsEmpty => Count == 0;
|
||||
|
||||
public int Capacity => _capacity;
|
||||
|
||||
public int Count => Volatile.Read(ref _count);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
// Pop all elements to return slots to empty state
|
||||
while (TryPop(out _)) { }
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
T[] snap = ToArraySnapshot();
|
||||
for (int i = 0; i < snap.Length; i++) {
|
||||
yield return snap[i];
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (TryPeek(out T? v)) {
|
||||
return v;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
if (TryPop(out T value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
public T[] PopRange(int count)
|
||||
{
|
||||
if (count <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
T[] result = new T[count];
|
||||
int i = 0;
|
||||
for (; i < count; i++)
|
||||
{
|
||||
if (!TryPop(out result[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == count) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
T[] trimmed = new T[i];
|
||||
Array.Copy(result, trimmed, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
SpinWait sw = default;
|
||||
while (true)
|
||||
{
|
||||
int oldTop = Volatile.Read(ref _top);
|
||||
if (oldTop >= _capacity) {
|
||||
throw new InvalidOperationException("Stack is full.");
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
|
||||
{
|
||||
sw.SpinOnce();
|
||||
continue; // failed to reserve slot
|
||||
}
|
||||
|
||||
// We exclusively own slot oldTop. Publish value then state.
|
||||
_values[oldTop] = item;
|
||||
Volatile.Write(ref _states[oldTop], 1); // publish
|
||||
_ = Interlocked.Increment(ref _count);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void PushRange(ReadOnlySpan<T> span)
|
||||
{
|
||||
for (int i = 0; i < span.Length; i++) {
|
||||
Push(span[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
|
||||
/// mutations.
|
||||
/// </summary>
|
||||
public T[] ToArraySnapshot()
|
||||
{
|
||||
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
|
||||
if (expected == 0) {
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
T[] snap = new T[expected];
|
||||
int copied = 0;
|
||||
int idx = Volatile.Read(ref _top) - 1;
|
||||
while (idx >= 0 && copied < expected)
|
||||
{
|
||||
if (Volatile.Read(ref _states[idx]) == 1) {
|
||||
snap[copied++] = _values[idx];
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (copied == snap.Length) {
|
||||
return snap;
|
||||
}
|
||||
|
||||
T[] trimmed = new T[copied];
|
||||
Array.Copy(snap, trimmed, copied);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public bool TryPeek(out T value)
|
||||
{
|
||||
value = default!;
|
||||
SpinWait sw = default;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int top = Volatile.Read(ref _top);
|
||||
if (top <= 0) {
|
||||
return false;
|
||||
}
|
||||
int idx = top - 1;
|
||||
|
||||
// If not yet published or racing, retry/spin
|
||||
if (Volatile.Read(ref _states[idx]) == 0)
|
||||
{
|
||||
if (Volatile.Read(ref _top) != top)
|
||||
{
|
||||
continue; // head moved, retry
|
||||
}
|
||||
sw.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
|
||||
T v = _values[idx];
|
||||
|
||||
// Validate consistency
|
||||
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
|
||||
{
|
||||
value = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
value = default!;
|
||||
SpinWait sw = default;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int oldTop = Volatile.Read(ref _top);
|
||||
if (oldTop <= 0)
|
||||
{
|
||||
return false; // empty
|
||||
}
|
||||
|
||||
int newTop = oldTop - 1; // index to consume
|
||||
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
|
||||
{
|
||||
sw.SpinOnce();
|
||||
continue; // failed to reserve element
|
||||
}
|
||||
|
||||
// If producer hasn't published yet, roll back our reservation and retry.
|
||||
if (Volatile.Read(ref _states[newTop]) == 0)
|
||||
{
|
||||
// attempt to restore top; ok if it fails due to concurrent operations
|
||||
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
|
||||
sw.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
|
||||
value = _values[newTop];
|
||||
_values[newTop] = default!; // avoid ref retention
|
||||
Volatile.Write(ref _states[newTop], 0); // mark empty
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int TryPopRange(Span<T> destination)
|
||||
{
|
||||
if (destination.IsEmpty) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (; i < destination.Length; i++)
|
||||
{
|
||||
if (!TryPop(out destination[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ConcurrentCapacityFixedStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Lock-free, fixed-capacity stack implemented on top of an array using atomic index
|
||||
/// operations. - No linked list; only array slots - Uses CAS on a shared top index to
|
||||
/// reserve/release slots - Per-slot state flag to safely publish/consume values under races
|
||||
/// </summary>
|
||||
public class ConcurrentCapacityFixedStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private readonly int[] _states; // 0 = empty, 1 = full
|
||||
private readonly T[] _values;
|
||||
private readonly int _capacity;
|
||||
|
||||
// Number of full slots (observable count)
|
||||
private int _count;
|
||||
|
||||
// Next push position (0.._capacity). Only modified via CAS.
|
||||
private int _top;
|
||||
|
||||
public ConcurrentCapacityFixedStack(int capacity)
|
||||
{
|
||||
if (capacity <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
_values = new T[capacity];
|
||||
_states = new int[capacity];
|
||||
_top = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public bool IsEmpty => Count == 0;
|
||||
|
||||
public int Capacity => _capacity;
|
||||
|
||||
public int Count => Volatile.Read(ref _count);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
// Pop all elements to return slots to empty state
|
||||
while (TryPop(out _)) { }
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
T[] snap = ToArraySnapshot();
|
||||
for (int i = 0; i < snap.Length; i++) {
|
||||
yield return snap[i];
|
||||
}
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (TryPeek(out T? v)) {
|
||||
return v;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
if (TryPop(out T value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
public T[] PopRange(int count)
|
||||
{
|
||||
if (count <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
T[] result = new T[count];
|
||||
int i = 0;
|
||||
for (; i < count; i++)
|
||||
{
|
||||
if (!TryPop(out result[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == count) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
T[] trimmed = new T[i];
|
||||
Array.Copy(result, trimmed, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
SpinWait sw = default;
|
||||
while (true)
|
||||
{
|
||||
int oldTop = Volatile.Read(ref _top);
|
||||
if (oldTop >= _capacity) {
|
||||
throw new InvalidOperationException("Stack is full.");
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _top, oldTop + 1, oldTop) != oldTop)
|
||||
{
|
||||
sw.SpinOnce();
|
||||
continue; // failed to reserve slot
|
||||
}
|
||||
|
||||
// We exclusively own slot oldTop. Publish value then state.
|
||||
_values[oldTop] = item;
|
||||
Volatile.Write(ref _states[oldTop], 1); // publish
|
||||
_ = Interlocked.Increment(ref _count);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void PushRange(ReadOnlySpan<T> span)
|
||||
{
|
||||
for (int i = 0; i < span.Length; i++) {
|
||||
Push(span[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort snapshot for enumeration; not strongly consistent under concurrent
|
||||
/// mutations.
|
||||
/// </summary>
|
||||
public T[] ToArraySnapshot()
|
||||
{
|
||||
int expected = Math.Min(Volatile.Read(ref _count), _capacity);
|
||||
if (expected == 0) {
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
T[] snap = new T[expected];
|
||||
int copied = 0;
|
||||
int idx = Volatile.Read(ref _top) - 1;
|
||||
while (idx >= 0 && copied < expected)
|
||||
{
|
||||
if (Volatile.Read(ref _states[idx]) == 1) {
|
||||
snap[copied++] = _values[idx];
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (copied == snap.Length) {
|
||||
return snap;
|
||||
}
|
||||
|
||||
T[] trimmed = new T[copied];
|
||||
Array.Copy(snap, trimmed, copied);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public bool TryPeek(out T value)
|
||||
{
|
||||
value = default!;
|
||||
SpinWait sw = default;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int top = Volatile.Read(ref _top);
|
||||
if (top <= 0) {
|
||||
return false;
|
||||
}
|
||||
int idx = top - 1;
|
||||
|
||||
// If not yet published or racing, retry/spin
|
||||
if (Volatile.Read(ref _states[idx]) == 0)
|
||||
{
|
||||
if (Volatile.Read(ref _top) != top)
|
||||
{
|
||||
continue; // head moved, retry
|
||||
}
|
||||
sw.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
|
||||
T v = _values[idx];
|
||||
|
||||
// Validate consistency
|
||||
if (Volatile.Read(ref _top) == top && Volatile.Read(ref _states[idx]) == 1)
|
||||
{
|
||||
value = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
value = default!;
|
||||
SpinWait sw = default;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int oldTop = Volatile.Read(ref _top);
|
||||
if (oldTop <= 0)
|
||||
{
|
||||
return false; // empty
|
||||
}
|
||||
|
||||
int newTop = oldTop - 1; // index to consume
|
||||
if (Interlocked.CompareExchange(ref _top, newTop, oldTop) != oldTop)
|
||||
{
|
||||
sw.SpinOnce();
|
||||
continue; // failed to reserve element
|
||||
}
|
||||
|
||||
// If producer hasn't published yet, roll back our reservation and retry.
|
||||
if (Volatile.Read(ref _states[newTop]) == 0)
|
||||
{
|
||||
// attempt to restore top; ok if it fails due to concurrent operations
|
||||
_ = Interlocked.CompareExchange(ref _top, newTop + 1, newTop);
|
||||
sw.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
|
||||
value = _values[newTop];
|
||||
_values[newTop] = default!; // avoid ref retention
|
||||
Volatile.Write(ref _states[newTop], 0); // mark empty
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int TryPopRange(Span<T> destination)
|
||||
{
|
||||
if (destination.IsEmpty) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (; i < destination.Length; i++)
|
||||
{
|
||||
if (!TryPop(out destination[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +1,84 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IBidirectionalDictionary"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>
|
||||
{
|
||||
|
||||
TFirst this[TSecond key] { get; set; }
|
||||
|
||||
ICollection<TFirst> FirstIndex { get; }
|
||||
|
||||
ICollection<TSecond> SecondIndex { get; }
|
||||
|
||||
IReadOnlyDictionary<TFirst, TSecond> Forward { get; }
|
||||
|
||||
IReadOnlyDictionary<TSecond, TFirst> Reverse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
|
||||
/// specified key.
|
||||
/// </summary>
|
||||
/// <param _label="key">
|
||||
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
|
||||
/// the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
bool Contains(TSecond key);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
|
||||
/// specified key.
|
||||
/// </summary>
|
||||
/// <param _label="key">
|
||||
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
|
||||
/// the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
bool Contains(TFirst key);
|
||||
|
||||
bool TryGetValue(TSecond key, out TFirst value);
|
||||
|
||||
#pragma warning disable CA1033
|
||||
ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex;
|
||||
|
||||
ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex;
|
||||
|
||||
bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key)
|
||||
{
|
||||
return Contains(key);
|
||||
}
|
||||
#pragma warning restore CA1033
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IBidirectionalDictionary"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public interface IBidirectionalDictionary<TFirst, TSecond> : IDictionary<TFirst, TSecond>
|
||||
{
|
||||
|
||||
TFirst this[TSecond key] { get; set; }
|
||||
|
||||
ICollection<TFirst> FirstIndex { get; }
|
||||
|
||||
ICollection<TSecond> SecondIndex { get; }
|
||||
|
||||
IReadOnlyDictionary<TFirst, TSecond> Forward { get; }
|
||||
|
||||
IReadOnlyDictionary<TSecond, TFirst> Reverse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
|
||||
/// specified key.
|
||||
/// </summary>
|
||||
/// <param _label="key">
|
||||
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
|
||||
/// the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
bool Contains(TSecond key);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains the
|
||||
/// specified key.
|
||||
/// </summary>
|
||||
/// <param _label="key">
|
||||
/// The value to locate in the <see cref="IBidirectionalDictionary{TFirst, TSecond}" />.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the <see cref="IBidirectionalDictionary{TFirst, TSecond}" /> contains an element with
|
||||
/// the specified key; otherwise, false.
|
||||
/// </returns>
|
||||
bool Contains(TFirst key);
|
||||
|
||||
bool TryGetValue(TSecond key, out TFirst value);
|
||||
|
||||
#pragma warning disable CA1033
|
||||
ICollection<TFirst> IDictionary<TFirst, TSecond>.Keys => FirstIndex;
|
||||
|
||||
ICollection<TSecond> IDictionary<TFirst, TSecond>.Values => SecondIndex;
|
||||
|
||||
bool IDictionary<TFirst, TSecond>.ContainsKey(TFirst key)
|
||||
{
|
||||
return Contains(key);
|
||||
}
|
||||
#pragma warning restore CA1033
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,53 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IHashMultiplexer"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public interface IHashMultiplexer<T> : IEqualityComparer<T>
|
||||
{
|
||||
|
||||
int RemainedMultiplexCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache hash result for obj with counts.
|
||||
/// </summary>
|
||||
/// <param name="obj">
|
||||
///
|
||||
/// </param>
|
||||
/// <param name="count">
|
||||
///
|
||||
/// </param>
|
||||
void CacheHash(T obj, int count);
|
||||
|
||||
void ExtendCache(int count);
|
||||
|
||||
int ForceComputeHash(T count);
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IHashMultiplexer"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public interface IHashMultiplexer<T> : IEqualityComparer<T>
|
||||
{
|
||||
|
||||
int RemainedMultiplexCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache hash result for obj with counts.
|
||||
/// </summary>
|
||||
/// <param name="obj">
|
||||
///
|
||||
/// </param>
|
||||
/// <param name="count">
|
||||
///
|
||||
/// </param>
|
||||
void CacheHash(T obj, int count);
|
||||
|
||||
void ExtendCache(int count);
|
||||
|
||||
int ForceComputeHash(T count);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,131 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ListSegment"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Part of a collection. This contains a direct reference to _origin collection, so all
|
||||
/// change to _origin collection will be reflected in this object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
///
|
||||
/// </typeparam>
|
||||
public struct ListSegment<T> : ICollection<T>
|
||||
{
|
||||
|
||||
private int _begin;
|
||||
private int _length;
|
||||
private List<T> _collection;
|
||||
|
||||
public ListSegment(List<T> collection, int begin, int length)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(length, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count);
|
||||
_collection = collection;
|
||||
_begin = begin;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public T this[int indexValue]
|
||||
{
|
||||
get
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length);
|
||||
return _collection[indexValue + _begin];
|
||||
}
|
||||
}
|
||||
|
||||
public T this[Index i]
|
||||
{
|
||||
get
|
||||
{
|
||||
int offset = i.IsFromEnd ? _length - i.Value : i.Value;
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length);
|
||||
return _collection[offset + _begin];
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public int Count => _length;
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
int index = _collection.IndexOf(item);
|
||||
return index >= _begin && index < _length + index;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(array);
|
||||
Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length);
|
||||
Span<T> target = new Span<T>(array, arrayIndex, _length);
|
||||
span.CopyTo(target);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = _begin; i < _length; i++) {
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<T>.Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ListSegment"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Part of a collection. This contains a direct reference to _origin collection, so all
|
||||
/// change to _origin collection will be reflected in this object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
///
|
||||
/// </typeparam>
|
||||
public struct ListSegment<T> : ICollection<T>
|
||||
{
|
||||
|
||||
private int _begin;
|
||||
private int _length;
|
||||
private List<T> _collection;
|
||||
|
||||
public ListSegment(List<T> collection, int begin, int length)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(collection);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(begin, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(length, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(begin + length, collection.Count);
|
||||
_collection = collection;
|
||||
_begin = begin;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public T this[int indexValue]
|
||||
{
|
||||
get
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(indexValue, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(indexValue, _length);
|
||||
return _collection[indexValue + _begin];
|
||||
}
|
||||
}
|
||||
|
||||
public T this[Index i]
|
||||
{
|
||||
get
|
||||
{
|
||||
int offset = i.IsFromEnd ? _length - i.Value : i.Value;
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(offset, 0);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _length);
|
||||
return _collection[offset + _begin];
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public int Count => _length;
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
int index = _collection.IndexOf(item);
|
||||
return index >= _begin && index < _length + index;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(array);
|
||||
Span<T> span = CollectionsMarshal.AsSpan<T>(_collection).Slice(_begin, _length);
|
||||
Span<T> target = new Span<T>(array, arrayIndex, _length);
|
||||
span.CopyTo(target);
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = _begin; i < _length; i++) {
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<T>.Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,200 +1,200 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="NotEnumerableList.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public static class LinearBagBuilder
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
|
||||
/// the input.
|
||||
/// </summary>
|
||||
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
if (values.IsEmpty) {
|
||||
return new LinearBag<T>(4);
|
||||
}
|
||||
LinearBag<T> bag = new(values.Length);
|
||||
for (int i = 0; i < values.Length; i++) {
|
||||
bag.Add(values[i]);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A very lightweight contiguous storage structure: unordered, not enumerable by design.
|
||||
/// Provides O(1) Add and O(1) RemoveAt (by swapping with the tail). Intended for high-
|
||||
/// performance scenarios where element ordering does not matter. NOT thread-safe.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Element type.
|
||||
/// </typeparam>
|
||||
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
|
||||
public class LinearBag<T>(int capacity = 4)
|
||||
{
|
||||
|
||||
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
|
||||
private T[] _items = new T[Math.Max(0, capacity)];
|
||||
|
||||
/// <summary>
|
||||
/// Access element by index (0..Count-1). Throws if out of range.
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set
|
||||
{
|
||||
if ((uint)index >= (uint)Count) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
_items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total allocated slots.
|
||||
/// </summary>
|
||||
public int Capacity => _items.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Number of currently stored elements (valid index range 0..Count-1).
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item (unordered). Expands internal array if needed.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count == _items.Length) {
|
||||
EnsureCapacity(Count + 1);
|
||||
}
|
||||
|
||||
_items[Count] = item;
|
||||
Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a span over the live portion of the bag (0..Count-1).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan()
|
||||
{
|
||||
return new Span<T>(_items, 0, Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_items, 0, Count);
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
|
||||
/// released. Use only when you are certain old references can remain.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void FastClear()
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of value (unordered). Does nothing if not found.
|
||||
/// </summary>
|
||||
public void Remove(T value)
|
||||
{
|
||||
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (comparer.Equals(_items[i], value))
|
||||
{
|
||||
RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes element at index by swapping the last element into its slot (unless already
|
||||
/// last). Preserves O(1) removal at cost of ordering.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if ((uint)index >= (uint)Count) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
int last = Count - 1;
|
||||
if (index != last) {
|
||||
_items[index] = _items[last];
|
||||
}
|
||||
|
||||
_items[last] = default!; // release reference / reset value
|
||||
Count = last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int min)
|
||||
{
|
||||
if (_items.Length >= min) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
|
||||
if (newCapacity < min) {
|
||||
newCapacity = min;
|
||||
}
|
||||
|
||||
Array.Resize(ref _items, newCapacity);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="NotEnumerableList.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public static class LinearBagBuilder
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Build a <see cref="LinearBag{T}" /> from a read-only span. Capacity will exactly fit
|
||||
/// the input.
|
||||
/// </summary>
|
||||
public static LinearBag<T> FromSpan<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
if (values.IsEmpty) {
|
||||
return new LinearBag<T>(4);
|
||||
}
|
||||
LinearBag<T> bag = new(values.Length);
|
||||
for (int i = 0; i < values.Length; i++) {
|
||||
bag.Add(values[i]);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A very lightweight contiguous storage structure: unordered, not enumerable by design.
|
||||
/// Provides O(1) Add and O(1) RemoveAt (by swapping with the tail). Intended for high-
|
||||
/// performance scenarios where element ordering does not matter. NOT thread-safe.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Element type.
|
||||
/// </typeparam>
|
||||
[CollectionBuilder(typeof(LinearBagBuilder), nameof(LinearBagBuilder.FromSpan))]
|
||||
public class LinearBag<T>(int capacity = 4)
|
||||
{
|
||||
|
||||
// Invariant: valid elements occupy indexes [0, Count-1]. Count is the number of live elements.
|
||||
private T[] _items = new T[Math.Max(0, capacity)];
|
||||
|
||||
/// <summary>
|
||||
/// Access element by index (0..Count-1). Throws if out of range.
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (uint)index >= (uint)Count ? throw new ArgumentOutOfRangeException(nameof(index)) : _items[index];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set
|
||||
{
|
||||
if ((uint)index >= (uint)Count) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
_items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total allocated slots.
|
||||
/// </summary>
|
||||
public int Capacity => _items.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Number of currently stored elements (valid index range 0..Count-1).
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item (unordered). Expands internal array if needed.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count == _items.Length) {
|
||||
EnsureCapacity(Count + 1);
|
||||
}
|
||||
|
||||
_items[Count] = item;
|
||||
Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a span over the live portion of the bag (0..Count-1).
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan()
|
||||
{
|
||||
return new Span<T>(_items, 0, Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all elements and zeroes out references to avoid retention. Count becomes 0.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_items, 0, Count);
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast clear: only resets Count (leaves underlying array unchanged). References are NOT
|
||||
/// released. Use only when you are certain old references can remain.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void FastClear()
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of value (unordered). Does nothing if not found.
|
||||
/// </summary>
|
||||
public void Remove(T value)
|
||||
{
|
||||
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (comparer.Equals(_items[i], value))
|
||||
{
|
||||
RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes element at index by swapping the last element into its slot (unless already
|
||||
/// last). Preserves O(1) removal at cost of ordering.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if ((uint)index >= (uint)Count) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
int last = Count - 1;
|
||||
if (index != last) {
|
||||
_items[index] = _items[last];
|
||||
}
|
||||
|
||||
_items[last] = default!; // release reference / reset value
|
||||
Count = last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures capacity is at least <paramref name="min" />. Doubles size when growing.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int min)
|
||||
{
|
||||
if (_items.Length >= min) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newCapacity = _items.Length == 0 ? 4 : _items.Length * 2;
|
||||
if (newCapacity < min) {
|
||||
newCapacity = min;
|
||||
}
|
||||
|
||||
Array.Resize(ref _items, newCapacity);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,181 +1,181 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="RandomAccessibleStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public class RandomAccessibleStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private const int DefaultCapacity = 4;
|
||||
|
||||
private T[] _array;
|
||||
private int _size;
|
||||
private int _version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
|
||||
/// default initial capacity.
|
||||
/// </summary>
|
||||
public RandomAccessibleStack()
|
||||
{
|
||||
_array = new T[DefaultCapacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
|
||||
/// specified initial capacity.
|
||||
/// </summary>
|
||||
public RandomAccessibleStack(int capacity)
|
||||
{
|
||||
if (capacity < 0) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(capacity), "Capacity must be non-negative.");
|
||||
}
|
||||
|
||||
_array = new T[capacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
|
||||
/// top).
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= _size) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
return _array[index];
|
||||
}
|
||||
/*
|
||||
set
|
||||
{
|
||||
if( index < 0 || index >= PrimSize ) {
|
||||
throw new ArgumentOutOfRangeException( nameof( index ) );
|
||||
}
|
||||
_array[ index ] = value;
|
||||
_version++;
|
||||
}
|
||||
/**/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements contained in the stack.
|
||||
/// </summary>
|
||||
public int Count => _size;
|
||||
|
||||
/// <summary>
|
||||
/// Removes all objects from the stack.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_array, 0, _size);
|
||||
_size = 0;
|
||||
_version++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the stack from top to bottom. Throws if
|
||||
/// the stack is modified during iteration.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
int expectedVersion = _version;
|
||||
for (int i = _size - 1; i >= 0; i--)
|
||||
{
|
||||
if (expectedVersion != _version) {
|
||||
throw new InvalidOperationException(
|
||||
"Collection was modified during enumeration.");
|
||||
}
|
||||
|
||||
yield return _array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the object at the top of the stack without removing it.
|
||||
/// </summary>
|
||||
public T Peek()
|
||||
{
|
||||
if (_size == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
return _array[_size - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes and returns the object at the top of the stack.
|
||||
/// </summary>
|
||||
public T Pop()
|
||||
{
|
||||
if (_size == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
T item = _array[--_size];
|
||||
_array[_size] = default!;
|
||||
_version++;
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the top of the stack.
|
||||
/// </summary>
|
||||
public void Push(T item)
|
||||
{
|
||||
if (_size == _array.Length) {
|
||||
EnsureCapacity(_size + 1);
|
||||
}
|
||||
|
||||
_array[_size++] = item;
|
||||
_version++;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int min)
|
||||
{
|
||||
int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2;
|
||||
if (newCapacity < min) {
|
||||
newCapacity = min;
|
||||
}
|
||||
|
||||
Array.Resize(ref _array, newCapacity);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="RandomAccessibleStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
public class RandomAccessibleStack<T> : IEnumerable<T>
|
||||
{
|
||||
|
||||
private const int DefaultCapacity = 4;
|
||||
|
||||
private T[] _array;
|
||||
private int _size;
|
||||
private int _version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
|
||||
/// default initial capacity.
|
||||
/// </summary>
|
||||
public RandomAccessibleStack()
|
||||
{
|
||||
_array = new T[DefaultCapacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the RandomAccessStack class that is empty and has the
|
||||
/// specified initial capacity.
|
||||
/// </summary>
|
||||
public RandomAccessibleStack(int capacity)
|
||||
{
|
||||
if (capacity < 0) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(capacity), "Capacity must be non-negative.");
|
||||
}
|
||||
|
||||
_array = new T[capacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element at the specified index in the stack (0 = bottom, Count-1 =
|
||||
/// top).
|
||||
/// </summary>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= _size) {
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
return _array[index];
|
||||
}
|
||||
/*
|
||||
set
|
||||
{
|
||||
if( index < 0 || index >= PrimSize ) {
|
||||
throw new ArgumentOutOfRangeException( nameof( index ) );
|
||||
}
|
||||
_array[ index ] = value;
|
||||
_version++;
|
||||
}
|
||||
/**/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements contained in the stack.
|
||||
/// </summary>
|
||||
public int Count => _size;
|
||||
|
||||
/// <summary>
|
||||
/// Removes all objects from the stack.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_array, 0, _size);
|
||||
_size = 0;
|
||||
_version++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the stack from top to bottom. Throws if
|
||||
/// the stack is modified during iteration.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
int expectedVersion = _version;
|
||||
for (int i = _size - 1; i >= 0; i--)
|
||||
{
|
||||
if (expectedVersion != _version) {
|
||||
throw new InvalidOperationException(
|
||||
"Collection was modified during enumeration.");
|
||||
}
|
||||
|
||||
yield return _array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the object at the top of the stack without removing it.
|
||||
/// </summary>
|
||||
public T Peek()
|
||||
{
|
||||
if (_size == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
return _array[_size - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes and returns the object at the top of the stack.
|
||||
/// </summary>
|
||||
public T Pop()
|
||||
{
|
||||
if (_size == 0) {
|
||||
throw new InvalidOperationException("Stack is empty.");
|
||||
}
|
||||
|
||||
T item = _array[--_size];
|
||||
_array[_size] = default!;
|
||||
_version++;
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the top of the stack.
|
||||
/// </summary>
|
||||
public void Push(T item)
|
||||
{
|
||||
if (_size == _array.Length) {
|
||||
EnsureCapacity(_size + 1);
|
||||
}
|
||||
|
||||
_array[_size++] = item;
|
||||
_version++;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int min)
|
||||
{
|
||||
int newCapacity = _array.Length == 0 ? DefaultCapacity : _array.Length * 2;
|
||||
if (newCapacity < min) {
|
||||
newCapacity = min;
|
||||
}
|
||||
|
||||
Array.Resize(ref _array, newCapacity);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,91 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SwissTable"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SwissTable"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace StgSharp.Collections
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed unsafe partial class SwissTable
|
||||
{
|
||||
|
||||
private const int BucketCount = 1 << EntryMaskWidth;
|
||||
private const int BucketSize = 16;
|
||||
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
|
||||
private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
|
||||
public sealed unsafe partial class SwissTable
|
||||
{
|
||||
|
||||
private const int BucketCount = 1 << EntryMaskWidth;
|
||||
private const int BucketSize = 16;
|
||||
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
|
||||
private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
|
||||
public const int MaxCapacity = BucketCount * BucketSize; // 256 entries per bucket, 1024 buckets
|
||||
|
||||
private readonly Entry* _entries;
|
||||
|
||||
private SwissTable(
|
||||
Entry* buffer
|
||||
)
|
||||
{
|
||||
_entries = buffer;
|
||||
}
|
||||
|
||||
public static SwissTable Create()
|
||||
{
|
||||
Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16);
|
||||
for (int i = 0; i < BucketCount; i++)
|
||||
{
|
||||
// Set all control bytes to 0xFF (empty)
|
||||
Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize);
|
||||
}
|
||||
return new SwissTable(buffer);
|
||||
}
|
||||
|
||||
public bool TryAddOrSet(
|
||||
nint key,
|
||||
nint value,
|
||||
out bool isNewKey
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
|
||||
}
|
||||
|
||||
public bool TryGet(
|
||||
nint key,
|
||||
out nint value
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
|
||||
private readonly Entry* _entries;
|
||||
|
||||
private SwissTable(
|
||||
Entry* buffer
|
||||
)
|
||||
{
|
||||
_entries = buffer;
|
||||
}
|
||||
|
||||
public static SwissTable Create()
|
||||
{
|
||||
Entry* buffer = (Entry*)NativeMemory.AlignedAlloc((nuint)Unsafe.SizeOf<Entry>(), 16);
|
||||
for (int i = 0; i < BucketCount; i++)
|
||||
{
|
||||
// Set all control bytes to 0xFF (empty)
|
||||
Unsafe.InitBlock(&(*buffer)[i].Control, 0xFF, BucketSize);
|
||||
}
|
||||
return new SwissTable(buffer);
|
||||
}
|
||||
|
||||
public bool TryAddOrSet(
|
||||
nint key,
|
||||
nint value,
|
||||
out bool isNewKey
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
|
||||
}
|
||||
|
||||
public bool TryGet(
|
||||
nint key,
|
||||
out nint value
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
return IBucketScanner.Current.TryFind(key, hash, bucket, out value);
|
||||
/*
|
||||
* hash = CRC32 of key
|
||||
@@ -93,108 +93,108 @@ namespace StgSharp.Collections
|
||||
* ctrl = 8-15 of hash
|
||||
* index = rest of hash
|
||||
* find value of hash
|
||||
*/
|
||||
}
|
||||
|
||||
public bool TryRemove(
|
||||
nint key,
|
||||
out nint value
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
return IBucketScanner.Current.TryRemove(key, hash, bucket, out value);
|
||||
}
|
||||
|
||||
~SwissTable()
|
||||
{
|
||||
NativeMemory.AlignedFree(_entries);
|
||||
}
|
||||
|
||||
[InlineArray(BucketCount)]
|
||||
private struct Entry
|
||||
{
|
||||
|
||||
public Bucket Bucket;
|
||||
|
||||
}
|
||||
|
||||
private unsafe struct Bucket
|
||||
{
|
||||
|
||||
public BucketControl Control;
|
||||
public BucketKeyStorage Keys;
|
||||
public BucketValueStorage Values;
|
||||
|
||||
}
|
||||
|
||||
private interface IBucketScanner
|
||||
{
|
||||
|
||||
static IBucketScanner Current { get; } = Create();
|
||||
|
||||
bool TryFind(
|
||||
nint key,
|
||||
uint Hash,
|
||||
Bucket* bucket,
|
||||
out nint value
|
||||
);
|
||||
|
||||
unsafe bool TryRemove(
|
||||
nint key,
|
||||
uint hash,
|
||||
Bucket* bucket,
|
||||
out nint value
|
||||
);
|
||||
|
||||
bool TrySet(
|
||||
nint key,
|
||||
uint hash,
|
||||
Bucket* bucket,
|
||||
nint value,
|
||||
out bool isNewKey
|
||||
);
|
||||
|
||||
private static IBucketScanner Create()
|
||||
{
|
||||
if (Sse42.IsSupported)
|
||||
{
|
||||
return new BucketScannerX86();
|
||||
} else if (ArmBase.IsSupported)
|
||||
{
|
||||
throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform.");
|
||||
} else
|
||||
{
|
||||
throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketControl
|
||||
{
|
||||
|
||||
public byte Control;
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketKeyStorage
|
||||
{
|
||||
|
||||
public nint value;
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketValueStorage
|
||||
{
|
||||
|
||||
public nint value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public bool TryRemove(
|
||||
nint key,
|
||||
out nint value
|
||||
)
|
||||
{
|
||||
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
|
||||
ushort entry = (ushort)(hash & (BucketCount - 1));
|
||||
Bucket* bucket = &(*_entries)[entry];
|
||||
return IBucketScanner.Current.TryRemove(key, hash, bucket, out value);
|
||||
}
|
||||
|
||||
~SwissTable()
|
||||
{
|
||||
NativeMemory.AlignedFree(_entries);
|
||||
}
|
||||
|
||||
[InlineArray(BucketCount)]
|
||||
private struct Entry
|
||||
{
|
||||
|
||||
public Bucket Bucket;
|
||||
|
||||
}
|
||||
|
||||
private unsafe struct Bucket
|
||||
{
|
||||
|
||||
public BucketControl Control;
|
||||
public BucketKeyStorage Keys;
|
||||
public BucketValueStorage Values;
|
||||
|
||||
}
|
||||
|
||||
private interface IBucketScanner
|
||||
{
|
||||
|
||||
static IBucketScanner Current { get; } = Create();
|
||||
|
||||
bool TryFind(
|
||||
nint key,
|
||||
uint Hash,
|
||||
Bucket* bucket,
|
||||
out nint value
|
||||
);
|
||||
|
||||
unsafe bool TryRemove(
|
||||
nint key,
|
||||
uint hash,
|
||||
Bucket* bucket,
|
||||
out nint value
|
||||
);
|
||||
|
||||
bool TrySet(
|
||||
nint key,
|
||||
uint hash,
|
||||
Bucket* bucket,
|
||||
nint value,
|
||||
out bool isNewKey
|
||||
);
|
||||
|
||||
private static IBucketScanner Create()
|
||||
{
|
||||
if (Sse42.IsSupported)
|
||||
{
|
||||
return new BucketScannerX86();
|
||||
} else if (ArmBase.IsSupported)
|
||||
{
|
||||
throw new NotSupportedException("ARM architecture is currently not supported for bucket scanning. Please use an x86 platform.");
|
||||
} else
|
||||
{
|
||||
throw new NotSupportedException("No suitable bucket scanner implementation found for the current platform.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketControl
|
||||
{
|
||||
|
||||
public byte Control;
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketKeyStorage
|
||||
{
|
||||
|
||||
public nint value;
|
||||
|
||||
}
|
||||
|
||||
[InlineArray(BucketSize)]
|
||||
private struct BucketValueStorage
|
||||
{
|
||||
|
||||
public nint value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,190 +1,190 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Dialogue"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static class Dialogue
|
||||
{
|
||||
|
||||
private static Process? _dialogueProcess;
|
||||
private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe;
|
||||
private static int _readAndWriteThreadId;
|
||||
private static ProcessStartInfo _dialogueProcessInfo;
|
||||
private static StreamReader _dr;
|
||||
private static StreamWriter _dw;
|
||||
|
||||
public static bool NeedShowWhenStartup { get; set; }
|
||||
|
||||
public static void PostError(
|
||||
Exception ex
|
||||
)
|
||||
{
|
||||
CreateDialogueProcessIfNotExist();
|
||||
|
||||
_dw.WriteLine($"posterror {ex.Message}");
|
||||
|
||||
_dialogueProcess!.Exited -= DialogueProcessExitCallback;
|
||||
}
|
||||
|
||||
public static void SetReadAndWriteThread(
|
||||
Thread thread
|
||||
)
|
||||
{
|
||||
_readAndWriteThreadId = thread.ManagedThreadId;
|
||||
}
|
||||
|
||||
internal static void CreateDialogueProcessIfNotExist()
|
||||
{
|
||||
if (_dialogueProcess != null) {
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(_dialogueProcessInfo.FileName)) {
|
||||
return;
|
||||
}
|
||||
_dialogueProcess = Process.Start(_dialogueProcessInfo);
|
||||
|
||||
_dr = new StreamReader(_dialogueClientPipe);
|
||||
_dw = new StreamWriter(_dialogueServerPipe);
|
||||
|
||||
_dialogueProcess!.Exited += DialogueProcessExitCallback;
|
||||
|
||||
_dialogueServerPipe.DisposeLocalCopyOfClientHandle();
|
||||
_dialogueClientPipe.DisposeLocalCopyOfClientHandle();
|
||||
}
|
||||
|
||||
internal static void LoadDialogue()
|
||||
{
|
||||
_dialogueServerPipe = new AnonymousPipeServerStream(
|
||||
PipeDirection.Out, HandleInheritability.Inheritable);
|
||||
_dialogueClientPipe = new AnonymousPipeServerStream(
|
||||
PipeDirection.In, HandleInheritability.Inheritable);
|
||||
|
||||
string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty;
|
||||
string path = Path.GetDirectoryName(route) ?? string.Empty;
|
||||
|
||||
string exeName;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
exeName = ".exe";
|
||||
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
exeName = string.Empty;
|
||||
} else
|
||||
{
|
||||
throw new PlatformNotSupportedException("Unknown OS platform");
|
||||
}
|
||||
|
||||
string consoleRoute = Path.Combine(
|
||||
path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}");
|
||||
|
||||
_dialogueProcessInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = consoleRoute,
|
||||
Arguments =
|
||||
$"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}",
|
||||
UseShellExecute = true,
|
||||
RedirectStandardOutput = false,
|
||||
};
|
||||
}
|
||||
|
||||
internal static void PostMessage(
|
||||
string instruction,
|
||||
params string[] operands
|
||||
)
|
||||
{
|
||||
CreateDialogueProcessIfNotExist();
|
||||
|
||||
_dw.WriteLine($"{instruction} {operands}");
|
||||
}
|
||||
|
||||
internal static void UnloadDialogue()
|
||||
{
|
||||
if (_dialogueProcess != null) {
|
||||
_dialogueProcess.Kill();
|
||||
}
|
||||
_dialogueProcess = null;
|
||||
|
||||
_dialogueServerPipe.Dispose();
|
||||
_dialogueClientPipe.Dispose();
|
||||
}
|
||||
|
||||
internal static string WaitCertainReturn(
|
||||
string target
|
||||
)
|
||||
{
|
||||
if (_dialogueProcess == null) {
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string temp = string .Empty;
|
||||
do
|
||||
{
|
||||
temp = _dr.ReadLine();
|
||||
temp = temp ?? string.Empty;
|
||||
if (!temp!.StartsWith(target))
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
int index = temp.IndexOf(' ');
|
||||
return index != -1 ? temp.Substring(index + 1) : string.Empty;
|
||||
}
|
||||
|
||||
private static void AssertThread()
|
||||
{
|
||||
if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) {
|
||||
throw new InvalidOperationException(
|
||||
"Communication with dialogue can only call from certain thread.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void DialogueProcessExitCallback(
|
||||
object? sender,
|
||||
EventArgs e
|
||||
)
|
||||
{
|
||||
_dialogueProcess = null;
|
||||
_dr.Close();
|
||||
_dw.Close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Dialogue"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static class Dialogue
|
||||
{
|
||||
|
||||
private static Process? _dialogueProcess;
|
||||
private static AnonymousPipeServerStream _dialogueClientPipe,_dialogueServerPipe;
|
||||
private static int _readAndWriteThreadId;
|
||||
private static ProcessStartInfo _dialogueProcessInfo;
|
||||
private static StreamReader _dr;
|
||||
private static StreamWriter _dw;
|
||||
|
||||
public static bool NeedShowWhenStartup { get; set; }
|
||||
|
||||
public static void PostError(
|
||||
Exception ex
|
||||
)
|
||||
{
|
||||
CreateDialogueProcessIfNotExist();
|
||||
|
||||
_dw.WriteLine($"posterror {ex.Message}");
|
||||
|
||||
_dialogueProcess!.Exited -= DialogueProcessExitCallback;
|
||||
}
|
||||
|
||||
public static void SetReadAndWriteThread(
|
||||
Thread thread
|
||||
)
|
||||
{
|
||||
_readAndWriteThreadId = thread.ManagedThreadId;
|
||||
}
|
||||
|
||||
internal static void CreateDialogueProcessIfNotExist()
|
||||
{
|
||||
if (_dialogueProcess != null) {
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(_dialogueProcessInfo.FileName)) {
|
||||
return;
|
||||
}
|
||||
_dialogueProcess = Process.Start(_dialogueProcessInfo);
|
||||
|
||||
_dr = new StreamReader(_dialogueClientPipe);
|
||||
_dw = new StreamWriter(_dialogueServerPipe);
|
||||
|
||||
_dialogueProcess!.Exited += DialogueProcessExitCallback;
|
||||
|
||||
_dialogueServerPipe.DisposeLocalCopyOfClientHandle();
|
||||
_dialogueClientPipe.DisposeLocalCopyOfClientHandle();
|
||||
}
|
||||
|
||||
internal static void LoadDialogue()
|
||||
{
|
||||
_dialogueServerPipe = new AnonymousPipeServerStream(
|
||||
PipeDirection.Out, HandleInheritability.Inheritable);
|
||||
_dialogueClientPipe = new AnonymousPipeServerStream(
|
||||
PipeDirection.In, HandleInheritability.Inheritable);
|
||||
|
||||
string route = Assembly.GetAssembly(typeof(World))?.Location ?? string.Empty;
|
||||
string path = Path.GetDirectoryName(route) ?? string.Empty;
|
||||
|
||||
string exeName;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
exeName = ".exe";
|
||||
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
exeName = string.Empty;
|
||||
} else
|
||||
{
|
||||
throw new PlatformNotSupportedException("Unknown OS platform");
|
||||
}
|
||||
|
||||
string consoleRoute = Path.Combine(
|
||||
path, @"StgSharp.TerminalDialogue", $@"StgSharp.TerminalDialogue{exeName}");
|
||||
|
||||
_dialogueProcessInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = consoleRoute,
|
||||
Arguments =
|
||||
$"{_dialogueServerPipe.GetClientHandleAsString()} {_dialogueClientPipe.GetClientHandleAsString()}",
|
||||
UseShellExecute = true,
|
||||
RedirectStandardOutput = false,
|
||||
};
|
||||
}
|
||||
|
||||
internal static void PostMessage(
|
||||
string instruction,
|
||||
params string[] operands
|
||||
)
|
||||
{
|
||||
CreateDialogueProcessIfNotExist();
|
||||
|
||||
_dw.WriteLine($"{instruction} {operands}");
|
||||
}
|
||||
|
||||
internal static void UnloadDialogue()
|
||||
{
|
||||
if (_dialogueProcess != null) {
|
||||
_dialogueProcess.Kill();
|
||||
}
|
||||
_dialogueProcess = null;
|
||||
|
||||
_dialogueServerPipe.Dispose();
|
||||
_dialogueClientPipe.Dispose();
|
||||
}
|
||||
|
||||
internal static string WaitCertainReturn(
|
||||
string target
|
||||
)
|
||||
{
|
||||
if (_dialogueProcess == null) {
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string temp = string .Empty;
|
||||
do
|
||||
{
|
||||
temp = _dr.ReadLine();
|
||||
temp = temp ?? string.Empty;
|
||||
if (!temp!.StartsWith(target))
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
int index = temp.IndexOf(' ');
|
||||
return index != -1 ? temp.Substring(index + 1) : string.Empty;
|
||||
}
|
||||
|
||||
private static void AssertThread()
|
||||
{
|
||||
if (_readAndWriteThreadId != Environment.CurrentManagedThreadId) {
|
||||
throw new InvalidOperationException(
|
||||
"Communication with dialogue can only call from certain thread.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void DialogueProcessExitCallback(
|
||||
object? sender,
|
||||
EventArgs e
|
||||
)
|
||||
{
|
||||
_dialogueProcess = null;
|
||||
_dr.Close();
|
||||
_dw.Close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ErrorDefine"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public class UniformEmptyReferenceException : Exception
|
||||
{
|
||||
|
||||
public UniformEmptyReferenceException(string program, string name)
|
||||
: base(
|
||||
$"Location -1 returned when trying to find uniform {name} in shader program {program}") { }
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ErrorDefine"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public class UniformEmptyReferenceException : Exception
|
||||
{
|
||||
|
||||
public UniformEmptyReferenceException(string program, string name)
|
||||
: base(
|
||||
$"Location -1 returned when trying to find uniform {name} in shader program {program}") { }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +1,98 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="GlobalSetting.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static unsafe partial class World
|
||||
{
|
||||
|
||||
internal const int ssdSegmentLength = 16;
|
||||
|
||||
private static byte[] _currentHash = [];
|
||||
|
||||
private static bool _inited;
|
||||
|
||||
public static byte[] CurrentAssemblyHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentHash == null)
|
||||
{
|
||||
string route = Assembly.GetExecutingAssembly().Location;
|
||||
string time = DateTime.UtcNow.ToString();
|
||||
using (SHA256 sh = SHA256.Create())
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(route);
|
||||
_currentHash = sh.ComputeHash(bytes);
|
||||
}
|
||||
}
|
||||
return _currentHash;
|
||||
}
|
||||
}
|
||||
|
||||
public static int MainThreadID
|
||||
{
|
||||
get => (_mainThreadID == -1) ?
|
||||
throw new InvalidOperationException(
|
||||
"StgSharp environment is not inited. Main thread id is not available") :
|
||||
_mainThreadID;
|
||||
internal set => _mainThreadID = value;
|
||||
}
|
||||
|
||||
private static int _mainThreadID { get; set; } = -1;
|
||||
|
||||
public static class GlobalSetting
|
||||
{
|
||||
|
||||
private static bool vSyncActivated;
|
||||
|
||||
public static bool VSyncActivated
|
||||
{
|
||||
get => vsyncActivated;
|
||||
set
|
||||
{
|
||||
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
|
||||
vsyncActivated = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}//-------------------------------------- End of Class ---------------------------------------//
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="GlobalSetting.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static unsafe partial class World
|
||||
{
|
||||
|
||||
internal const int ssdSegmentLength = 16;
|
||||
|
||||
private static byte[] _currentHash = [];
|
||||
|
||||
private static bool _inited;
|
||||
|
||||
public static byte[] CurrentAssemblyHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentHash == null)
|
||||
{
|
||||
string route = Assembly.GetExecutingAssembly().Location;
|
||||
string time = DateTime.UtcNow.ToString();
|
||||
using (SHA256 sh = SHA256.Create())
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(route);
|
||||
_currentHash = sh.ComputeHash(bytes);
|
||||
}
|
||||
}
|
||||
return _currentHash;
|
||||
}
|
||||
}
|
||||
|
||||
public static int MainThreadID
|
||||
{
|
||||
get => (_mainThreadID == -1) ?
|
||||
throw new InvalidOperationException(
|
||||
"StgSharp environment is not inited. Main thread id is not available") :
|
||||
_mainThreadID;
|
||||
internal set => _mainThreadID = value;
|
||||
}
|
||||
|
||||
private static int _mainThreadID { get; set; } = -1;
|
||||
|
||||
public static class GlobalSetting
|
||||
{
|
||||
|
||||
private static bool vSyncActivated;
|
||||
|
||||
public static bool VSyncActivated
|
||||
{
|
||||
get => vsyncActivated;
|
||||
set
|
||||
{
|
||||
// GraphicFramework.glfwSwapInterval(value ? 1 : 0);
|
||||
vsyncActivated = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}//-------------------------------------- End of Class ---------------------------------------//
|
||||
}
|
||||
@@ -1,284 +1,284 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IStaticModule"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a statically activated module that can be registered and initialized as part of
|
||||
/// the application startup sequence, and gracefully torn down on shutdown.
|
||||
/// </summary>
|
||||
public interface IStaticModule
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of this module, used for logging and diagnostics.
|
||||
/// </summary>
|
||||
string ModuleName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs all initialization work for this module using the provided profile.
|
||||
/// </summary>
|
||||
/// <param name="profile">
|
||||
/// The configuration profile supplied at registration time. Use <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
|
||||
/// </param>
|
||||
void InitializeModule(IModuleInitializeProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should
|
||||
/// be idempotent: calling this method more than once must not throw.
|
||||
/// </summary>
|
||||
void UninitializeModule();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for module initialization configuration. Implementations are plain data
|
||||
/// carriers; no static factory is required.
|
||||
/// </summary>
|
||||
public interface IModuleInitializeProfile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A shared, allocation-free empty profile for modules that require no configuration.
|
||||
/// </summary>
|
||||
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance;
|
||||
|
||||
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile
|
||||
{
|
||||
|
||||
public static readonly EmptyModuleInitializeProfile Instance = new();
|
||||
|
||||
private EmptyModuleInitializeProfile() { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
|
||||
/// ensuring the two are never stored or iterated independently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
|
||||
/// the pair were stored as two parallel lists of reference types.
|
||||
/// </remarks>
|
||||
public readonly struct ModuleDescriptor
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> using <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> as the profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile)
|
||||
{
|
||||
Module = module;
|
||||
Profile = profile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration profile associated with the module.
|
||||
/// </summary>
|
||||
public IModuleInitializeProfile Profile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module to be initialized.
|
||||
/// </summary>
|
||||
public IStaticModule Module { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
Module.InitializeModule(Profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module.
|
||||
/// </summary>
|
||||
public void Uninitialize()
|
||||
{
|
||||
Module.UninitializeModule();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An ordered collection of modules to be initialized during application startup and
|
||||
/// uninitialized on shutdown. Supports three registration strategies so that the <c>new()</c>
|
||||
/// generic constraint is never a hard requirement.
|
||||
/// </summary>
|
||||
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
|
||||
{
|
||||
|
||||
private readonly List<ModuleDescriptor> _descriptors = [];
|
||||
|
||||
internal ModuleToInitializeCollection(World.Initializer i)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(i));
|
||||
}
|
||||
|
||||
// ── IEnumerable<IStaticModule> ───────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IStaticModule> GetEnumerator()
|
||||
{
|
||||
foreach (ModuleDescriptor d in _descriptors) {
|
||||
yield return d.Module;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Initialization / Uninitialization ────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Initializes all registered modules in the order they were registered. Each module
|
||||
/// receives the profile that was supplied at registration time.
|
||||
/// </summary>
|
||||
public void InitializeModule()
|
||||
{
|
||||
for (int i = 0; i < _descriptors.Count; i++) {
|
||||
_descriptors[i].Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninitializes all registered modules in <b> reverse </b> registration order, mirroring
|
||||
/// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems
|
||||
/// that depend on it).
|
||||
/// </summary>
|
||||
public void UninitializeModule()
|
||||
{
|
||||
for (int i = _descriptors.Count - 1; i >= 0; i--) {
|
||||
_descriptors[i].Uninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Registration API ─────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Registers a module of type <typeparamref name="T" /> using its default constructor.
|
||||
/// This is the most concise overload for simple modules that expose a public
|
||||
/// parameterless constructor.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// A concrete module type that implements <see cref="IStaticModule" /> and exposes a
|
||||
/// public parameterless constructor.
|
||||
/// </typeparam>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null)
|
||||
where T: IStaticModule, new()
|
||||
{
|
||||
return UseModule(static() => new T(), profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a module produced by a factory delegate. This overload imposes no generic
|
||||
/// constraints and supports any construction strategy, including dependency injection and
|
||||
/// parameterized constructors.
|
||||
/// </summary>
|
||||
/// <param name="factory">
|
||||
/// A delegate that constructs and returns the module instance. The delegate is invoked
|
||||
/// once, immediately at registration time. If <paramref name="factory" /> is <see
|
||||
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored
|
||||
/// and the collection is returned unchanged.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(
|
||||
Func<IStaticModule> factory,
|
||||
IModuleInitializeProfile? profile = null)
|
||||
{
|
||||
if (factory is null) {
|
||||
return this;
|
||||
}
|
||||
IStaticModule? mod = factory();
|
||||
if (mod is null) {
|
||||
return this;
|
||||
}
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
mod,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a pre-existing module instance. Use this overload when the module is
|
||||
/// provided externally, such as from a DI container, an object pool, or a singleton.
|
||||
/// </summary>
|
||||
/// <param name="module">
|
||||
/// The module instance to register.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
module,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
// ── IStaticModule explicit implementation ────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see
|
||||
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
|
||||
/// its own registered profile.
|
||||
/// </summary>
|
||||
void IStaticModule.InitializeModule(IModuleInitializeProfile profile)
|
||||
{
|
||||
InitializeModule();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
string IStaticModule.ModuleName => "Module Initializer";
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IStaticModule"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a statically activated module that can be registered and initialized as part of
|
||||
/// the application startup sequence, and gracefully torn down on shutdown.
|
||||
/// </summary>
|
||||
public interface IStaticModule
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of this module, used for logging and diagnostics.
|
||||
/// </summary>
|
||||
string ModuleName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs all initialization work for this module using the provided profile.
|
||||
/// </summary>
|
||||
/// <param name="profile">
|
||||
/// The configuration profile supplied at registration time. Use <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> if no configuration is required.
|
||||
/// </param>
|
||||
void InitializeModule(IModuleInitializeProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources acquired during <see cref="InitializeModule" />. Implementations should
|
||||
/// be idempotent: calling this method more than once must not throw.
|
||||
/// </summary>
|
||||
void UninitializeModule();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for module initialization configuration. Implementations are plain data
|
||||
/// carriers; no static factory is required.
|
||||
/// </summary>
|
||||
public interface IModuleInitializeProfile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A shared, allocation-free empty profile for modules that require no configuration.
|
||||
/// </summary>
|
||||
static readonly IModuleInitializeProfile Empty = EmptyModuleInitializeProfile.Instance;
|
||||
|
||||
private sealed class EmptyModuleInitializeProfile : IModuleInitializeProfile
|
||||
{
|
||||
|
||||
public static readonly EmptyModuleInitializeProfile Instance = new();
|
||||
|
||||
private EmptyModuleInitializeProfile() { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pairs an <see cref="IStaticModule" /> with its <see cref="IModuleInitializeProfile" />,
|
||||
/// ensuring the two are never stored or iterated independently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using a <see langword="readonly struct" /> avoids the boxing overhead that would occur if
|
||||
/// the pair were stored as two parallel lists of reference types.
|
||||
/// </remarks>
|
||||
public readonly struct ModuleDescriptor
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> using <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> as the profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(IStaticModule module) : this(module, IModuleInitializeProfile.Empty) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ModuleDescriptor" /> with an explicit profile.
|
||||
/// </summary>
|
||||
public ModuleDescriptor(IStaticModule module, IModuleInitializeProfile profile)
|
||||
{
|
||||
Module = module;
|
||||
Profile = profile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration profile associated with the module.
|
||||
/// </summary>
|
||||
public IModuleInitializeProfile Profile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module to be initialized.
|
||||
/// </summary>
|
||||
public IStaticModule Module { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IStaticModule.InitializeModule" /> with the stored profile.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
Module.InitializeModule(Profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IStaticModule.UninitializeModule" /> on the stored module.
|
||||
/// </summary>
|
||||
public void Uninitialize()
|
||||
{
|
||||
Module.UninitializeModule();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An ordered collection of modules to be initialized during application startup and
|
||||
/// uninitialized on shutdown. Supports three registration strategies so that the <c>new()</c>
|
||||
/// generic constraint is never a hard requirement.
|
||||
/// </summary>
|
||||
public sealed class ModuleToInitializeCollection : IEnumerable<IStaticModule>, IStaticModule
|
||||
{
|
||||
|
||||
private readonly List<ModuleDescriptor> _descriptors = [];
|
||||
|
||||
internal ModuleToInitializeCollection(World.Initializer i)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(i));
|
||||
}
|
||||
|
||||
// ── IEnumerable<IStaticModule> ───────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IStaticModule> GetEnumerator()
|
||||
{
|
||||
foreach (ModuleDescriptor d in _descriptors) {
|
||||
yield return d.Module;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Initialization / Uninitialization ────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Initializes all registered modules in the order they were registered. Each module
|
||||
/// receives the profile that was supplied at registration time.
|
||||
/// </summary>
|
||||
public void InitializeModule()
|
||||
{
|
||||
for (int i = 0; i < _descriptors.Count; i++) {
|
||||
_descriptors[i].Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninitializes all registered modules in <b> reverse </b> registration order, mirroring
|
||||
/// the typical LIFO teardown convention (e.g., GLFW must be unloaded after all subsystems
|
||||
/// that depend on it).
|
||||
/// </summary>
|
||||
public void UninitializeModule()
|
||||
{
|
||||
for (int i = _descriptors.Count - 1; i >= 0; i--) {
|
||||
_descriptors[i].Uninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Registration API ─────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Registers a module of type <typeparamref name="T" /> using its default constructor.
|
||||
/// This is the most concise overload for simple modules that expose a public
|
||||
/// parameterless constructor.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// A concrete module type that implements <see cref="IStaticModule" /> and exposes a
|
||||
/// public parameterless constructor.
|
||||
/// </typeparam>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule<T>(IModuleInitializeProfile? profile = null)
|
||||
where T: IStaticModule, new()
|
||||
{
|
||||
return UseModule(static() => new T(), profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a module produced by a factory delegate. This overload imposes no generic
|
||||
/// constraints and supports any construction strategy, including dependency injection and
|
||||
/// parameterized constructors.
|
||||
/// </summary>
|
||||
/// <param name="factory">
|
||||
/// A delegate that constructs and returns the module instance. The delegate is invoked
|
||||
/// once, immediately at registration time. If <paramref name="factory" /> is <see
|
||||
/// langword="null" /> or returns <see langword="null" />, the call is silently ignored
|
||||
/// and the collection is returned unchanged.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(
|
||||
Func<IStaticModule> factory,
|
||||
IModuleInitializeProfile? profile = null)
|
||||
{
|
||||
if (factory is null) {
|
||||
return this;
|
||||
}
|
||||
IStaticModule? mod = factory();
|
||||
if (mod is null) {
|
||||
return this;
|
||||
}
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
mod,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a pre-existing module instance. Use this overload when the module is
|
||||
/// provided externally, such as from a DI container, an object pool, or a singleton.
|
||||
/// </summary>
|
||||
/// <param name="module">
|
||||
/// The module instance to register.
|
||||
/// </param>
|
||||
/// <param name="profile">
|
||||
/// Optional initialization profile. Defaults to <see
|
||||
/// cref="IModuleInitializeProfile.Empty" /> when <see langword="null" />.
|
||||
/// </param>
|
||||
public ModuleToInitializeCollection UseModule(IStaticModule module, IModuleInitializeProfile? profile = null)
|
||||
{
|
||||
_descriptors.Add(new ModuleDescriptor(
|
||||
module,
|
||||
profile ?? IModuleInitializeProfile.Empty));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
// ── IStaticModule explicit implementation ────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Explicit <see cref="IStaticModule" /> implementation. Delegates to <see
|
||||
/// cref="InitializeModule()" />, ignoring the supplied profile because each contained module uses
|
||||
/// its own registered profile.
|
||||
/// </summary>
|
||||
void IStaticModule.InitializeModule(IModuleInitializeProfile profile)
|
||||
{
|
||||
InitializeModule();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
string IStaticModule.ModuleName => "Module Initializer";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,237 +1,237 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Platform"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
internal enum GraphicAPI : byte
|
||||
{
|
||||
|
||||
GL,
|
||||
VK
|
||||
|
||||
}
|
||||
|
||||
public static partial class World
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Version of current World platform. Main version represent in hundred's digit, and sub
|
||||
/// version represent in ten's digit.
|
||||
/// </summary>
|
||||
public const long version = 430;
|
||||
|
||||
private static bool vsyncActivated;
|
||||
|
||||
private static uint markCount = 0;
|
||||
internal static GraphicAPI API = default;
|
||||
|
||||
public static ModuleToInitializeCollection Initialize()
|
||||
{
|
||||
return new ModuleToInitializeCollection(new Initializer());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogError(
|
||||
Exception ex
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(ex.Message, LogType.Error);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogInfo(
|
||||
string log
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Info);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogWarning(
|
||||
string log
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogWarning(
|
||||
Exception notVerySeriesException
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Mark(
|
||||
bool needConsole
|
||||
)
|
||||
{
|
||||
markCount++;
|
||||
if (needConsole) {
|
||||
Console.WriteLine(markCount);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Terminate(
|
||||
int exitCode
|
||||
)
|
||||
{
|
||||
MainTimeProvider.StopProvidingTime();
|
||||
|
||||
// GraphicFramework.glfwTerminate();
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
public static void Track(
|
||||
object target,
|
||||
string message,
|
||||
bool isConsole,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerLineNumber] int lineNumber = 0
|
||||
)
|
||||
{
|
||||
Type t = target.GetType();
|
||||
string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n";
|
||||
FieldInfo[] fieldInfo = t.GetFields(
|
||||
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n";
|
||||
foreach (FieldInfo field in fieldInfo) {
|
||||
log += $"|{field.Name}\t\t|{field.FieldType}\t\t|{field.GetValue(target)}\t\t|\n";
|
||||
}
|
||||
log += $"Tracking at:\n{filePath}\t{callerName}\tline {lineNumber}\n";
|
||||
log += $"Additional information:\n{message}\n";
|
||||
if (isConsole)
|
||||
{
|
||||
Console.WriteLine(log);
|
||||
} else
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a log message to log file, and mark the _level of this message.
|
||||
/// </summary>
|
||||
/// <param _label="message">
|
||||
/// A string contains information of how current evironment runs.
|
||||
/// </param>
|
||||
/// <param _label="logType">
|
||||
/// The severity of current message.
|
||||
/// </param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLog(
|
||||
string message,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(message, logType);
|
||||
}
|
||||
|
||||
internal static TDele ConvertAPI<TDele>(
|
||||
IntPtr funcPtr
|
||||
)
|
||||
{
|
||||
if (funcPtr == IntPtr.Zero) {
|
||||
throw new InvalidCastException(
|
||||
"Failed to convert api, the function pointer is zero.");
|
||||
}
|
||||
try
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Delegate ConvertAPI(
|
||||
IntPtr funcPtr,
|
||||
Type T
|
||||
)
|
||||
{
|
||||
if (funcPtr == IntPtr.Zero) {
|
||||
throw new InvalidCastException(
|
||||
"Failed to convert api, the function pointer is zero.");
|
||||
}
|
||||
try
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer(funcPtr, T);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe void InternalInitialize()
|
||||
{
|
||||
// Initialize time provider and start providing time
|
||||
MainTimeProvider.StartProvidingTime();
|
||||
DefaultLog.InternalAppendLog("\n\n\n");
|
||||
DefaultLog.InternalWriteLog(
|
||||
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
|
||||
MainThreadID = System.Environment.CurrentManagedThreadId;
|
||||
|
||||
/**/
|
||||
Dialogue.NeedShowWhenStartup = false;
|
||||
Dialogue.LoadDialogue();
|
||||
if (Dialogue.NeedShowWhenStartup) {
|
||||
Dialogue.CreateDialogueProcessIfNotExist();
|
||||
}
|
||||
/**/
|
||||
}
|
||||
|
||||
internal class Initializer : IStaticModule
|
||||
{
|
||||
|
||||
public string ModuleName => "StgSharpCore";
|
||||
|
||||
public void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
)
|
||||
{
|
||||
World.InternalInitialize();
|
||||
}
|
||||
|
||||
public void UninitializeModule()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Platform"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
internal enum GraphicAPI : byte
|
||||
{
|
||||
|
||||
GL,
|
||||
VK
|
||||
|
||||
}
|
||||
|
||||
public static partial class World
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Version of current World platform. Main version represent in hundred's digit, and sub
|
||||
/// version represent in ten's digit.
|
||||
/// </summary>
|
||||
public const long version = 430;
|
||||
|
||||
private static bool vsyncActivated;
|
||||
|
||||
private static uint markCount = 0;
|
||||
internal static GraphicAPI API = default;
|
||||
|
||||
public static ModuleToInitializeCollection Initialize()
|
||||
{
|
||||
return new ModuleToInitializeCollection(new Initializer());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogError(
|
||||
Exception ex
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(ex.Message, LogType.Error);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogInfo(
|
||||
string log
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Info);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogWarning(
|
||||
string log
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogWarning(
|
||||
Exception notVerySeriesException
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(notVerySeriesException.Message, LogType.Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Mark(
|
||||
bool needConsole
|
||||
)
|
||||
{
|
||||
markCount++;
|
||||
if (needConsole) {
|
||||
Console.WriteLine(markCount);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Terminate(
|
||||
int exitCode
|
||||
)
|
||||
{
|
||||
MainTimeProvider.StopProvidingTime();
|
||||
|
||||
// GraphicFramework.glfwTerminate();
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
public static void Track(
|
||||
object target,
|
||||
string message,
|
||||
bool isConsole,
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerLineNumber] int lineNumber = 0
|
||||
)
|
||||
{
|
||||
Type t = target.GetType();
|
||||
string log = $"Tracked object: {nameof(target)}\t\tType:{t.Name}\n";
|
||||
FieldInfo[] fieldInfo = t.GetFields(
|
||||
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
log += "|\tFieldName\t|\tFieldType\t|\tFieldValue\t|\n";
|
||||
foreach (FieldInfo field in fieldInfo) {
|
||||
log += $"|{field.Name}\t\t|{field.FieldType}\t\t|{field.GetValue(target)}\t\t|\n";
|
||||
}
|
||||
log += $"Tracking at:\n{filePath}\t{callerName}\tline {lineNumber}\n";
|
||||
log += $"Additional information:\n{message}\n";
|
||||
if (isConsole)
|
||||
{
|
||||
Console.WriteLine(log);
|
||||
} else
|
||||
{
|
||||
DefaultLog.InternalWriteLog(log, LogType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a log message to log file, and mark the _level of this message.
|
||||
/// </summary>
|
||||
/// <param _label="message">
|
||||
/// A string contains information of how current evironment runs.
|
||||
/// </param>
|
||||
/// <param _label="logType">
|
||||
/// The severity of current message.
|
||||
/// </param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLog(
|
||||
string message,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
DefaultLog.InternalWriteLog(message, logType);
|
||||
}
|
||||
|
||||
internal static TDele ConvertAPI<TDele>(
|
||||
IntPtr funcPtr
|
||||
)
|
||||
{
|
||||
if (funcPtr == IntPtr.Zero) {
|
||||
throw new InvalidCastException(
|
||||
"Failed to convert api, the function pointer is zero.");
|
||||
}
|
||||
try
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer<TDele>(funcPtr);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Delegate ConvertAPI(
|
||||
IntPtr funcPtr,
|
||||
Type T
|
||||
)
|
||||
{
|
||||
if (funcPtr == IntPtr.Zero) {
|
||||
throw new InvalidCastException(
|
||||
"Failed to convert api, the function pointer is zero.");
|
||||
}
|
||||
try
|
||||
{
|
||||
return Marshal.GetDelegateForFunctionPointer(funcPtr, T);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe void InternalInitialize()
|
||||
{
|
||||
// Initialize time provider and start providing time
|
||||
MainTimeProvider.StartProvidingTime();
|
||||
DefaultLog.InternalAppendLog("\n\n\n");
|
||||
DefaultLog.InternalWriteLog(
|
||||
$"Program {Assembly.GetEntryAssembly()!.FullName} on {Environment.MachineName} Started.", LogType.Info);
|
||||
MainThreadID = System.Environment.CurrentManagedThreadId;
|
||||
|
||||
/**/
|
||||
Dialogue.NeedShowWhenStartup = false;
|
||||
Dialogue.LoadDialogue();
|
||||
if (Dialogue.NeedShowWhenStartup) {
|
||||
Dialogue.CreateDialogueProcessIfNotExist();
|
||||
}
|
||||
/**/
|
||||
}
|
||||
|
||||
internal class Initializer : IStaticModule
|
||||
{
|
||||
|
||||
public string ModuleName => "StgSharpCore";
|
||||
|
||||
public void InitializeModule(
|
||||
IModuleInitializeProfile profile
|
||||
)
|
||||
{
|
||||
World.InternalInitialize();
|
||||
}
|
||||
|
||||
public void UninitializeModule()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+37
-37
@@ -1,37 +1,37 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Global"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
global using StgSharp.Internal;
|
||||
global
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("StgSharp.Mathematics")]
|
||||
[assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")]
|
||||
[assembly: InternalsVisibleTo("StgSharp.Graphics")]
|
||||
[assembly: InternalsVisibleTo("StgSharpDebug")]
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Global"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
global using StgSharp.Internal;
|
||||
global
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("StgSharp.Mathematics")]
|
||||
[assembly: InternalsVisibleTo("StgSharp.RegularAnalysis")]
|
||||
[assembly: InternalsVisibleTo("StgSharp.Graphics")]
|
||||
[assembly: InternalsVisibleTo("StgSharpDebug")]
|
||||
|
||||
@@ -1,163 +1,163 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BufferStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
[CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))]
|
||||
public unsafe class BufferStack<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private byte* _buffer;
|
||||
private ulong _count;
|
||||
|
||||
public BufferStack(int capacity)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
|
||||
|
||||
AllocateBuffer(capacity);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (int)_count;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
((Header*)_buffer)->Top = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
Marshal.FreeHGlobal((nint)_buffer);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Pop()
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top == 0) {
|
||||
throw new InvalidOperationException("Stack underflow");
|
||||
}
|
||||
|
||||
header->Top--;
|
||||
_count--;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
return dataStart[header->Top];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Push(T item)
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top >= header->Capacity)
|
||||
{
|
||||
Expand();
|
||||
header = (Header*)_buffer;
|
||||
}
|
||||
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
dataStart[header->Top] = item;
|
||||
header->Top++;
|
||||
_count++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top == 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
header->Top--;
|
||||
_count--;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
value = dataStart[header->Top];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal method for efficient bulk initialization
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.IsEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Header* header = (Header*)_buffer;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
|
||||
fixed (T* sourcePtr = span) {
|
||||
Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T),
|
||||
(nuint)span.Length * (nuint)sizeof(T));
|
||||
}
|
||||
|
||||
header->Top = (nuint)span.Length;
|
||||
_count = (ulong)span.Length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void AllocateBuffer(int capacity)
|
||||
{
|
||||
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
|
||||
_buffer = (byte*)NativeMemory.Alloc(size);
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
header->Capacity = (nuint)capacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Expand()
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
nuint newCap = header->Capacity * 2;
|
||||
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
|
||||
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
|
||||
header = (Header*)_buffer;
|
||||
header->Capacity = newCap;
|
||||
}
|
||||
|
||||
private struct Header
|
||||
{
|
||||
|
||||
public nuint Capacity;
|
||||
public nuint Top;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BufferStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
[CollectionBuilder(typeof(BufferStackBuilder), nameof(BufferStackBuilder.Create))]
|
||||
public unsafe class BufferStack<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private byte* _buffer;
|
||||
private ulong _count;
|
||||
|
||||
public BufferStack(int capacity)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
|
||||
|
||||
AllocateBuffer(capacity);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (int)_count;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
((Header*)_buffer)->Top = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
Marshal.FreeHGlobal((nint)_buffer);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Pop()
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top == 0) {
|
||||
throw new InvalidOperationException("Stack underflow");
|
||||
}
|
||||
|
||||
header->Top--;
|
||||
_count--;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
return dataStart[header->Top];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Push(T item)
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top >= header->Capacity)
|
||||
{
|
||||
Expand();
|
||||
header = (Header*)_buffer;
|
||||
}
|
||||
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
dataStart[header->Top] = item;
|
||||
header->Top++;
|
||||
_count++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
if (header->Top == 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
header->Top--;
|
||||
_count--;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
value = dataStart[header->Top];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal method for efficient bulk initialization
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.IsEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Header* header = (Header*)_buffer;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
|
||||
fixed (T* sourcePtr = span) {
|
||||
Buffer.MemoryCopy(sourcePtr, dataStart, header->Capacity * (nuint)sizeof(T),
|
||||
(nuint)span.Length * (nuint)sizeof(T));
|
||||
}
|
||||
|
||||
header->Top = (nuint)span.Length;
|
||||
_count = (ulong)span.Length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void AllocateBuffer(int capacity)
|
||||
{
|
||||
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
|
||||
_buffer = (byte*)NativeMemory.Alloc(size);
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
header->Capacity = (nuint)capacity;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Expand()
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
nuint newCap = header->Capacity * 2;
|
||||
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
|
||||
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
|
||||
header = (Header*)_buffer;
|
||||
header->Capacity = newCap;
|
||||
}
|
||||
|
||||
private struct Header
|
||||
{
|
||||
|
||||
public nuint Capacity;
|
||||
public nuint Top;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,308 +1,308 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ConcurrentBufferStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public static class BufferStackBuilder
|
||||
{
|
||||
|
||||
// For BufferStack collection builder
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return new BufferStack<T>(4); // Default small capacity
|
||||
}
|
||||
|
||||
BufferStack<T> stack = new(span.Length);
|
||||
|
||||
|
||||
// Use internal method for efficient bulk initialization
|
||||
stack.InitializeFromSpan(span);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// For ConcurrentBufferStack collection builder
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return new ConcurrentBufferStack<T>(4); // Default small capacity
|
||||
}
|
||||
|
||||
ConcurrentBufferStack<T> stack = new(span.Length);
|
||||
|
||||
|
||||
// Use internal method for efficient bulk initialization
|
||||
stack.InitializeFromSpan(span);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))]
|
||||
public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private byte* _buffer;
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private long _count;
|
||||
|
||||
public ConcurrentBufferStack()
|
||||
{
|
||||
AllocateBuffer(4);
|
||||
}
|
||||
|
||||
public ConcurrentBufferStack(int capacity)
|
||||
{
|
||||
if (capacity <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
}
|
||||
|
||||
AllocateBuffer(capacity);
|
||||
}
|
||||
|
||||
public ulong Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (ulong)Volatile.Read(ref _count);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
_lock.EnterBufferCopy(); // exclusive while mutating Top
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
Volatile.Write(ref _count, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_lock.Dispose();
|
||||
NativeMemory.Free(_buffer);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int oldTop = Volatile.Read(ref header->Top);
|
||||
if (oldTop <= 0) {
|
||||
throw new InvalidOperationException("Stack underflow");
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
|
||||
{
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return dataStart[oldTop - 1];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Ensure capacity, expanding if needed
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int topSnapshot = Volatile.Read(ref header->Top);
|
||||
int cap = Volatile.Read(ref header->Capacity);
|
||||
if (topSnapshot >= cap)
|
||||
{
|
||||
// Need expansion
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
header = (Header*)_buffer;
|
||||
if (header->Top >= header->Capacity)
|
||||
{
|
||||
int newCap = header->Capacity * 2;
|
||||
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
|
||||
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
|
||||
((Header*)_buffer)->Capacity = newCap;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int idx = header->Top; // reserve next slot
|
||||
if (idx >= header->Capacity)
|
||||
{
|
||||
// lost race against expander or other producer; retry
|
||||
continue;
|
||||
}
|
||||
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
dataStart[idx] = item; // write first
|
||||
header->Top = idx + 1; // then publish by increasing top
|
||||
_ = Interlocked.Increment(ref _count);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_lock.EnterBufferRead();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int oldTop = Volatile.Read(ref header->Top);
|
||||
if (oldTop <= 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
|
||||
{
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
value = dataStart[oldTop - 1];
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal method for efficient bulk initialization
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.IsEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Header* header = (Header*)_buffer;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
|
||||
fixed (T* sourcePtr = span) {
|
||||
Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T),
|
||||
(nuint)span.Length * (nuint)sizeof(T));
|
||||
}
|
||||
|
||||
header->Top = span.Length;
|
||||
Volatile.Write(ref _count, span.Length);
|
||||
}
|
||||
|
||||
private void AllocateBuffer(int capacity)
|
||||
{
|
||||
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
|
||||
_buffer = (byte*)NativeMemory.Alloc(size);
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
header->Capacity = capacity;
|
||||
}
|
||||
|
||||
private struct Header
|
||||
{
|
||||
|
||||
public int Capacity; // max capacity of the stack (number of elements)
|
||||
public int Top; // next available index in the stack
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ConcurrentBufferStack"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public static class BufferStackBuilder
|
||||
{
|
||||
|
||||
// For BufferStack collection builder
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static BufferStack<T> Create<T>(ReadOnlySpan<T> span) where T: unmanaged
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return new BufferStack<T>(4); // Default small capacity
|
||||
}
|
||||
|
||||
BufferStack<T> stack = new(span.Length);
|
||||
|
||||
|
||||
// Use internal method for efficient bulk initialization
|
||||
stack.InitializeFromSpan(span);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// For ConcurrentBufferStack collection builder
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ConcurrentBufferStack<T> CreateConcurrent<T>(ReadOnlySpan<T> span) where T: unmanaged
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return new ConcurrentBufferStack<T>(4); // Default small capacity
|
||||
}
|
||||
|
||||
ConcurrentBufferStack<T> stack = new(span.Length);
|
||||
|
||||
|
||||
// Use internal method for efficient bulk initialization
|
||||
stack.InitializeFromSpan(span);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CollectionBuilder(builderType: typeof(BufferStackBuilder), methodName: nameof(BufferStackBuilder.CreateConcurrent))]
|
||||
public unsafe class ConcurrentBufferStack<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private byte* _buffer;
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private long _count;
|
||||
|
||||
public ConcurrentBufferStack()
|
||||
{
|
||||
AllocateBuffer(4);
|
||||
}
|
||||
|
||||
public ConcurrentBufferStack(int capacity)
|
||||
{
|
||||
if (capacity <= 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
}
|
||||
|
||||
AllocateBuffer(capacity);
|
||||
}
|
||||
|
||||
public ulong Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (ulong)Volatile.Read(ref _count);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
_lock.EnterBufferCopy(); // exclusive while mutating Top
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
Volatile.Write(ref _count, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_lock.Dispose();
|
||||
NativeMemory.Free(_buffer);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_lock.EnterBufferRead(); // prevent buffer being reallocated while reading/modifying Top
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int oldTop = Volatile.Read(ref header->Top);
|
||||
if (oldTop <= 0) {
|
||||
throw new InvalidOperationException("Stack underflow");
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
|
||||
{
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return dataStart[oldTop - 1];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Ensure capacity, expanding if needed
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int topSnapshot = Volatile.Read(ref header->Top);
|
||||
int cap = Volatile.Read(ref header->Capacity);
|
||||
if (topSnapshot >= cap)
|
||||
{
|
||||
// Need expansion
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
header = (Header*)_buffer;
|
||||
if (header->Top >= header->Capacity)
|
||||
{
|
||||
int newCap = header->Capacity * 2;
|
||||
nuint newSize = (nuint)sizeof(Header) + ((nuint)newCap * (nuint)sizeof(T));
|
||||
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
|
||||
((Header*)_buffer)->Capacity = newCap;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve and write under exclusive buffer lock to avoid consumers reading unwritten data
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int idx = header->Top; // reserve next slot
|
||||
if (idx >= header->Capacity)
|
||||
{
|
||||
// lost race against expander or other producer; retry
|
||||
continue;
|
||||
}
|
||||
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
dataStart[idx] = item; // write first
|
||||
header->Top = idx + 1; // then publish by increasing top
|
||||
_ = Interlocked.Increment(ref _count);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPop(out T value)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_lock.EnterBufferRead();
|
||||
try
|
||||
{
|
||||
Header* header = (Header*)_buffer;
|
||||
int oldTop = Volatile.Read(ref header->Top);
|
||||
if (oldTop <= 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
|
||||
{
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
value = dataStart[oldTop - 1];
|
||||
_ = Interlocked.Decrement(ref _count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal method for efficient bulk initialization
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe void InitializeFromSpan(ReadOnlySpan<T> span)
|
||||
{
|
||||
if (span.IsEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Header* header = (Header*)_buffer;
|
||||
T* dataStart = (T*)(_buffer + sizeof(Header));
|
||||
|
||||
fixed (T* sourcePtr = span) {
|
||||
Buffer.MemoryCopy(sourcePtr, dataStart, (nuint)header->Capacity * (nuint)sizeof(T),
|
||||
(nuint)span.Length * (nuint)sizeof(T));
|
||||
}
|
||||
|
||||
header->Top = span.Length;
|
||||
Volatile.Write(ref _count, span.Length);
|
||||
}
|
||||
|
||||
private void AllocateBuffer(int capacity)
|
||||
{
|
||||
nuint size = (nuint)sizeof(Header) + (nuint)capacity * (nuint)sizeof(T);
|
||||
_buffer = (byte*)NativeMemory.Alloc(size);
|
||||
Header* header = (Header*)_buffer;
|
||||
header->Top = 0;
|
||||
header->Capacity = capacity;
|
||||
}
|
||||
|
||||
private struct Header
|
||||
{
|
||||
|
||||
public int Capacity; // max capacity of the stack (number of elements)
|
||||
public int Top; // next available index in the stack
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,114 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocationHandle"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle
|
||||
{
|
||||
|
||||
internal readonly Allocator.Entry* EntryHandle;
|
||||
public readonly long AllocSize;
|
||||
|
||||
internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s)
|
||||
{
|
||||
EntryHandle = handle;
|
||||
Pointer = (byte*)handle->Position;
|
||||
AllocSize = s;
|
||||
|
||||
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
|
||||
}
|
||||
|
||||
public readonly ref byte this[uint index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
|
||||
return ref Pointer[index];
|
||||
}
|
||||
}
|
||||
|
||||
public readonly byte* Pointer { get; init; }
|
||||
|
||||
public readonly Span<byte> BufferHandle
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(Pointer, (int)AllocSize);
|
||||
}
|
||||
|
||||
public readonly Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
public ref struct Enumerator : IEnumerator<byte>
|
||||
{
|
||||
|
||||
private HybridLayerSegregatedFitAllocationHandle _handle;
|
||||
|
||||
private uint _index = ~0u;
|
||||
|
||||
internal Enumerator(HybridLayerSegregatedFitAllocationHandle span)
|
||||
{
|
||||
_handle = span;
|
||||
Span = span.BufferHandle;
|
||||
}
|
||||
|
||||
public ref byte RefCurrent => ref _handle[_index];
|
||||
|
||||
public Span<byte> Span { get; init; }
|
||||
|
||||
public readonly void Dispose() { }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _handle.AllocSize;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
object IEnumerator.Current => RefCurrent;
|
||||
|
||||
byte IEnumerator<byte>.Current => RefCurrent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocationHandle"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Allocator = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocator;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public readonly unsafe struct HybridLayerSegregatedFitAllocationHandle
|
||||
{
|
||||
|
||||
internal readonly Allocator.Entry* EntryHandle;
|
||||
public readonly long AllocSize;
|
||||
|
||||
internal HybridLayerSegregatedFitAllocationHandle(Allocator.Entry* handle, long s)
|
||||
{
|
||||
EntryHandle = handle;
|
||||
Pointer = (byte*)handle->Position;
|
||||
AllocSize = s;
|
||||
|
||||
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
|
||||
}
|
||||
|
||||
public readonly ref byte this[uint index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
|
||||
return ref Pointer[index];
|
||||
}
|
||||
}
|
||||
|
||||
public readonly byte* Pointer { get; init; }
|
||||
|
||||
public readonly Span<byte> BufferHandle
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(Pointer, (int)AllocSize);
|
||||
}
|
||||
|
||||
public readonly Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
public ref struct Enumerator : IEnumerator<byte>
|
||||
{
|
||||
|
||||
private HybridLayerSegregatedFitAllocationHandle _handle;
|
||||
|
||||
private uint _index = ~0u;
|
||||
|
||||
internal Enumerator(HybridLayerSegregatedFitAllocationHandle span)
|
||||
{
|
||||
_handle = span;
|
||||
Span = span.BufferHandle;
|
||||
}
|
||||
|
||||
public ref byte RefCurrent => ref _handle[_index];
|
||||
|
||||
public Span<byte> Span { get; init; }
|
||||
|
||||
public readonly void Dispose() { }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _handle.AllocSize;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
object IEnumerator.Current => RefCurrent;
|
||||
|
||||
byte IEnumerator<byte>.Current => RefCurrent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Alloc"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
public hlsfHandle Alloc(long size)
|
||||
{
|
||||
if (size > _maxSize) {
|
||||
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
|
||||
}
|
||||
|
||||
int level = GetLevelFromSize(size);
|
||||
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
|
||||
|
||||
int i = level, s = segmentIndex;
|
||||
Entry* handle;
|
||||
while (!TryPopLevelForAllocation(i, s, out handle))
|
||||
{
|
||||
if (++s >= 3)
|
||||
{
|
||||
s = 0;
|
||||
i++;
|
||||
}
|
||||
if (i > MaxLevel)
|
||||
{
|
||||
if (TryPopLevelForAllocation(10, 1, out handle))
|
||||
{
|
||||
break;
|
||||
} else if (TryAllocateNew32MBBlock(out handle))
|
||||
{
|
||||
break;
|
||||
}
|
||||
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
|
||||
}
|
||||
}
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
|
||||
if (requestedOffset > handle->Size) {
|
||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
||||
}
|
||||
long remainder = handle->Size - requestedOffset;
|
||||
SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
|
||||
handle->State = EntryState.Alloc;
|
||||
if (remainder > 0) {
|
||||
SliceMemory(handle->NextNear, remainder);
|
||||
}
|
||||
}
|
||||
return new hlsfHandle(handle, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
|
||||
/// Notice: this method is not responsible for freeing the original BucketNode
|
||||
/// </summary>
|
||||
/// <param name="e">
|
||||
///
|
||||
/// </param>
|
||||
/// <param name="sizeToSlice">
|
||||
///
|
||||
/// </param>
|
||||
private void SliceMemory(Entry* e, long sizeToSlice)
|
||||
{
|
||||
Entry* next = e;
|
||||
long remain = sizeToSlice; // remained size to slice
|
||||
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
|
||||
long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
|
||||
|
||||
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
|
||||
i = int.Min(10, i);
|
||||
for (; i < _levelSizeArray.Length; i++)
|
||||
{
|
||||
int size = _levelSizeArray[i];
|
||||
int upper = size * 4;
|
||||
long edge = (offset + upper - 1) / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = count * size;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
}
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > MaxLevel)
|
||||
{
|
||||
int size = _levelSizeArray[^1];
|
||||
int upper = size * 4;
|
||||
long edge = end / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / upper);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = (long)count * (long)upper;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i--; i >= 0; i--)
|
||||
{
|
||||
int size = _levelSizeArray[i];
|
||||
long edge = end / size * size;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = count * size;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
b->NextLevel = null;
|
||||
b->PreviousLevel = null;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
}
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to allocate a new 16MB block when all buckets are empty
|
||||
/// </summary>
|
||||
/// <param name="handle">
|
||||
/// Output handle to the allocated Entry
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if allocation succeeded
|
||||
/// </returns>
|
||||
private bool TryAllocateNew32MBBlock(out Entry* handle)
|
||||
{
|
||||
nuint pos = _spareMemory->Position;
|
||||
long remain = _spareMemory->Size;
|
||||
if (remain <= 0)
|
||||
{
|
||||
handle = null;
|
||||
return false;
|
||||
}
|
||||
long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
|
||||
|
||||
|
||||
// build new entry
|
||||
Entry* e = (Entry*)_entries.Allocate();
|
||||
SetEntryLevel(e, pos, size);
|
||||
InsertEntryBefore(e, _spareMemory);
|
||||
e->State = EntryState.Empty;
|
||||
|
||||
// set fields of spare memory
|
||||
_spareMemory->Size -= size;
|
||||
_spareMemory->Position += (nuint)size;
|
||||
|
||||
|
||||
handle = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for allocation - converts BucketNode to Entry
|
||||
/// </summary>
|
||||
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
|
||||
{
|
||||
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
|
||||
{
|
||||
entry = bucketNode->EntryRef;
|
||||
return entry != null;
|
||||
}
|
||||
entry = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Alloc"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
public hlsfHandle Alloc(long size)
|
||||
{
|
||||
if (size > _maxSize) {
|
||||
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
|
||||
}
|
||||
|
||||
int level = GetLevelFromSize(size);
|
||||
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
|
||||
|
||||
int i = level, s = segmentIndex;
|
||||
Entry* handle;
|
||||
while (!TryPopLevelForAllocation(i, s, out handle))
|
||||
{
|
||||
if (++s >= 3)
|
||||
{
|
||||
s = 0;
|
||||
i++;
|
||||
}
|
||||
if (i > MaxLevel)
|
||||
{
|
||||
if (TryPopLevelForAllocation(10, 1, out handle))
|
||||
{
|
||||
break;
|
||||
} else if (TryAllocateNew32MBBlock(out handle))
|
||||
{
|
||||
break;
|
||||
}
|
||||
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
|
||||
}
|
||||
}
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
|
||||
if (requestedOffset > handle->Size) {
|
||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
||||
}
|
||||
long remainder = handle->Size - requestedOffset;
|
||||
SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
|
||||
handle->State = EntryState.Alloc;
|
||||
if (remainder > 0) {
|
||||
SliceMemory(handle->NextNear, remainder);
|
||||
}
|
||||
}
|
||||
return new hlsfHandle(handle, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
|
||||
/// Notice: this method is not responsible for freeing the original BucketNode
|
||||
/// </summary>
|
||||
/// <param name="e">
|
||||
///
|
||||
/// </param>
|
||||
/// <param name="sizeToSlice">
|
||||
///
|
||||
/// </param>
|
||||
private void SliceMemory(Entry* e, long sizeToSlice)
|
||||
{
|
||||
Entry* next = e;
|
||||
long remain = sizeToSlice; // remained size to slice
|
||||
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
|
||||
long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
|
||||
|
||||
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
|
||||
i = int.Min(10, i);
|
||||
for (; i < _levelSizeArray.Length; i++)
|
||||
{
|
||||
int size = _levelSizeArray[i];
|
||||
int upper = size * 4;
|
||||
long edge = (offset + upper - 1) / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = count * size;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
}
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > MaxLevel)
|
||||
{
|
||||
int size = _levelSizeArray[^1];
|
||||
int upper = size * 4;
|
||||
long edge = end / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / upper);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = (long)count * (long)upper;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i--; i >= 0; i--)
|
||||
{
|
||||
int size = _levelSizeArray[i];
|
||||
long edge = end / size * size;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
if (count > 0)
|
||||
{
|
||||
long newSize = count * size;
|
||||
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
InsertEntryBefore(newEntry, next);
|
||||
SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
|
||||
newEntry->State = EntryState.Empty;
|
||||
|
||||
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||
b->EntryRef = newEntry;
|
||||
b->NextLevel = null;
|
||||
b->PreviousLevel = null;
|
||||
PushBucketToLevel(i, count - 1, b);
|
||||
offset += newSize;
|
||||
remain -= newSize;
|
||||
}
|
||||
if (remain <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to allocate a new 16MB block when all buckets are empty
|
||||
/// </summary>
|
||||
/// <param name="handle">
|
||||
/// Output handle to the allocated Entry
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if allocation succeeded
|
||||
/// </returns>
|
||||
private bool TryAllocateNew32MBBlock(out Entry* handle)
|
||||
{
|
||||
nuint pos = _spareMemory->Position;
|
||||
long remain = _spareMemory->Size;
|
||||
if (remain <= 0)
|
||||
{
|
||||
handle = null;
|
||||
return false;
|
||||
}
|
||||
long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
|
||||
|
||||
|
||||
// build new entry
|
||||
Entry* e = (Entry*)_entries.Allocate();
|
||||
SetEntryLevel(e, pos, size);
|
||||
InsertEntryBefore(e, _spareMemory);
|
||||
e->State = EntryState.Empty;
|
||||
|
||||
// set fields of spare memory
|
||||
_spareMemory->Size -= size;
|
||||
_spareMemory->Position += (nuint)size;
|
||||
|
||||
|
||||
handle = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for allocation - converts BucketNode to Entry
|
||||
/// </summary>
|
||||
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
|
||||
{
|
||||
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
|
||||
{
|
||||
entry = bucketNode->EntryRef;
|
||||
return entry != null;
|
||||
}
|
||||
entry = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,171 +1,171 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.BucketNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
if (node == null || node->EntryRef == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] is null)
|
||||
{
|
||||
_bucketHeads[index] = node;
|
||||
node->IsInBucket = true;
|
||||
node->NextLevel = null;
|
||||
node->PreviousLevel = null;
|
||||
|
||||
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
|
||||
return;
|
||||
} else
|
||||
{
|
||||
BucketNode* head = _bucketHeads[index];
|
||||
node->NextLevel = head;
|
||||
BucketNode* headNode = head;
|
||||
node->IsInBucket = true;
|
||||
_bucketHeads[index] = node;
|
||||
|
||||
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
|
||||
if (headNode != null)
|
||||
{
|
||||
headNode->PreviousLevel = node;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] == node)
|
||||
{
|
||||
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
BucketNode* previous = node->PreviousLevel;
|
||||
BucketNode* next = node->NextLevel;
|
||||
|
||||
if (previous != null) {
|
||||
previous->NextLevel = next;
|
||||
}
|
||||
if (next != null) {
|
||||
next->PreviousLevel = previous;
|
||||
}
|
||||
|
||||
node->NextLevel = (BucketNode*)null;
|
||||
node->PreviousLevel = (BucketNode*)null;
|
||||
node->IsInBucket = false;
|
||||
return;
|
||||
}
|
||||
|
||||
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
|
||||
{
|
||||
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
|
||||
BucketNode* head = _bucketHeads[index];
|
||||
|
||||
if (head == null)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
|
||||
BucketNode* next = head->NextLevel;
|
||||
|
||||
if (next == null)
|
||||
{
|
||||
_bucketHeads[index] = null;
|
||||
node = head;
|
||||
return true;
|
||||
}
|
||||
_bucketHeads[index] = head->NextLevel;
|
||||
node = head;
|
||||
node->IsInBucket = false;
|
||||
node->NextLevel = null;
|
||||
if (next != null) {
|
||||
next->PreviousLevel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] == node)
|
||||
{
|
||||
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
|
||||
return ok;
|
||||
}
|
||||
|
||||
BucketNode* previous = node->PreviousLevel;
|
||||
BucketNode* next = node->NextLevel;
|
||||
|
||||
if (previous != null) {
|
||||
previous->NextLevel = next;
|
||||
}
|
||||
if (next != null) {
|
||||
next->PreviousLevel = previous;
|
||||
}
|
||||
|
||||
node->NextLevel = null;
|
||||
node->PreviousLevel = null;
|
||||
node->IsInBucket = false;
|
||||
node->EntryRef = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BucketNode structure - specialized node for bucket linked lists Stored in main
|
||||
/// allocation area (user-overwritable region)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct BucketNode
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
|
||||
// [FieldOffset(16)] internal ulong LockBuffer;
|
||||
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
|
||||
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
|
||||
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.BucketNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
if (node == null || node->EntryRef == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] is null)
|
||||
{
|
||||
_bucketHeads[index] = node;
|
||||
node->IsInBucket = true;
|
||||
node->NextLevel = null;
|
||||
node->PreviousLevel = null;
|
||||
|
||||
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
|
||||
return;
|
||||
} else
|
||||
{
|
||||
BucketNode* head = _bucketHeads[index];
|
||||
node->NextLevel = head;
|
||||
BucketNode* headNode = head;
|
||||
node->IsInBucket = true;
|
||||
_bucketHeads[index] = node;
|
||||
|
||||
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
|
||||
if (headNode != null)
|
||||
{
|
||||
headNode->PreviousLevel = node;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] == node)
|
||||
{
|
||||
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
BucketNode* previous = node->PreviousLevel;
|
||||
BucketNode* next = node->NextLevel;
|
||||
|
||||
if (previous != null) {
|
||||
previous->NextLevel = next;
|
||||
}
|
||||
if (next != null) {
|
||||
next->PreviousLevel = previous;
|
||||
}
|
||||
|
||||
node->NextLevel = (BucketNode*)null;
|
||||
node->PreviousLevel = (BucketNode*)null;
|
||||
node->IsInBucket = false;
|
||||
return;
|
||||
}
|
||||
|
||||
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
|
||||
{
|
||||
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
|
||||
BucketNode* head = _bucketHeads[index];
|
||||
|
||||
if (head == null)
|
||||
{
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
|
||||
BucketNode* next = head->NextLevel;
|
||||
|
||||
if (next == null)
|
||||
{
|
||||
_bucketHeads[index] = null;
|
||||
node = head;
|
||||
return true;
|
||||
}
|
||||
_bucketHeads[index] = head->NextLevel;
|
||||
node = head;
|
||||
node->IsInBucket = false;
|
||||
node->NextLevel = null;
|
||||
if (next != null) {
|
||||
next->PreviousLevel = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||
{
|
||||
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||
if (_bucketHeads[index] == node)
|
||||
{
|
||||
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
|
||||
return ok;
|
||||
}
|
||||
|
||||
BucketNode* previous = node->PreviousLevel;
|
||||
BucketNode* next = node->NextLevel;
|
||||
|
||||
if (previous != null) {
|
||||
previous->NextLevel = next;
|
||||
}
|
||||
if (next != null) {
|
||||
next->PreviousLevel = previous;
|
||||
}
|
||||
|
||||
node->NextLevel = null;
|
||||
node->PreviousLevel = null;
|
||||
node->IsInBucket = false;
|
||||
node->EntryRef = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BucketNode structure - specialized node for bucket linked lists Stored in main
|
||||
/// allocation area (user-overwritable region)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct BucketNode
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
|
||||
// [FieldOffset(16)] internal ulong LockBuffer;
|
||||
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
|
||||
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
|
||||
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +1,122 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Debug"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public sealed class HLSFPositionChainBreakException : Exception
|
||||
{
|
||||
|
||||
public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { }
|
||||
|
||||
}
|
||||
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private object _lock = new object();
|
||||
|
||||
public void DisplayEntryLayout(bool singleStep)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Entry* cur = _spareMemory;
|
||||
while (cur != null)
|
||||
{
|
||||
Console.WriteLine(EntryToString(cur));
|
||||
cur = cur->PreviousNear;
|
||||
if (singleStep) {
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
Entry* cur = _spareMemory;
|
||||
int cnt = 0;
|
||||
if (cur != null) {
|
||||
cur = cur->NextNear;
|
||||
}
|
||||
Console.WriteLine(EntryToString(_spareMemory));
|
||||
while (cur != null && cur != _spareMemory && cnt < maxCount)
|
||||
{
|
||||
Console.WriteLine(EntryToString(cur));
|
||||
cnt++;
|
||||
if (singleStep) {
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
cur = cur->NextNear;
|
||||
}
|
||||
if (cur == _spareMemory)
|
||||
{
|
||||
Console.WriteLine("(reached sentinel)");
|
||||
} else if (cnt >= maxCount) {
|
||||
Console.WriteLine("(stopped at maxCount)");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> ShowEntryLayout()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Stack<string> layout = [];
|
||||
Entry* cur = _spareMemory;
|
||||
while (cur != null)
|
||||
{
|
||||
layout.Push(EntryToString(cur));
|
||||
cur = cur->PreviousNear;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EntryToString(Entry* e)
|
||||
{
|
||||
BucketNode* b = (BucketNode*)e->Position;
|
||||
return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Debug"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public sealed class HLSFPositionChainBreakException : Exception
|
||||
{
|
||||
|
||||
public HLSFPositionChainBreakException() : base("Position chain of an HLSF allocator is broken") { }
|
||||
|
||||
}
|
||||
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private object _lock = new object();
|
||||
|
||||
public void DisplayEntryLayout(bool singleStep)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Entry* cur = _spareMemory;
|
||||
while (cur != null)
|
||||
{
|
||||
Console.WriteLine(EntryToString(cur));
|
||||
cur = cur->PreviousNear;
|
||||
if (singleStep) {
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
Entry* cur = _spareMemory;
|
||||
int cnt = 0;
|
||||
if (cur != null) {
|
||||
cur = cur->NextNear;
|
||||
}
|
||||
Console.WriteLine(EntryToString(_spareMemory));
|
||||
while (cur != null && cur != _spareMemory && cnt < maxCount)
|
||||
{
|
||||
Console.WriteLine(EntryToString(cur));
|
||||
cnt++;
|
||||
if (singleStep) {
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
cur = cur->NextNear;
|
||||
}
|
||||
if (cur == _spareMemory)
|
||||
{
|
||||
Console.WriteLine("(reached sentinel)");
|
||||
} else if (cnt >= maxCount) {
|
||||
Console.WriteLine("(stopped at maxCount)");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> ShowEntryLayout()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
Stack<string> layout = [];
|
||||
Entry* cur = _spareMemory;
|
||||
while (cur != null)
|
||||
{
|
||||
layout.Push(EntryToString(cur));
|
||||
cur = cur->PreviousNear;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EntryToString(Entry* e)
|
||||
{
|
||||
BucketNode* b = (BucketNode*)e->Position;
|
||||
return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,129 +1,129 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Free"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public enum FreePolicy
|
||||
{
|
||||
|
||||
FullCollect,
|
||||
LazyCollect,
|
||||
NoCollect
|
||||
|
||||
}
|
||||
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
|
||||
/// back to bucket. Caller-responsible design: no slicing or redistribution.
|
||||
/// </summary>
|
||||
public void Collect()
|
||||
{
|
||||
Entry* cur = _spareMemory->NextNear;
|
||||
long size = 0;
|
||||
while (cur != null && cur != _spareMemory)
|
||||
{
|
||||
// cur is not empty, skip
|
||||
if (cur->State != EntryState.Empty)
|
||||
{
|
||||
cur = cur->NextNear;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove cur from bucket
|
||||
do
|
||||
{
|
||||
size += cur->Size;
|
||||
Entry* next = cur->NextNear;
|
||||
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
|
||||
RemoveFromPositionChain(cur);
|
||||
cur = next;
|
||||
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
|
||||
if (size > 0) {
|
||||
SliceMemory(cur, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free allocated memory block and perform adjacent block merging
|
||||
/// </summary>
|
||||
/// <param name="handle">
|
||||
/// Handle to the allocated memory block
|
||||
/// <param name="collectPolicy">
|
||||
/// </param>
|
||||
public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect)
|
||||
{
|
||||
Entry* e = handle.EntryHandle;
|
||||
if (e == null) {
|
||||
return;
|
||||
}
|
||||
if (e->State != EntryState.Alloc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as empty so it can be merged
|
||||
e->State = EntryState.Empty;
|
||||
|
||||
// Try merge with adjacent free blocks and remove them from buckets
|
||||
if (collectPolicy == FreePolicy.LazyCollect)
|
||||
{
|
||||
MergeAndRemoveFromBuckets(e);
|
||||
}
|
||||
|
||||
// Calculate final level and segment for the merged block
|
||||
long finalSize = e->Size;
|
||||
int finalLevel = GetLevelFromSize(finalSize);
|
||||
e->Level = finalLevel;
|
||||
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
|
||||
|
||||
// Push the merged entry back to appropriate bucket
|
||||
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
||||
|
||||
// Console.WriteLine("one block freed");
|
||||
if (collectPolicy == FreePolicy.FullCollect)
|
||||
{
|
||||
Collect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Free"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using hlsfHandle = StgSharp.Mathematics.Memory.HybridLayerSegregatedFitAllocationHandle;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public enum FreePolicy
|
||||
{
|
||||
|
||||
FullCollect,
|
||||
LazyCollect,
|
||||
NoCollect
|
||||
|
||||
}
|
||||
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
|
||||
/// back to bucket. Caller-responsible design: no slicing or redistribution.
|
||||
/// </summary>
|
||||
public void Collect()
|
||||
{
|
||||
Entry* cur = _spareMemory->NextNear;
|
||||
long size = 0;
|
||||
while (cur != null && cur != _spareMemory)
|
||||
{
|
||||
// cur is not empty, skip
|
||||
if (cur->State != EntryState.Empty)
|
||||
{
|
||||
cur = cur->NextNear;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove cur from bucket
|
||||
do
|
||||
{
|
||||
size += cur->Size;
|
||||
Entry* next = cur->NextNear;
|
||||
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
|
||||
RemoveFromPositionChain(cur);
|
||||
cur = next;
|
||||
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
|
||||
if (size > 0) {
|
||||
SliceMemory(cur, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Free allocated memory block and perform adjacent block merging
|
||||
/// </summary>
|
||||
/// <param name="handle">
|
||||
/// Handle to the allocated memory block
|
||||
/// <param name="collectPolicy">
|
||||
/// </param>
|
||||
public void Free(hlsfHandle handle, FreePolicy collectPolicy = FreePolicy.NoCollect)
|
||||
{
|
||||
Entry* e = handle.EntryHandle;
|
||||
if (e == null) {
|
||||
return;
|
||||
}
|
||||
if (e->State != EntryState.Alloc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as empty so it can be merged
|
||||
e->State = EntryState.Empty;
|
||||
|
||||
// Try merge with adjacent free blocks and remove them from buckets
|
||||
if (collectPolicy == FreePolicy.LazyCollect)
|
||||
{
|
||||
MergeAndRemoveFromBuckets(e);
|
||||
}
|
||||
|
||||
// Calculate final level and segment for the merged block
|
||||
long finalSize = e->Size;
|
||||
int finalLevel = GetLevelFromSize(finalSize);
|
||||
e->Level = finalLevel;
|
||||
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
|
||||
|
||||
// Push the merged entry back to appropriate bucket
|
||||
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
||||
|
||||
// Console.WriteLine("one block freed");
|
||||
if (collectPolicy == FreePolicy.FullCollect)
|
||||
{
|
||||
Collect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +1,92 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Level"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private const int MaxLevel = 9;
|
||||
|
||||
private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216];
|
||||
|
||||
public int AlignOfLevel(int bucketLevel)
|
||||
{
|
||||
int blockSize = _levelSizeArray[bucketLevel];
|
||||
return int.Min(blockSize, _align);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(long size)
|
||||
{
|
||||
/*
|
||||
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, ......]
|
||||
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
|
||||
*
|
||||
* For size S, we need: 2^(6+2*level) >= S
|
||||
* So: 6+2*level >= log2(S)
|
||||
* Therefore: level >= (log2(S) - 6) / 2
|
||||
*
|
||||
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
|
||||
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
|
||||
*/
|
||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
||||
|
||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
||||
int level = Math.Max(0, (log2Size - 6) / 2);
|
||||
|
||||
// caller is responsible for ensuring level is within bounds
|
||||
return level;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(uint size)
|
||||
{
|
||||
return GetLevelFromSize(size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum CommonMemoryAlign : int
|
||||
{
|
||||
|
||||
XMMLevel = 16,
|
||||
YMMLevel = 32,
|
||||
ZMMLevel = 64,
|
||||
|
||||
ArmNEONLevel = 16,
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Level"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
private const int MaxLevel = 9;
|
||||
|
||||
private int[] _levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216];
|
||||
|
||||
public int AlignOfLevel(int bucketLevel)
|
||||
{
|
||||
int blockSize = _levelSizeArray[bucketLevel];
|
||||
return int.Min(blockSize, _align);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(long size)
|
||||
{
|
||||
/*
|
||||
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, ......]
|
||||
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
|
||||
*
|
||||
* For size S, we need: 2^(6+2*level) >= S
|
||||
* So: 6+2*level >= log2(S)
|
||||
* Therefore: level >= (log2(S) - 6) / 2
|
||||
*
|
||||
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
|
||||
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
|
||||
*/
|
||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
||||
|
||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
||||
int level = Math.Max(0, (log2Size - 6) / 2);
|
||||
|
||||
// caller is responsible for ensuring level is within bounds
|
||||
return level;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(uint size)
|
||||
{
|
||||
return GetLevelFromSize(size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum CommonMemoryAlign : int
|
||||
{
|
||||
|
||||
XMMLevel = 16,
|
||||
YMMLevel = 32,
|
||||
ZMMLevel = 64,
|
||||
|
||||
ArmNEONLevel = 16,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,208 +1,208 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.SegmentEntry"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Check if two Entries are adjacent in memory
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool AssertAdjacentTo(Entry* current, Entry* other)
|
||||
{
|
||||
// Only a predicate; never throw here. Callers rely on false to skip merging.
|
||||
return other->Position + (nuint)other->Size == current->Position ||
|
||||
current->Position + (nuint)current->Size == other->Position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
||||
/// </summary>
|
||||
private bool AssertBoundary(Entry* previous, Entry* current, int level)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
int nextLevelSize = _levelSizeArray[level] * 4;
|
||||
ulong prevHead = previous->Position - (ulong)m_Buffer,
|
||||
currHead = current->Position - (ulong)m_Buffer;
|
||||
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine segment index based on size and originLevel
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
return (int)((size - 1) / levelSizeArray[level]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryAfter(Entry* entry, Entry* position)
|
||||
{
|
||||
Entry* next = position->NextNear;
|
||||
entry->NextNear = next;
|
||||
entry->PreviousNear = position;
|
||||
next->PreviousNear = entry;
|
||||
position->NextNear = entry;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryBefore(Entry* entry, Entry* position)
|
||||
{
|
||||
Entry* prev = position->PreviousNear;
|
||||
entry->NextNear = position;
|
||||
entry->PreviousNear = prev;
|
||||
prev->NextNear = entry;
|
||||
position->PreviousNear = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
|
||||
/// Current Entry will serve as the final merged block. The Merge will be in lazy
|
||||
/// strategy.
|
||||
/// </summary>
|
||||
private void MergeAndRemoveFromBuckets(Entry* entry)
|
||||
{
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
long size = entry->Size;
|
||||
int originLevel = entry->Level;
|
||||
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
|
||||
do
|
||||
{
|
||||
// previous direction merge
|
||||
Entry* p = entry->PreviousNear;
|
||||
if (p == null ||
|
||||
p == _spareMemory ||
|
||||
p->State != EntryState.Empty ||
|
||||
p->Level != entry->Level ||
|
||||
!AssertBoundary(p, entry, originLevel))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
|
||||
entry->Size += p->Size;
|
||||
entry->Position = p->Position;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
|
||||
RemoveFromPositionChain(p);
|
||||
} while (true);
|
||||
|
||||
do
|
||||
{
|
||||
// next direction merge
|
||||
Entry* n = entry->NextNear;
|
||||
if (n == null ||
|
||||
n == _spareMemory ||
|
||||
n->State != EntryState.Empty ||
|
||||
n->Level != entry->Level ||
|
||||
!AssertBoundary(n, entry, originLevel))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
|
||||
entry->Size += n->Size;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
|
||||
RemoveFromPositionChain(n);
|
||||
} while (true);
|
||||
originLevel = int.Min(9, originLevel);
|
||||
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
|
||||
entry->Level &= 15;
|
||||
|
||||
BucketNode* bucket = (BucketNode*)entry->Position;
|
||||
bucket->EntryRef = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove node from position linked list, this method does not recycle a BucketNode from
|
||||
/// size bucket
|
||||
/// </summary>
|
||||
private void RemoveFromPositionChain(Entry* entry)
|
||||
{
|
||||
Entry* prev = entry->PreviousNear;
|
||||
Entry* next = entry->NextNear;
|
||||
|
||||
prev->NextNear = next;
|
||||
next->PreviousNear = prev;
|
||||
|
||||
// Clear node pointers
|
||||
entry->NextNear = null;
|
||||
entry->PreviousNear = null;
|
||||
|
||||
// Console.WriteLine("entry recycled.");
|
||||
_entries.Free((nuint)entry);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void SetEntryLevel(Entry* entry, nuint position, long size)
|
||||
{
|
||||
entry ->Position = position;
|
||||
entry->Size = size;
|
||||
entry->Level = GetLevelFromSize(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry structure - streamlined version (only position and state information) Stored in
|
||||
/// external SLAB
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 40)]
|
||||
internal struct Entry
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
|
||||
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
||||
|
||||
[FieldOffset(24)] internal int Level; // Level of the block in the allocator
|
||||
[FieldOffset(16)] internal long Size; // Block size information
|
||||
[FieldOffset(36)] public EntryState State;
|
||||
[FieldOffset(28)] public nuint Position; // Memory block position
|
||||
|
||||
}
|
||||
|
||||
internal enum EntryState : uint
|
||||
{
|
||||
|
||||
Spare,
|
||||
Empty,
|
||||
Alloc,
|
||||
Large
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.SegmentEntry"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Check if two Entries are adjacent in memory
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool AssertAdjacentTo(Entry* current, Entry* other)
|
||||
{
|
||||
// Only a predicate; never throw here. Callers rely on false to skip merging.
|
||||
return other->Position + (nuint)other->Size == current->Position ||
|
||||
current->Position + (nuint)current->Size == other->Position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
||||
/// </summary>
|
||||
private bool AssertBoundary(Entry* previous, Entry* current, int level)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
int nextLevelSize = _levelSizeArray[level] * 4;
|
||||
ulong prevHead = previous->Position - (ulong)m_Buffer,
|
||||
currHead = current->Position - (ulong)m_Buffer;
|
||||
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine segment index based on size and originLevel
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int DetermineSegmentIndex(int[] levelSizeArray, long size, int level)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
return (int)((size - 1) / levelSizeArray[level]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryAfter(Entry* entry, Entry* position)
|
||||
{
|
||||
Entry* next = position->NextNear;
|
||||
entry->NextNear = next;
|
||||
entry->PreviousNear = position;
|
||||
next->PreviousNear = entry;
|
||||
position->NextNear = entry;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryBefore(Entry* entry, Entry* position)
|
||||
{
|
||||
Entry* prev = position->PreviousNear;
|
||||
entry->NextNear = position;
|
||||
entry->PreviousNear = prev;
|
||||
prev->NextNear = entry;
|
||||
position->PreviousNear = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
|
||||
/// Current Entry will serve as the final merged block. The Merge will be in lazy
|
||||
/// strategy.
|
||||
/// </summary>
|
||||
private void MergeAndRemoveFromBuckets(Entry* entry)
|
||||
{
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
long size = entry->Size;
|
||||
int originLevel = entry->Level;
|
||||
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
|
||||
do
|
||||
{
|
||||
// previous direction merge
|
||||
Entry* p = entry->PreviousNear;
|
||||
if (p == null ||
|
||||
p == _spareMemory ||
|
||||
p->State != EntryState.Empty ||
|
||||
p->Level != entry->Level ||
|
||||
!AssertBoundary(p, entry, originLevel))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
|
||||
entry->Size += p->Size;
|
||||
entry->Position = p->Position;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
|
||||
RemoveFromPositionChain(p);
|
||||
} while (true);
|
||||
|
||||
do
|
||||
{
|
||||
// next direction merge
|
||||
Entry* n = entry->NextNear;
|
||||
if (n == null ||
|
||||
n == _spareMemory ||
|
||||
n->State != EntryState.Empty ||
|
||||
n->Level != entry->Level ||
|
||||
!AssertBoundary(n, entry, originLevel))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
|
||||
entry->Size += n->Size;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
|
||||
RemoveFromPositionChain(n);
|
||||
} while (true);
|
||||
originLevel = int.Min(9, originLevel);
|
||||
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
|
||||
entry->Level &= 15;
|
||||
|
||||
BucketNode* bucket = (BucketNode*)entry->Position;
|
||||
bucket->EntryRef = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove node from position linked list, this method does not recycle a BucketNode from
|
||||
/// size bucket
|
||||
/// </summary>
|
||||
private void RemoveFromPositionChain(Entry* entry)
|
||||
{
|
||||
Entry* prev = entry->PreviousNear;
|
||||
Entry* next = entry->NextNear;
|
||||
|
||||
prev->NextNear = next;
|
||||
next->PreviousNear = prev;
|
||||
|
||||
// Clear node pointers
|
||||
entry->NextNear = null;
|
||||
entry->PreviousNear = null;
|
||||
|
||||
// Console.WriteLine("entry recycled.");
|
||||
_entries.Free((nuint)entry);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void SetEntryLevel(Entry* entry, nuint position, long size)
|
||||
{
|
||||
entry ->Position = position;
|
||||
entry->Size = size;
|
||||
entry->Level = GetLevelFromSize(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry structure - streamlined version (only position and state information) Stored in
|
||||
/// external SLAB
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 40)]
|
||||
internal struct Entry
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
|
||||
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
||||
|
||||
[FieldOffset(24)] internal int Level; // Level of the block in the allocator
|
||||
[FieldOffset(16)] internal long Size; // Block size information
|
||||
[FieldOffset(36)] public EntryState State;
|
||||
[FieldOffset(28)] public nuint Position; // Memory block position
|
||||
|
||||
}
|
||||
|
||||
internal enum EntryState : uint
|
||||
{
|
||||
|
||||
Spare,
|
||||
Empty,
|
||||
Alloc,
|
||||
Large
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,178 +1,178 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
|
||||
{
|
||||
|
||||
// Bucket system field - directly in main allocator class
|
||||
private readonly BucketNode *[] _bucketHeads;
|
||||
|
||||
// _spareMemory is also the beginning position of allocator
|
||||
private Entry *_spareMemory;
|
||||
private readonly byte* m_Buffer;
|
||||
private bool disposedValue;
|
||||
private readonly int _align;
|
||||
private readonly long _maxSize;
|
||||
private readonly nuint _size;
|
||||
private readonly SlabAllocator<Entry> _entries;
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize)
|
||||
{
|
||||
if (byteSize < 64) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
_size = byteSize;
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
_align = 16;
|
||||
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Spare;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
|
||||
{
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
_align = 16;
|
||||
_size = byteSize;
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[(levelCount * 3) + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing:true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing) {
|
||||
_entries.Dispose();
|
||||
}
|
||||
NativeMemory.AlignedFree(m_Buffer);
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~HybridLayerSegregatedFitAllocator()
|
||||
{
|
||||
Dispose(disposing:false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
|
||||
{
|
||||
|
||||
// Bucket system field - directly in main allocator class
|
||||
private readonly BucketNode *[] _bucketHeads;
|
||||
|
||||
// _spareMemory is also the beginning position of allocator
|
||||
private Entry *_spareMemory;
|
||||
private readonly byte* m_Buffer;
|
||||
private bool disposedValue;
|
||||
private readonly int _align;
|
||||
private readonly long _maxSize;
|
||||
private readonly nuint _size;
|
||||
private readonly SlabAllocator<Entry> _entries;
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize)
|
||||
{
|
||||
if (byteSize < 64) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
_size = byteSize;
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
_align = 16;
|
||||
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Spare;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
|
||||
{
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
_align = 16;
|
||||
_size = byteSize;
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[(levelCount * 3) + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align, long maxBlockSize)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing:true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing) {
|
||||
_entries.Dispose();
|
||||
}
|
||||
NativeMemory.AlignedFree(m_Buffer);
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~HybridLayerSegregatedFitAllocator()
|
||||
{
|
||||
Dispose(disposing:false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,137 +1,137 @@
|
||||
# HLSF (Hybrid Layer Segregated Fit) Allocator
|
||||
|
||||
A constant-time (O(1)) allocator for real-time, high-throughput scenarios. It uses layered size classes with segmented buckets and stores all metadata externally for clean user memory and better cache locality.
|
||||
|
||||
## Key Points
|
||||
|
||||
- O(1) allocate/free with 10 levels: 64B x 16MB; each level split into 1x/2x/3x segments
|
||||
- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
|
||||
- Configurable alignment (>=16B); maximum block size is configurable at construction time via the `maxBlockSize` parameter (default 32MB, 10 levels). The level array and bucket table are dynamically extended when a larger `maxBlockSize` is specified. There is no implicit fallback to `NativeMemory` for oversized requests.
|
||||
- Single-threaded implementation (external sync required for multi-threaded use)
|
||||
|
||||
## Quick Use
|
||||
|
||||
```csharp
|
||||
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
|
||||
var h = hlsf.Alloc(1024);
|
||||
Span<byte> s = h.BufferHandle; s[0] = 42;
|
||||
hlsf.Free(h);
|
||||
```
|
||||
|
||||
## Core APIs
|
||||
|
||||
### Alloc(uint size)
|
||||
|
||||
- Purpose: Allocate a block of at least `size` bytes.
|
||||
- Input: `size` in bytes. Must not exceed the `maxBlockSize` configured at construction time (default 32MB). If `size > _maxSize`, an `InvalidOperationException` is thrown.
|
||||
- Output: `HybridLayerSegregatedFitAllocationHandle` with pointer access and `Span<byte>`.
|
||||
- Behavior:
|
||||
- Classify into level/segment and pop a matching bucket; escalate segments and levels if empty.
|
||||
- Fallback to overflow bucket or carve 32MB from spare memory if all buckets are empty.
|
||||
- Mark selected `Entry` as Alloc; slice any remainder into `Empty` entries and push to buckets.
|
||||
- There is no implicit fallback to `NativeMemory.AlignedAlloc` for oversized requests — all allocations are served from the pre-allocated arena. The level array and bucket table are dynamically sized based on the `maxBlockSize` parameter.
|
||||
|
||||
### Free(HybridLayerSegregatedFitAllocationHandle handle)
|
||||
|
||||
- Purpose: Free a previously allocated block.
|
||||
- Behavior:
|
||||
- Mark the corresponding `Entry` as `Empty`.
|
||||
- Merge with adjacent `Empty` entries in both directions (boundary and level constraints).
|
||||
- Remove merged neighbors from buckets, update position chain, and optionally promote level.
|
||||
- Re-enqueue the final `Entry` into the appropriate bucket.
|
||||
|
||||
### Collect()
|
||||
|
||||
- Purpose: Reclaim and rebalance free entries without touching allocated blocks.
|
||||
- Behavior:
|
||||
- Consolidate fragmented regions where possible and improve bucket coverage.
|
||||
- Intended to be cheap; safe to call between benchmark phases.
|
||||
|
||||
### BenchmarkDotNet (HLSF vs Libc)
|
||||
|
||||
Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H.
|
||||
|
||||
| Method | Mean | StdDev | Ratio | Allocated |
|
||||
|------- |-----:|------:|------:|----------:|
|
||||
| Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B |
|
||||
| Libc | 849.8 µs | 100.2 µs | 1.01 | 64 B |
|
||||
|
||||
Interpretation: HLSF matches libc allocation throughput under the same workload, with slightly tighter dispersion in this run.
|
||||
|
||||
> Note: BenchmarkDotNet also warned that the minimum iteration time was under 1ms; for more stable statistics, increase per-iteration work (e.g., higher `AllocationCount`).
|
||||
|
||||
## Detailed Design (summary)
|
||||
-
|
||||
- Size classes: The default level size array is [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216] (10 levels, 64B × 4^level). When `maxBlockSize` is specified at construction and exceeds the default range, additional levels are appended (each 4× the previous) to cover the requested maximum block size.
|
||||
- Segments per level: 1x, 2x, 3x
|
||||
- Buckets: index = 3 * level + segment (overflow bucket for level > 9)
|
||||
- Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain)
|
||||
- Position chain: circular doubly linked list anchored by spare sentinel
|
||||
- Allocation: search buckets by level/segment with escalation; if none, carve 32MB from spare; set Alloc; slice remainder into buckets
|
||||
- Slicing: align to level boundaries, pack contiguous blocks forward/backward to minimize overhead
|
||||
- Free/Merge: bidirectional merge under boundary and level constraints; update buckets; optional level promotion
|
||||
- Alignment: default 16B; internal blocks aligned by slicing/edge calculation
|
||||
- Complexity: average O(1) with bounded levels and constant-time bucket ops
|
||||
|
||||
- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State`
|
||||
- Invariants: near neighbors must form a consistent double-linked ring; `Level` setter validates size vs. level window
|
||||
- `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
|
||||
- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
|
||||
|
||||
### Allocation Flow
|
||||
|
||||
1) Compute `level` and `segment` from requested `size`
|
||||
2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
|
||||
- Advance `segment` 0��1��2, then `level++`, until `MaxLevel`
|
||||
- If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock`
|
||||
3) On success: bound-check the slice offset; set `entry` to `Alloc` using `SetEntryLevel`
|
||||
4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
|
||||
|
||||
### Slicing Logic (`SliceMemory`)
|
||||
|
||||
- Inputs: `next` entry position, `sizeToSlice`
|
||||
- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`:
|
||||
- `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `�� MaxLevel`
|
||||
- Three phases to cover [offset, end):
|
||||
1) Forward levels i..N-1: for each level size `size` and `upper=size*4`, align to `edge=((offset+upper-1)/upper)*upper`, pack `count = ((min(edge,end) - offset) / size)` blocks
|
||||
2) If `i > MaxLevel`: pack in largest upper window on tail
|
||||
3) Backward i-1..0: fill remaining tail with smaller sizes
|
||||
- For each packed region: allocate new `Entry`, insert before `next`, mark `Empty`, and push corresponding `BucketNode` into the `(level, segment)` bucket (`PushBucketToLevel(i, count-1, b)`)
|
||||
|
||||
### Free and Merge (`MergeAndRemoveFromBuckets`)
|
||||
|
||||
- Given an `Entry` to free (turned `Empty` by caller):
|
||||
- Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
|
||||
- Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
|
||||
- Merge right with neighbor `n` by the same rule
|
||||
- Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4`
|
||||
- Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
|
||||
|
||||
### Buckets
|
||||
|
||||
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
|
||||
- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false
|
||||
- `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
|
||||
|
||||
### Large Allocations and Spare Memory
|
||||
|
||||
- Requests exceeding the configured `maxBlockSize` are rejected with `InvalidOperationException`. All allocations are served from the pre-allocated arena.
|
||||
- When all buckets are empty, `TryAllocateNew32MBBlock` carves a region (up to 32MB) from `_spareMemory` to replenish the bucket system, builds an `Empty` entry, and advances the spare pointer
|
||||
|
||||
### Alignment
|
||||
|
||||
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
|
||||
- Internal blocks align to level boundaries by design of slicing/edge calculations
|
||||
|
||||
### Complexity and Invariants
|
||||
|
||||
- Allocate/free are O(1) on average with bounded levels (��10) and constant-time bucket ops
|
||||
- Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
|
||||
|
||||
## Profiling (hotspots, current workloads)
|
||||
|
||||
- SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path
|
||||
- MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path
|
||||
- Collect ~7.6% (self ~7.5%) �� low overall cost
|
||||
|
||||
# HLSF (Hybrid Layer Segregated Fit) Allocator
|
||||
|
||||
A constant-time (O(1)) allocator for real-time, high-throughput scenarios. It uses layered size classes with segmented buckets and stores all metadata externally for clean user memory and better cache locality.
|
||||
|
||||
## Key Points
|
||||
|
||||
- O(1) allocate/free with 10 levels: 64B x 16MB; each level split into 1x/2x/3x segments
|
||||
- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
|
||||
- Configurable alignment (>=16B); maximum block size is configurable at construction time via the `maxBlockSize` parameter (default 32MB, 10 levels). The level array and bucket table are dynamically extended when a larger `maxBlockSize` is specified. There is no implicit fallback to `NativeMemory` for oversized requests.
|
||||
- Single-threaded implementation (external sync required for multi-threaded use)
|
||||
|
||||
## Quick Use
|
||||
|
||||
```csharp
|
||||
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
|
||||
var h = hlsf.Alloc(1024);
|
||||
Span<byte> s = h.BufferHandle; s[0] = 42;
|
||||
hlsf.Free(h);
|
||||
```
|
||||
|
||||
## Core APIs
|
||||
|
||||
### Alloc(uint size)
|
||||
|
||||
- Purpose: Allocate a block of at least `size` bytes.
|
||||
- Input: `size` in bytes. Must not exceed the `maxBlockSize` configured at construction time (default 32MB). If `size > _maxSize`, an `InvalidOperationException` is thrown.
|
||||
- Output: `HybridLayerSegregatedFitAllocationHandle` with pointer access and `Span<byte>`.
|
||||
- Behavior:
|
||||
- Classify into level/segment and pop a matching bucket; escalate segments and levels if empty.
|
||||
- Fallback to overflow bucket or carve 32MB from spare memory if all buckets are empty.
|
||||
- Mark selected `Entry` as Alloc; slice any remainder into `Empty` entries and push to buckets.
|
||||
- There is no implicit fallback to `NativeMemory.AlignedAlloc` for oversized requests — all allocations are served from the pre-allocated arena. The level array and bucket table are dynamically sized based on the `maxBlockSize` parameter.
|
||||
|
||||
### Free(HybridLayerSegregatedFitAllocationHandle handle)
|
||||
|
||||
- Purpose: Free a previously allocated block.
|
||||
- Behavior:
|
||||
- Mark the corresponding `Entry` as `Empty`.
|
||||
- Merge with adjacent `Empty` entries in both directions (boundary and level constraints).
|
||||
- Remove merged neighbors from buckets, update position chain, and optionally promote level.
|
||||
- Re-enqueue the final `Entry` into the appropriate bucket.
|
||||
|
||||
### Collect()
|
||||
|
||||
- Purpose: Reclaim and rebalance free entries without touching allocated blocks.
|
||||
- Behavior:
|
||||
- Consolidate fragmented regions where possible and improve bucket coverage.
|
||||
- Intended to be cheap; safe to call between benchmark phases.
|
||||
|
||||
### BenchmarkDotNet (HLSF vs Libc)
|
||||
|
||||
Environment: .NET 8.0.23 (Release), x64 RyuJIT AVX2, Windows 11, AMD Ryzen 7 6800H.
|
||||
|
||||
| Method | Mean | StdDev | Ratio | Allocated |
|
||||
|------- |-----:|------:|------:|----------:|
|
||||
| Hlsf | 838.4 µs | 40.6 µs | 1.00 | 64 B |
|
||||
| Libc | 849.8 µs | 100.2 µs | 1.01 | 64 B |
|
||||
|
||||
Interpretation: HLSF matches libc allocation throughput under the same workload, with slightly tighter dispersion in this run.
|
||||
|
||||
> Note: BenchmarkDotNet also warned that the minimum iteration time was under 1ms; for more stable statistics, increase per-iteration work (e.g., higher `AllocationCount`).
|
||||
|
||||
## Detailed Design (summary)
|
||||
-
|
||||
- Size classes: The default level size array is [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216] (10 levels, 64B × 4^level). When `maxBlockSize` is specified at construction and exceeds the default range, additional levels are appended (each 4× the previous) to cover the requested maximum block size.
|
||||
- Segments per level: 1x, 2x, 3x
|
||||
- Buckets: index = 3 * level + segment (overflow bucket for level > 9)
|
||||
- Metadata: Entry (external SLAB, position chain), BucketNode (user region, bucket chain)
|
||||
- Position chain: circular doubly linked list anchored by spare sentinel
|
||||
- Allocation: search buckets by level/segment with escalation; if none, carve 32MB from spare; set Alloc; slice remainder into buckets
|
||||
- Slicing: align to level boundaries, pack contiguous blocks forward/backward to minimize overhead
|
||||
- Free/Merge: bidirectional merge under boundary and level constraints; update buckets; optional level promotion
|
||||
- Alignment: default 16B; internal blocks aligned by slicing/edge calculation
|
||||
- Complexity: average O(1) with bounded levels and constant-time bucket ops
|
||||
|
||||
- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State`
|
||||
- Invariants: near neighbors must form a consistent double-linked ring; `Level` setter validates size vs. level window
|
||||
- `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
|
||||
- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
|
||||
|
||||
### Allocation Flow
|
||||
|
||||
1) Compute `level` and `segment` from requested `size`
|
||||
2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
|
||||
- Advance `segment` 0��1��2, then `level++`, until `MaxLevel`
|
||||
- If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock`
|
||||
3) On success: bound-check the slice offset; set `entry` to `Alloc` using `SetEntryLevel`
|
||||
4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
|
||||
|
||||
### Slicing Logic (`SliceMemory`)
|
||||
|
||||
- Inputs: `next` entry position, `sizeToSlice`
|
||||
- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`:
|
||||
- `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `�� MaxLevel`
|
||||
- Three phases to cover [offset, end):
|
||||
1) Forward levels i..N-1: for each level size `size` and `upper=size*4`, align to `edge=((offset+upper-1)/upper)*upper`, pack `count = ((min(edge,end) - offset) / size)` blocks
|
||||
2) If `i > MaxLevel`: pack in largest upper window on tail
|
||||
3) Backward i-1..0: fill remaining tail with smaller sizes
|
||||
- For each packed region: allocate new `Entry`, insert before `next`, mark `Empty`, and push corresponding `BucketNode` into the `(level, segment)` bucket (`PushBucketToLevel(i, count-1, b)`)
|
||||
|
||||
### Free and Merge (`MergeAndRemoveFromBuckets`)
|
||||
|
||||
- Given an `Entry` to free (turned `Empty` by caller):
|
||||
- Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
|
||||
- Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
|
||||
- Merge right with neighbor `n` by the same rule
|
||||
- Optionally promote `Level` if `Size �� levelSizeArray[originLevel] * 4`
|
||||
- Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
|
||||
|
||||
### Buckets
|
||||
|
||||
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
|
||||
- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false
|
||||
- `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
|
||||
|
||||
### Large Allocations and Spare Memory
|
||||
|
||||
- Requests exceeding the configured `maxBlockSize` are rejected with `InvalidOperationException`. All allocations are served from the pre-allocated arena.
|
||||
- When all buckets are empty, `TryAllocateNew32MBBlock` carves a region (up to 32MB) from `_spareMemory` to replenish the bucket system, builds an `Empty` entry, and advances the spare pointer
|
||||
|
||||
### Alignment
|
||||
|
||||
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
|
||||
- Internal blocks align to level boundaries by design of slicing/edge calculations
|
||||
|
||||
### Complexity and Invariants
|
||||
|
||||
- Allocate/free are O(1) on average with bounded levels (��10) and constant-time bucket ops
|
||||
- Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
|
||||
|
||||
## Profiling (hotspots, current workloads)
|
||||
|
||||
- SliceMemory ~22% (self ~17%) �� slicing and bucket enqueue dominate allocation path
|
||||
- MergeAndRemoveFromBuckets ~13% (self ~12%) �� merge + removal dominate free path
|
||||
- Collect ~7.6% (self ~7.5%) �� low overall cost
|
||||
|
||||
Focus: reduce per-slice math and bucket ops; add fast-paths for aligned regions; batch/lazy removals after merges.
|
||||
@@ -1,56 +1,56 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocationHandle"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private readonly SlabAllocator<T> _allocator;
|
||||
|
||||
internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked)
|
||||
{
|
||||
_allocator = allocator;
|
||||
Locked = locked;
|
||||
}
|
||||
|
||||
public bool Locked { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
_allocator.ExitBufferReading();
|
||||
Locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocationHandle"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public unsafe struct SlabAllocationHandle<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
private readonly SlabAllocator<T> _allocator;
|
||||
|
||||
internal SlabAllocationHandle(SlabAllocator<T> allocator, bool locked)
|
||||
{
|
||||
_allocator = allocator;
|
||||
Locked = locked;
|
||||
}
|
||||
|
||||
public bool Locked { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
_allocator.ExitBufferReading();
|
||||
Locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +1,134 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.Chunked"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private BufferStack<nuint> _buffers;
|
||||
private BufferStack<nuint> _recycle;
|
||||
private readonly int _elementSize = Unsafe.SizeOf<T>();
|
||||
|
||||
private nuint _currentBuffer;
|
||||
private nuint _currentCapacity, _currentIndex;
|
||||
|
||||
public ChunkedSlabAllocator(nuint count)
|
||||
{
|
||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||
|
||||
_currentCapacity = count;
|
||||
int initCount = count switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)count
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||
}
|
||||
_buffers = new(4);
|
||||
_buffers.Push(_currentBuffer);
|
||||
_recycle = BufferStackBuilder.Create(span);
|
||||
_currentCapacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_recycle.TryPop(out nuint handle)) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
// nuint bufferHandle = _currentBuffer;
|
||||
nuint curIndex = _currentIndex;
|
||||
if (curIndex >= _currentCapacity)
|
||||
{
|
||||
nuint cap = _currentCapacity;
|
||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||
_buffers.Push(newBuffer);
|
||||
_currentBuffer = newBuffer;
|
||||
_currentIndex = 0;
|
||||
}
|
||||
curIndex = _currentIndex;
|
||||
_currentIndex += 1;
|
||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
|
||||
// NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer))
|
||||
{
|
||||
NativeMemory.Free((void*)buffer);
|
||||
}
|
||||
_buffers.Dispose();
|
||||
_recycle.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_recycle.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
return new SlabAllocationHandle<T>(this, false);
|
||||
}
|
||||
|
||||
~ChunkedSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.Chunked"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private BufferStack<nuint> _buffers;
|
||||
private BufferStack<nuint> _recycle;
|
||||
private readonly int _elementSize = Unsafe.SizeOf<T>();
|
||||
|
||||
private nuint _currentBuffer;
|
||||
private nuint _currentCapacity, _currentIndex;
|
||||
|
||||
public ChunkedSlabAllocator(nuint count)
|
||||
{
|
||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||
|
||||
_currentCapacity = count;
|
||||
int initCount = count switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)count
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||
}
|
||||
_buffers = new(4);
|
||||
_buffers.Push(_currentBuffer);
|
||||
_recycle = BufferStackBuilder.Create(span);
|
||||
_currentCapacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_recycle.TryPop(out nuint handle)) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
// nuint bufferHandle = _currentBuffer;
|
||||
nuint curIndex = _currentIndex;
|
||||
if (curIndex >= _currentCapacity)
|
||||
{
|
||||
nuint cap = _currentCapacity;
|
||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||
_buffers.Push(newBuffer);
|
||||
_currentBuffer = newBuffer;
|
||||
_currentIndex = 0;
|
||||
}
|
||||
curIndex = _currentIndex;
|
||||
_currentIndex += 1;
|
||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
|
||||
// NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer))
|
||||
{
|
||||
NativeMemory.Free((void*)buffer);
|
||||
}
|
||||
_buffers.Dispose();
|
||||
_recycle.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_recycle.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
return new SlabAllocationHandle<T>(this, false);
|
||||
}
|
||||
|
||||
~ChunkedSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,157 +1,157 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.ChunkedConcurrent"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private ConcurrentBufferStack<nuint> _buffers;
|
||||
private ConcurrentBufferStack<nuint> _recycle;
|
||||
private readonly int _elementSize = Unsafe.SizeOf<T>();
|
||||
|
||||
private nuint _currentBuffer;
|
||||
private nuint _currentCapacity, _currentIndex;
|
||||
|
||||
public ConcurrentChunkedSlabAllocator(nuint count)
|
||||
{
|
||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||
_currentCapacity = count;
|
||||
int initCount = count switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)count
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||
}
|
||||
_buffers = new(4);
|
||||
_recycle = BufferStackBuilder.CreateConcurrent(span);
|
||||
_currentCapacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_recycle.TryPop(out nuint handle)) {
|
||||
return handle;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
nuint bufferHandle = Volatile.Read(ref _currentBuffer);
|
||||
nuint curIndex = Volatile.Read(ref _currentIndex);
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
if (curIndex >= _currentCapacity)
|
||||
{
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
nuint cap = Volatile.Read(ref _currentCapacity);
|
||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||
_buffers.Push(bufferHandle);
|
||||
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
|
||||
_ = Interlocked.Exchange(ref _currentIndex, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
curIndex = Volatile.Read(ref _currentIndex);
|
||||
_ = Interlocked.Add(
|
||||
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
|
||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer)) {
|
||||
NativeMemory.Free((void*)buffer);
|
||||
}
|
||||
_lock.Dispose();
|
||||
_buffers.Dispose();
|
||||
_recycle.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_recycle.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
return new SlabAllocationHandle<T>(this, false);
|
||||
}
|
||||
|
||||
~ConcurrentChunkedSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.ChunkedConcurrent"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
internal sealed unsafe class ConcurrentChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private ConcurrentBufferStack<nuint> _buffers;
|
||||
private ConcurrentBufferStack<nuint> _recycle;
|
||||
private readonly int _elementSize = Unsafe.SizeOf<T>();
|
||||
|
||||
private nuint _currentBuffer;
|
||||
private nuint _currentCapacity, _currentIndex;
|
||||
|
||||
public ConcurrentChunkedSlabAllocator(nuint count)
|
||||
{
|
||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||
_currentCapacity = count;
|
||||
int initCount = count switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)count
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||
}
|
||||
_buffers = new(4);
|
||||
_recycle = BufferStackBuilder.CreateConcurrent(span);
|
||||
_currentCapacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_recycle.TryPop(out nuint handle)) {
|
||||
return handle;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
nuint bufferHandle = Volatile.Read(ref _currentBuffer);
|
||||
nuint curIndex = Volatile.Read(ref _currentIndex);
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
if (curIndex >= _currentCapacity)
|
||||
{
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
nuint cap = Volatile.Read(ref _currentCapacity);
|
||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||
_buffers.Push(bufferHandle);
|
||||
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
|
||||
_ = Interlocked.Exchange(ref _currentIndex, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
curIndex = Volatile.Read(ref _currentIndex);
|
||||
_ = Interlocked.Add(
|
||||
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
|
||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer)) {
|
||||
NativeMemory.Free((void*)buffer);
|
||||
}
|
||||
_lock.Dispose();
|
||||
_buffers.Dispose();
|
||||
_recycle.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading() { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_recycle.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
return new SlabAllocationHandle<T>(this, false);
|
||||
}
|
||||
|
||||
~ConcurrentChunkedSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,165 +1,165 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.SequencialConcurrent"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private T* _buffer;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private readonly ConcurrentBufferStack<nuint> _stack;
|
||||
private readonly int _elementSize;
|
||||
private nuint _highestAllocated;
|
||||
private ulong _capacity;
|
||||
|
||||
public ConcurrentSequentialSlabAllocator(nuint initialCapacity)
|
||||
{
|
||||
_elementSize = Unsafe.SizeOf<T>();
|
||||
_capacity = initialCapacity;
|
||||
_highestAllocated = 0;
|
||||
|
||||
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
|
||||
int initCount = _capacity switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)_capacity / 4
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = (nuint)i;
|
||||
}
|
||||
_stack = BufferStackBuilder.CreateConcurrent(span);
|
||||
_highestAllocated += (nuint)initCount;
|
||||
}
|
||||
|
||||
internal T* BasePointer
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _buffer;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_stack.TryPop(out nuint index)) {
|
||||
return index;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
index = Volatile.Read(ref _highestAllocated);
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
if (index >= _capacity)
|
||||
{
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
_capacity = Volatile.Read(ref _capacity) * 2;
|
||||
nuint newSize = (nuint)_capacity * (nuint)_elementSize;
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
|
||||
return index;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
_stack.Dispose();
|
||||
NativeMemory.Free(_buffer);
|
||||
_lock.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading()
|
||||
{
|
||||
_lock.EnterBufferRead();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading()
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_stack.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
EnterBufferReading();
|
||||
return new SlabAllocationHandle<T>(this, true);
|
||||
}
|
||||
|
||||
~ConcurrentSequentialSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator.SequencialConcurrent"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Mathematics.Memory
|
||||
{
|
||||
public sealed unsafe class ConcurrentSequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
||||
{
|
||||
|
||||
private T* _buffer;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly BufferExpansionLock _lock = new();
|
||||
private readonly ConcurrentBufferStack<nuint> _stack;
|
||||
private readonly int _elementSize;
|
||||
private nuint _highestAllocated;
|
||||
private ulong _capacity;
|
||||
|
||||
public ConcurrentSequentialSlabAllocator(nuint initialCapacity)
|
||||
{
|
||||
_elementSize = Unsafe.SizeOf<T>();
|
||||
_capacity = initialCapacity;
|
||||
_highestAllocated = 0;
|
||||
|
||||
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
|
||||
int initCount = _capacity switch
|
||||
{
|
||||
<= 4 => 4,
|
||||
> 64 => 64,
|
||||
_ => (int)_capacity / 4
|
||||
};
|
||||
Span<nuint> span = stackalloc nuint[initCount];
|
||||
for (int i = 0; i < initCount; i++) {
|
||||
span[i] = (nuint)i;
|
||||
}
|
||||
_stack = BufferStackBuilder.CreateConcurrent(span);
|
||||
_highestAllocated += (nuint)initCount;
|
||||
}
|
||||
|
||||
internal T* BasePointer
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _buffer;
|
||||
}
|
||||
|
||||
public override nuint Allocate()
|
||||
{
|
||||
if (_stack.TryPop(out nuint index)) {
|
||||
return index;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
index = Volatile.Read(ref _highestAllocated);
|
||||
_lock.EnterMetaDataRead();
|
||||
try
|
||||
{
|
||||
if (index >= _capacity)
|
||||
{
|
||||
_lock.ExitMetaDataRead();
|
||||
_lock.EnterExpansionProcess();
|
||||
try
|
||||
{
|
||||
_capacity = Volatile.Read(ref _capacity) * 2;
|
||||
nuint newSize = (nuint)_capacity * (nuint)_elementSize;
|
||||
_lock.EnterBufferCopy();
|
||||
try
|
||||
{
|
||||
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitBufferCopy();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.ExitExpansionProcess();
|
||||
}
|
||||
}
|
||||
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
|
||||
return index;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_lock.IsThreadReadingMetaData) {
|
||||
_lock.ExitMetaDataRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_disposed) {
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
_stack.Dispose();
|
||||
NativeMemory.Free(_buffer);
|
||||
_lock.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void EnterBufferReading()
|
||||
{
|
||||
_lock.EnterBufferRead();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void ExitBufferReading()
|
||||
{
|
||||
_lock.ExitBufferRead();
|
||||
}
|
||||
|
||||
public override void Free(nuint index)
|
||||
{
|
||||
_stack.Push(index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override SlabAllocationHandle<T> ReadAllocation()
|
||||
{
|
||||
EnterBufferReading();
|
||||
return new SlabAllocationHandle<T>(this, true);
|
||||
}
|
||||
|
||||
~ConcurrentSequentialSlabAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,118 +1,118 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics.Memory
|
||||
{
|
||||
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
public abstract nuint Allocate();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new SLAB allocator instance with the specified configuration.
|
||||
/// </summary>
|
||||
/// <param name="count">
|
||||
/// Initial number of objects that can be allocated.
|
||||
/// </param>
|
||||
/// <param name="layout">
|
||||
/// Memory layout strategy for the SLAB allocator.
|
||||
/// </param>
|
||||
/// <param name="concurrentSupport">
|
||||
/// Whether to enable concurrent access support.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A new SLAB allocator instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when an invalid layout is specified.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Thrown when requesting unsupported configurations.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
|
||||
/// /// allocators themselves may be designed for single-threaded use, allocated objects
|
||||
/// are /// frequently shared across multiple threads in real-world applications. Due to
|
||||
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
|
||||
/// they rely /// on internal expansion locks to ensure that memory buffer expansions
|
||||
/// (which involve /// copying the entire buffer to a new location) do not occur while
|
||||
/// other threads are /// reading from allocated regions. </para> <para><strong>
|
||||
/// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators
|
||||
/// cannot provide a single-threaded /// version because:<list
|
||||
/// type="bullet"><item><description>They use contiguous memory /// layout that requires
|
||||
/// buffer expansion when capacity is exceeded
|
||||
/// ///</description></item><item><description>Buffer expansion involves copying all ///
|
||||
/// existing data to a new memory location</description></item><item><description> ///
|
||||
/// Without proper synchronization, concurrent access to allocated objects during ///
|
||||
/// expansion would cause memory corruption</description></item><item><description>The
|
||||
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
|
||||
/// threaded </description></item></list></para> <para><strong>Chunked Layout:
|
||||
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
|
||||
/// /// versions since they don't require buffer expansion and copying. Each chunk is
|
||||
/// /// allocated independently, avoiding the memory relocation issues present in
|
||||
/// sequential /// layouts. </para>
|
||||
/// </remarks>
|
||||
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
|
||||
{
|
||||
if (concurrentSupport) {
|
||||
return layout switch
|
||||
{
|
||||
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
|
||||
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
|
||||
};
|
||||
}
|
||||
return layout switch
|
||||
{
|
||||
SlabBufferLayout.Sequential => throw new NotSupportedException("Non-concurrent version of sequential layout SLAB is not supported due to expansion lock requirements for memory safety during buffer expansion."),
|
||||
SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
|
||||
};
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract void EnterBufferReading();
|
||||
|
||||
public abstract void ExitBufferReading();
|
||||
|
||||
public abstract void Free(nuint index);
|
||||
|
||||
public abstract SlabAllocationHandle<T> ReadAllocation();
|
||||
|
||||
}
|
||||
|
||||
public enum SlabBufferLayout
|
||||
{
|
||||
|
||||
Sequential,
|
||||
Chunked
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SlabAllocator"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics.Memory
|
||||
{
|
||||
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
|
||||
{
|
||||
|
||||
public abstract nuint Allocate();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new SLAB allocator instance with the specified configuration.
|
||||
/// </summary>
|
||||
/// <param name="count">
|
||||
/// Initial number of objects that can be allocated.
|
||||
/// </param>
|
||||
/// <param name="layout">
|
||||
/// Memory layout strategy for the SLAB allocator.
|
||||
/// </param>
|
||||
/// <param name="concurrentSupport">
|
||||
/// Whether to enable concurrent access support.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A new SLAB allocator instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when an invalid layout is specified.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Thrown when requesting unsupported configurations.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
|
||||
/// /// allocators themselves may be designed for single-threaded use, allocated objects
|
||||
/// are /// frequently shared across multiple threads in real-world applications. Due to
|
||||
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
|
||||
/// they rely /// on internal expansion locks to ensure that memory buffer expansions
|
||||
/// (which involve /// copying the entire buffer to a new location) do not occur while
|
||||
/// other threads are /// reading from allocated regions. </para> <para><strong>
|
||||
/// Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB allocators
|
||||
/// cannot provide a single-threaded /// version because:<list
|
||||
/// type="bullet"><item><description>They use contiguous memory /// layout that requires
|
||||
/// buffer expansion when capacity is exceeded
|
||||
/// ///</description></item><item><description>Buffer expansion involves copying all ///
|
||||
/// existing data to a new memory location</description></item><item><description> ///
|
||||
/// Without proper synchronization, concurrent access to allocated objects during ///
|
||||
/// expansion would cause memory corruption</description></item><item><description>The
|
||||
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
|
||||
/// threaded </description></item></list></para> <para><strong>Chunked Layout:
|
||||
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
|
||||
/// /// versions since they don't require buffer expansion and copying. Each chunk is
|
||||
/// /// allocated independently, avoiding the memory relocation issues present in
|
||||
/// sequential /// layouts. </para>
|
||||
/// </remarks>
|
||||
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
|
||||
{
|
||||
if (concurrentSupport) {
|
||||
return layout switch
|
||||
{
|
||||
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
|
||||
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
|
||||
};
|
||||
}
|
||||
return layout switch
|
||||
{
|
||||
SlabBufferLayout.Sequential => throw new NotSupportedException("Non-concurrent version of sequential layout SLAB is not supported due to expansion lock requirements for memory safety during buffer expansion."),
|
||||
SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
|
||||
};
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract void EnterBufferReading();
|
||||
|
||||
public abstract void ExitBufferReading();
|
||||
|
||||
public abstract void Free(nuint index);
|
||||
|
||||
public abstract SlabAllocationHandle<T> ReadAllocation();
|
||||
|
||||
}
|
||||
|
||||
public enum SlabBufferLayout
|
||||
{
|
||||
|
||||
Sequential,
|
||||
Chunked
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +1,98 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IRegisterType"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
|
||||
/// reference access, broadcast initialization and element level access. Typical
|
||||
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
|
||||
/// </summary>
|
||||
|
||||
public interface IRegisterPresentation
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref
|
||||
/// name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// Writable reference to element 0 (lowest lane / lowest bits).
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Intended for treating the register as a contiguous block of elements. The returned reference
|
||||
/// must remain valid for the lifetime of the register and be identical to <see
|
||||
/// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
|
||||
/// actual register representation.
|
||||
/// </remarks>
|
||||
ref T AsRef<T>() where T: unmanaged, INumber<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <param name="value">
|
||||
/// Scalar value to replicate to every element.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Should map to a single broadcast/shuffle instruction where possible. After completion all
|
||||
/// lanes read back the same value.
|
||||
/// </remarks>
|
||||
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a writable reference to the element at the specified <paramref name="index" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <param name="index">
|
||||
/// Zero-based lane index (range defined by implementation).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Writable reference to the lane.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit
|
||||
/// bounds checks for performance. Caller is responsible for passing a valid index.
|
||||
/// </remarks>
|
||||
ref T Member<T>(int index) where T: unmanaged, INumber<T>;
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IRegisterType"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
|
||||
/// reference access, broadcast initialization and element level access. Typical
|
||||
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
|
||||
/// </summary>
|
||||
|
||||
public interface IRegisterPresentation
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref
|
||||
/// name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// Writable reference to element 0 (lowest lane / lowest bits).
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Intended for treating the register as a contiguous block of elements. The returned reference
|
||||
/// must remain valid for the lifetime of the register and be identical to <see
|
||||
/// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
|
||||
/// actual register representation.
|
||||
/// </remarks>
|
||||
ref T AsRef<T>() where T: unmanaged, INumber<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <param name="value">
|
||||
/// Scalar value to replicate to every element.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Should map to a single broadcast/shuffle instruction where possible. After completion all
|
||||
/// lanes read back the same value.
|
||||
/// </remarks>
|
||||
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a writable reference to the element at the specified <paramref name="index" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
|
||||
/// </typeparam>
|
||||
/// <param name="index">
|
||||
/// Zero-based lane index (range defined by implementation).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Writable reference to the lane.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit
|
||||
/// bounds checks for performance. Caller is responsible for passing a valid index.
|
||||
/// </remarks>
|
||||
ref T Member<T>(int index) where T: unmanaged, INumber<T>;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,120 +1,120 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M128"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 16)]
|
||||
public unsafe struct M128 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal fixed byte Buffer[16];
|
||||
[FieldOffset(0)] internal Vector128<float> Vec;
|
||||
|
||||
internal M128(Vector4 v)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Unsafe.SkipInit(out Vec);
|
||||
Vec = v.AsVector128();
|
||||
}
|
||||
|
||||
internal M128(Vector128<float> v)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vec = v;
|
||||
}
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref Buffer[0]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Vector4 AsVector4()
|
||||
{
|
||||
return Vec.AsVector4();
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector128.Create<T>(value).AsSingle();
|
||||
}
|
||||
|
||||
public override readonly bool Equals([NotNullWhen(true)] object obj)
|
||||
{
|
||||
return obj is M128 other && this == other;
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return Vec.GetHashCode();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.Add(ref AsRef<T>(), index);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public string ToString(int sample)
|
||||
{
|
||||
string str = string.Empty;
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
str += $"{Convert.ToString(Buffer[i], sample)} ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(M128 left, M128 right)
|
||||
{
|
||||
return left.Vec != right.Vec;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(M128 left, M128 right)
|
||||
{
|
||||
return left.Vec == right.Vec;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M128"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 16)]
|
||||
public unsafe struct M128 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal fixed byte Buffer[16];
|
||||
[FieldOffset(0)] internal Vector128<float> Vec;
|
||||
|
||||
internal M128(Vector4 v)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Unsafe.SkipInit(out Vec);
|
||||
Vec = v.AsVector128();
|
||||
}
|
||||
|
||||
internal M128(Vector128<float> v)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vec = v;
|
||||
}
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref Buffer[0]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Vector4 AsVector4()
|
||||
{
|
||||
return Vec.AsVector4();
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector128.Create<T>(value).AsSingle();
|
||||
}
|
||||
|
||||
public override readonly bool Equals([NotNullWhen(true)] object obj)
|
||||
{
|
||||
return obj is M128 other && this == other;
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return Vec.GetHashCode();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.Add(ref AsRef<T>(), index);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public string ToString(int sample)
|
||||
{
|
||||
string str = string.Empty;
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
str += $"{Convert.ToString(Buffer[i], sample)} ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(M128 left, M128 right)
|
||||
{
|
||||
return left.Vec != right.Vec;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(M128 left, M128 right)
|
||||
{
|
||||
return left.Vec == right.Vec;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M256"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 16)]
|
||||
public unsafe struct M256 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal fixed byte Buffer[32];
|
||||
[FieldOffset(0)] internal Vector256<float> Vec;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref Buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector256.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.Add(ref AsRef<T>(), index);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M256"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 16)]
|
||||
public unsafe struct M256 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] internal fixed byte Buffer[32];
|
||||
[FieldOffset(0)] internal Vector256<float> Vec;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref Buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector256.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.Add(ref AsRef<T>(), index);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M512"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
|
||||
public unsafe struct M512 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] private fixed byte _buffer[64];
|
||||
[FieldOffset(0)] private Vector512<float> Vec;
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector512.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M512"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
|
||||
public unsafe struct M512 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] private fixed byte _buffer[64];
|
||||
[FieldOffset(0)] private Vector512<float> Vec;
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
Vec = Vector512.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,100 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M64"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 8)]
|
||||
public unsafe struct M64 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] private fixed byte buffer[8];
|
||||
[FieldOffset(0)] private ulong value;
|
||||
[FieldOffset(0)] private Vector64<float> vec;
|
||||
|
||||
public M64()
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Unsafe.SkipInit(out vec);
|
||||
Unsafe.SkipInit(out value);
|
||||
}
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged,INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
vec = Vector64.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is M64 m64 && this == m64;
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
|
||||
}
|
||||
|
||||
public static bool operator !=(M64 left, M64 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static M64 operator <<(M64 value, int shift)
|
||||
{
|
||||
M64 result = new M64();
|
||||
result.value = value.value << shift;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool operator ==(M64 left, M64 right)
|
||||
{
|
||||
return left.value == right.value;
|
||||
}
|
||||
public static M64 operator >>(M64 value, int shift)
|
||||
{
|
||||
M64 result = new M64();
|
||||
result.value = value.value << shift;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="M64"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace StgSharp.Mathematics
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 8)]
|
||||
public unsafe struct M64 : IRegisterPresentation
|
||||
{
|
||||
|
||||
[FieldOffset(0)] private fixed byte buffer[8];
|
||||
[FieldOffset(0)] private ulong value;
|
||||
[FieldOffset(0)] private Vector64<float> vec;
|
||||
|
||||
public M64()
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Unsafe.SkipInit(out vec);
|
||||
Unsafe.SkipInit(out value);
|
||||
}
|
||||
|
||||
public ref T AsRef<T>() where T: unmanaged,INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref buffer[0]);
|
||||
}
|
||||
|
||||
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
|
||||
{
|
||||
vec = Vector64.Create(value).AsSingle();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is M64 m64 && this == m64;
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
|
||||
{
|
||||
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
|
||||
}
|
||||
|
||||
public static bool operator !=(M64 left, M64 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static M64 operator <<(M64 value, int shift)
|
||||
{
|
||||
M64 result = new M64();
|
||||
result.value = value.value << shift;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool operator ==(M64 left, M64 right)
|
||||
{
|
||||
return left.value == right.value;
|
||||
}
|
||||
public static M64 operator >>(M64 value, int shift)
|
||||
{
|
||||
M64 result = new M64();
|
||||
result.value = value.value << shift;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +1,133 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="DefaultLog"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Internal
|
||||
{
|
||||
internal static unsafe partial class DefaultLog
|
||||
{
|
||||
|
||||
private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB
|
||||
|
||||
private static LogType _currentLogLevel = LogType.Info;
|
||||
|
||||
internal static readonly GCHandle ssc_logHandle =
|
||||
GCHandle.Alloc(new byte[512], GCHandleType.Pinned);
|
||||
|
||||
internal static SemaphoreSlim logSyncSemaphore = new(1, 1);
|
||||
|
||||
internal static void InternalAppendLog(
|
||||
string log
|
||||
)
|
||||
{
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
|
||||
logStream.WriteLine(log);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InternalWriteLog(
|
||||
string logLine,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
if (logType < _currentLogLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
logSyncSemaphore.Wait();
|
||||
try
|
||||
{
|
||||
RotateLogFile();
|
||||
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
|
||||
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = logSyncSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InternalWriteLog(
|
||||
string beforeTime,
|
||||
string logLine,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
logSyncSemaphore.Wait();
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath))
|
||||
{
|
||||
logStream.WriteLine(beforeTime);
|
||||
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
|
||||
}
|
||||
_ = logSyncSemaphore.Release(1);
|
||||
}
|
||||
|
||||
internal static void SetLogLevel(
|
||||
LogType logLevel
|
||||
)
|
||||
{
|
||||
_currentLogLevel = logLevel;
|
||||
}
|
||||
|
||||
private static void RotateLogFile()
|
||||
{
|
||||
if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize)
|
||||
{
|
||||
string backupFile = Native.LogPath + ".backup";
|
||||
if (File.Exists(backupFile)) {
|
||||
File.Delete(backupFile);
|
||||
}
|
||||
File.Move(Native.LogPath, backupFile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="DefaultLog"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Internal
|
||||
{
|
||||
internal static unsafe partial class DefaultLog
|
||||
{
|
||||
|
||||
private const long MaxLogFileSize = 10 * 1024 * 1024; // 10 MB
|
||||
|
||||
private static LogType _currentLogLevel = LogType.Info;
|
||||
|
||||
internal static readonly GCHandle ssc_logHandle =
|
||||
GCHandle.Alloc(new byte[512], GCHandleType.Pinned);
|
||||
|
||||
internal static SemaphoreSlim logSyncSemaphore = new(1, 1);
|
||||
|
||||
internal static void InternalAppendLog(
|
||||
string log
|
||||
)
|
||||
{
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
|
||||
logStream.WriteLine(log);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InternalWriteLog(
|
||||
string logLine,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
if (logType < _currentLogLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
logSyncSemaphore.Wait();
|
||||
try
|
||||
{
|
||||
RotateLogFile();
|
||||
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath)) {
|
||||
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = logSyncSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InternalWriteLog(
|
||||
string beforeTime,
|
||||
string logLine,
|
||||
LogType logType
|
||||
)
|
||||
{
|
||||
logSyncSemaphore.Wait();
|
||||
if (!File.Exists(Native.LogPath))
|
||||
{
|
||||
FileStream stream = File.Create(Native.LogPath);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
using (StreamWriter logStream = File.AppendText(Native.LogPath))
|
||||
{
|
||||
logStream.WriteLine(beforeTime);
|
||||
logStream.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] [{logType}] {logLine}");
|
||||
}
|
||||
_ = logSyncSemaphore.Release(1);
|
||||
}
|
||||
|
||||
internal static void SetLogLevel(
|
||||
LogType logLevel
|
||||
)
|
||||
{
|
||||
_currentLogLevel = logLevel;
|
||||
}
|
||||
|
||||
private static void RotateLogFile()
|
||||
{
|
||||
if (File.Exists(Native.LogPath) && new FileInfo(Native.LogPath).Length > MaxLogFileSize)
|
||||
{
|
||||
string backupFile = Native.LogPath + ".backup";
|
||||
if (File.Exists(backupFile)) {
|
||||
File.Delete(backupFile);
|
||||
}
|
||||
File.Move(Native.LogPath, backupFile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Native"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Internal
|
||||
{
|
||||
internal static unsafe partial class Native
|
||||
{
|
||||
|
||||
internal const string LibName =
|
||||
"StgSharp.Native";
|
||||
|
||||
internal const string LogPath =
|
||||
"SS_error.log";
|
||||
|
||||
internal static IntPtr _libPtr = IntPtr.Zero;
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="Native"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Internal
|
||||
{
|
||||
internal static unsafe partial class Native
|
||||
{
|
||||
|
||||
internal const string LibName =
|
||||
"StgSharp.Native";
|
||||
|
||||
internal const string LogPath =
|
||||
"SS_error.log";
|
||||
|
||||
internal static IntPtr _libPtr = IntPtr.Zero;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="public"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public enum LogType
|
||||
{
|
||||
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
InfoError,
|
||||
InfoWarning,
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="public"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public enum LogType
|
||||
{
|
||||
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
InfoError,
|
||||
InfoWarning,
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,91 +1,91 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CrossFramePipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.PipeLine;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class CrossFrameOperation : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private static string[] _inputName = [ "TaskIn" ];
|
||||
private static string[] _outputName = [ "TaskOut" ];
|
||||
private Action _mainExecution;
|
||||
private int currentCount;
|
||||
private readonly int cycleCount;
|
||||
|
||||
public CrossFrameOperation(Action startup, int cycleCount)
|
||||
{
|
||||
MainExecution = startup;
|
||||
this.cycleCount = (cycleCount > 1) ?
|
||||
cycleCount :
|
||||
throw new ArgumentException(
|
||||
$"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}");
|
||||
this.cycleCount = cycleCount;
|
||||
currentCount = cycleCount;
|
||||
}
|
||||
|
||||
public Action MainExecution
|
||||
{
|
||||
get => _mainExecution;
|
||||
private protected set => _mainExecution = value;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName => _inputName;
|
||||
|
||||
public IEnumerable<string> OutputPortName => _outputName;
|
||||
|
||||
public PipelineNodeOperation Operation
|
||||
{
|
||||
get => NodeMain;
|
||||
}
|
||||
|
||||
public void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
if (currentCount < cycleCount)
|
||||
{
|
||||
Interlocked.Increment(ref currentCount);
|
||||
} else
|
||||
{
|
||||
Interlocked.Exchange(ref currentCount, 0);
|
||||
Task.Run(MainExecution);
|
||||
}
|
||||
|
||||
// Console.WriteLine(currentCount);
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CrossFramePipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.PipeLine;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class CrossFrameOperation : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private static string[] _inputName = [ "TaskIn" ];
|
||||
private static string[] _outputName = [ "TaskOut" ];
|
||||
private Action _mainExecution;
|
||||
private int currentCount;
|
||||
private readonly int cycleCount;
|
||||
|
||||
public CrossFrameOperation(Action startup, int cycleCount)
|
||||
{
|
||||
MainExecution = startup;
|
||||
this.cycleCount = (cycleCount > 1) ?
|
||||
cycleCount :
|
||||
throw new ArgumentException(
|
||||
$"{$"The {typeof(CrossFrameOperation).Name} crosses only one cycle, "}{$"please use a {typeof(PipelineNode).Name} instead."}");
|
||||
this.cycleCount = cycleCount;
|
||||
currentCount = cycleCount;
|
||||
}
|
||||
|
||||
public Action MainExecution
|
||||
{
|
||||
get => _mainExecution;
|
||||
private protected set => _mainExecution = value;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName => _inputName;
|
||||
|
||||
public IEnumerable<string> OutputPortName => _outputName;
|
||||
|
||||
public PipelineNodeOperation Operation
|
||||
{
|
||||
get => NodeMain;
|
||||
}
|
||||
|
||||
public void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
if (currentCount < cycleCount)
|
||||
{
|
||||
Interlocked.Increment(ref currentCount);
|
||||
} else
|
||||
{
|
||||
Interlocked.Exchange(ref currentCount, 0);
|
||||
Task.Run(MainExecution);
|
||||
}
|
||||
|
||||
// Console.WriteLine(currentCount);
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IConvertableToPipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public delegate void PipelineNodeOperation(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output);
|
||||
|
||||
public interface IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
public IEnumerable<string> InputPortName { get; }
|
||||
|
||||
public IEnumerable<string> OutputPortName { get; }
|
||||
|
||||
PipelineNodeOperation Operation { get; }
|
||||
|
||||
void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output);
|
||||
|
||||
}
|
||||
|
||||
internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private string[] _input;
|
||||
private string[] _output;
|
||||
private PipelineNodeOperation _execution;
|
||||
|
||||
public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output)
|
||||
{
|
||||
_input = input;
|
||||
_output = output;
|
||||
_execution = execution;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName => _input;
|
||||
|
||||
public IEnumerable<string> OutputPortName => _output;
|
||||
|
||||
public PipelineNodeOperation Operation => _execution;
|
||||
|
||||
public void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
_execution.Invoke(input, output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="IConvertableToPipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public delegate void PipelineNodeOperation(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output);
|
||||
|
||||
public interface IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
public IEnumerable<string> InputPortName { get; }
|
||||
|
||||
public IEnumerable<string> OutputPortName { get; }
|
||||
|
||||
PipelineNodeOperation Operation { get; }
|
||||
|
||||
void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output);
|
||||
|
||||
}
|
||||
|
||||
internal class DefaultConvertableToBlueprintNode : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private string[] _input;
|
||||
private string[] _output;
|
||||
private PipelineNodeOperation _execution;
|
||||
|
||||
public DefaultConvertableToBlueprintNode(PipelineNodeOperation execution, string[] input, string[] output)
|
||||
{
|
||||
_input = input;
|
||||
_output = output;
|
||||
_execution = execution;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName => _input;
|
||||
|
||||
public IEnumerable<string> OutputPortName => _output;
|
||||
|
||||
public PipelineNodeOperation Operation => _execution;
|
||||
|
||||
public void NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
_execution.Invoke(input, output);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,242 +1,242 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipeLineRunner"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public static class PipeLineRunner
|
||||
{
|
||||
|
||||
private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray();
|
||||
private static CancellationTokenSource cts = new CancellationTokenSource();
|
||||
private static ConcurrentStack<PipelineScheduler> pipelineInWaiting
|
||||
= new ConcurrentStack<PipelineScheduler>();
|
||||
private static int _callingThread;
|
||||
private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1);
|
||||
|
||||
public static void Run(PipelineScheduler pipeline)
|
||||
{
|
||||
// Console.WriteLine();
|
||||
if (pipeline == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (pipeline.IsRunning) {
|
||||
throw new InvalidOperationException("Attempt to run a blueprint instance twice.");
|
||||
}
|
||||
if (!IsValidCalling()) {
|
||||
pipelineLock.Wait();
|
||||
}
|
||||
if (InterruptAndRunNewPipeline(pipeline))
|
||||
{
|
||||
while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { }
|
||||
}
|
||||
pipelineLock.Release();
|
||||
Interlocked.Exchange(ref _callingThread, -1);
|
||||
}
|
||||
|
||||
public static void SetMaxTaskCount(int size)
|
||||
{
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
if (allAvailableTask.Length >= size)
|
||||
{
|
||||
for (int i = size; i < allAvailableTask.Length; i++)
|
||||
{
|
||||
if (allAvailableTask[i] != null) {
|
||||
allAvailableTask[i].Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Array.Resize(ref allAvailableTask, size);
|
||||
}
|
||||
|
||||
public static void Terminate()
|
||||
{
|
||||
cts.Cancel();
|
||||
if (NodeRunner.CurrentPipeline is null) {
|
||||
return;
|
||||
}
|
||||
NodeRunner.CurrentPipeline.Terminate();
|
||||
foreach (NodeRunner task in allAvailableTask) {
|
||||
task.Cts.Cancel();
|
||||
}
|
||||
foreach (PipelineScheduler bp in pipelineInWaiting) {
|
||||
bp.Terminate();
|
||||
}
|
||||
pipelineInWaiting.Clear();
|
||||
}
|
||||
|
||||
internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newPipeline);
|
||||
if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null))
|
||||
{
|
||||
// if a running bp exists
|
||||
pipelineInWaiting.Push(NodeRunner.CurrentPipeline);
|
||||
}
|
||||
|
||||
// load new bp to current bp
|
||||
NodeRunner.CurrentPipeline = newPipeline;
|
||||
Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId);
|
||||
|
||||
// run beginning node to init it first.
|
||||
if (!NodeRunner.CurrentPipeline.IsRunning)
|
||||
{
|
||||
(NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run();
|
||||
}
|
||||
|
||||
// refresh all tasks
|
||||
/*
|
||||
for( int i = 0; i < allAvailableTask.Length; i++ )
|
||||
{
|
||||
NodeRunner item = allAvailableTask[ i ];
|
||||
if( ( item is null ) || item.IsCompleted ) {
|
||||
allAvailableTask[ i ] = NodeRunner.Run();
|
||||
}
|
||||
}
|
||||
/**/
|
||||
PipelineNode node;
|
||||
while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node))
|
||||
{
|
||||
node.Run();
|
||||
if (!NodeRunner.CurrentPipeline.IsRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while ((NodeRunner.CurrentPipeline != null) &&
|
||||
NodeRunner.CurrentPipeline.IsRunning &&
|
||||
!cts.Token.IsCancellationRequested &&
|
||||
NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node))
|
||||
{
|
||||
node.Run();
|
||||
if (!NodeRunner.CurrentPipeline!.IsRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
(NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run();
|
||||
bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline);
|
||||
NodeRunner.CurrentPipeline = pipeline!;
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> Only the following occasions of calling are valid: </para> <para> 1. no bp is
|
||||
/// running or the only bp is quitting. </para><para> 2. calling from the main thread or a
|
||||
/// thread the current bp is running on.</para>
|
||||
/// </summary>
|
||||
internal static bool IsValidCalling()
|
||||
{
|
||||
if ((NodeRunner.CurrentPipeline == null) || //no bp running at all
|
||||
((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false)))
|
||||
{
|
||||
// a bp is quitting
|
||||
// in case no bp is running
|
||||
return true;
|
||||
}
|
||||
int? id = Task.CurrentId;
|
||||
if (id is null)
|
||||
{
|
||||
// in case calling from main thread and current running bp
|
||||
return _callingThread == Environment.CurrentManagedThreadId;
|
||||
}
|
||||
foreach (NodeRunner item in allAvailableTask)
|
||||
{
|
||||
if (item.Id == id)
|
||||
{
|
||||
// if calling from any bp running task
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static NodeRunner[] CreateBlueprintRunnerArray()
|
||||
{
|
||||
int count = Environment.ProcessorCount;
|
||||
return new NodeRunner[ count < 2 ? count : count - 2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class NodeRunner : CancellableTask
|
||||
{
|
||||
|
||||
private static volatile PipelineScheduler? _current;
|
||||
|
||||
internal NodeRunner()
|
||||
: base()
|
||||
{
|
||||
_startup = InternalRun;
|
||||
}
|
||||
|
||||
internal static PipelineScheduler? CurrentPipeline
|
||||
{
|
||||
get => _current;
|
||||
set => _current = value;
|
||||
}
|
||||
|
||||
internal static NodeRunner Run()
|
||||
{
|
||||
NodeRunner ret = new NodeRunner
|
||||
{
|
||||
_startup = InternalRun
|
||||
};
|
||||
ret._internalTask = Task.Run(ret.InternalAction);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void InternalRun(CancellationToken token)
|
||||
{
|
||||
PipelineNode item;
|
||||
Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts.");
|
||||
while ((CurrentPipeline != null) &&
|
||||
CurrentPipeline.IsRunning &&
|
||||
!token.IsCancellationRequested &&
|
||||
CurrentPipeline.RequestNexConcurrentNode(out item)) {
|
||||
item.Run();
|
||||
}
|
||||
}
|
||||
|
||||
}//------------------------------------- End of Class ------------------------------------------
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipeLineRunner"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public static class PipeLineRunner
|
||||
{
|
||||
|
||||
private static NodeRunner[] allAvailableTask = CreateBlueprintRunnerArray();
|
||||
private static CancellationTokenSource cts = new CancellationTokenSource();
|
||||
private static ConcurrentStack<PipelineScheduler> pipelineInWaiting
|
||||
= new ConcurrentStack<PipelineScheduler>();
|
||||
private static int _callingThread;
|
||||
private static SemaphoreSlim pipelineLock = new SemaphoreSlim(0, 1);
|
||||
|
||||
public static void Run(PipelineScheduler pipeline)
|
||||
{
|
||||
// Console.WriteLine();
|
||||
if (pipeline == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (pipeline.IsRunning) {
|
||||
throw new InvalidOperationException("Attempt to run a blueprint instance twice.");
|
||||
}
|
||||
if (!IsValidCalling()) {
|
||||
pipelineLock.Wait();
|
||||
}
|
||||
if (InterruptAndRunNewPipeline(pipeline))
|
||||
{
|
||||
while (InterruptAndRunNewPipeline(NodeRunner.CurrentPipeline!)) { }
|
||||
}
|
||||
pipelineLock.Release();
|
||||
Interlocked.Exchange(ref _callingThread, -1);
|
||||
}
|
||||
|
||||
public static void SetMaxTaskCount(int size)
|
||||
{
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
if (allAvailableTask.Length >= size)
|
||||
{
|
||||
for (int i = size; i < allAvailableTask.Length; i++)
|
||||
{
|
||||
if (allAvailableTask[i] != null) {
|
||||
allAvailableTask[i].Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Array.Resize(ref allAvailableTask, size);
|
||||
}
|
||||
|
||||
public static void Terminate()
|
||||
{
|
||||
cts.Cancel();
|
||||
if (NodeRunner.CurrentPipeline is null) {
|
||||
return;
|
||||
}
|
||||
NodeRunner.CurrentPipeline.Terminate();
|
||||
foreach (NodeRunner task in allAvailableTask) {
|
||||
task.Cts.Cancel();
|
||||
}
|
||||
foreach (PipelineScheduler bp in pipelineInWaiting) {
|
||||
bp.Terminate();
|
||||
}
|
||||
pipelineInWaiting.Clear();
|
||||
}
|
||||
|
||||
internal static bool InterruptAndRunNewPipeline(PipelineScheduler newPipeline)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newPipeline);
|
||||
if ((newPipeline != NodeRunner.CurrentPipeline) && (NodeRunner.CurrentPipeline != null))
|
||||
{
|
||||
// if a running bp exists
|
||||
pipelineInWaiting.Push(NodeRunner.CurrentPipeline);
|
||||
}
|
||||
|
||||
// load new bp to current bp
|
||||
NodeRunner.CurrentPipeline = newPipeline;
|
||||
Interlocked.Exchange(ref _callingThread, Environment.CurrentManagedThreadId);
|
||||
|
||||
// run beginning node to init it first.
|
||||
if (!NodeRunner.CurrentPipeline.IsRunning)
|
||||
{
|
||||
(NodeRunner.CurrentPipeline.BeginLayer as BeginningNode)!.Run();
|
||||
}
|
||||
|
||||
// refresh all tasks
|
||||
/*
|
||||
for( int i = 0; i < allAvailableTask.Length; i++ )
|
||||
{
|
||||
NodeRunner item = allAvailableTask[ i ];
|
||||
if( ( item is null ) || item.IsCompleted ) {
|
||||
allAvailableTask[ i ] = NodeRunner.Run();
|
||||
}
|
||||
}
|
||||
/**/
|
||||
PipelineNode node;
|
||||
while (!cts.Token.IsCancellationRequested && NodeRunner.CurrentPipeline.RequestNextMainThreadNode(out node))
|
||||
{
|
||||
node.Run();
|
||||
if (!NodeRunner.CurrentPipeline.IsRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while ((NodeRunner.CurrentPipeline != null) &&
|
||||
NodeRunner.CurrentPipeline.IsRunning &&
|
||||
!cts.Token.IsCancellationRequested &&
|
||||
NodeRunner.CurrentPipeline!.RequestNexConcurrentNode(out node))
|
||||
{
|
||||
node.Run();
|
||||
if (!NodeRunner.CurrentPipeline!.IsRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
(NodeRunner.CurrentPipeline!.EndLayer as EndingNode)!.Run();
|
||||
bool isEmpty = pipelineInWaiting.TryPop(out PipelineScheduler? pipeline);
|
||||
NodeRunner.CurrentPipeline = pipeline!;
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> Only the following occasions of calling are valid: </para> <para> 1. no bp is
|
||||
/// running or the only bp is quitting. </para><para> 2. calling from the main thread or a
|
||||
/// thread the current bp is running on.</para>
|
||||
/// </summary>
|
||||
internal static bool IsValidCalling()
|
||||
{
|
||||
if ((NodeRunner.CurrentPipeline == null) || //no bp running at all
|
||||
((pipelineInWaiting.IsEmpty) && (NodeRunner.CurrentPipeline.IsRunning == false)))
|
||||
{
|
||||
// a bp is quitting
|
||||
// in case no bp is running
|
||||
return true;
|
||||
}
|
||||
int? id = Task.CurrentId;
|
||||
if (id is null)
|
||||
{
|
||||
// in case calling from main thread and current running bp
|
||||
return _callingThread == Environment.CurrentManagedThreadId;
|
||||
}
|
||||
foreach (NodeRunner item in allAvailableTask)
|
||||
{
|
||||
if (item.Id == id)
|
||||
{
|
||||
// if calling from any bp running task
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static NodeRunner[] CreateBlueprintRunnerArray()
|
||||
{
|
||||
int count = Environment.ProcessorCount;
|
||||
return new NodeRunner[ count < 2 ? count : count - 2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class NodeRunner : CancellableTask
|
||||
{
|
||||
|
||||
private static volatile PipelineScheduler? _current;
|
||||
|
||||
internal NodeRunner()
|
||||
: base()
|
||||
{
|
||||
_startup = InternalRun;
|
||||
}
|
||||
|
||||
internal static PipelineScheduler? CurrentPipeline
|
||||
{
|
||||
get => _current;
|
||||
set => _current = value;
|
||||
}
|
||||
|
||||
internal static NodeRunner Run()
|
||||
{
|
||||
NodeRunner ret = new NodeRunner
|
||||
{
|
||||
_startup = InternalRun
|
||||
};
|
||||
ret._internalTask = Task.Run(ret.InternalAction);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void InternalRun(CancellationToken token)
|
||||
{
|
||||
PipelineNode item;
|
||||
Console.WriteLine($"Global thread {Environment.CurrentManagedThreadId} starts.");
|
||||
while ((CurrentPipeline != null) &&
|
||||
CurrentPipeline.IsRunning &&
|
||||
!token.IsCancellationRequested &&
|
||||
CurrentPipeline.RequestNexConcurrentNode(out item)) {
|
||||
item.Run();
|
||||
}
|
||||
}
|
||||
|
||||
}//------------------------------------- End of Class ------------------------------------------
|
||||
}
|
||||
|
||||
@@ -1,114 +1,114 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineMarshal"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public static class PipelineMarshal
|
||||
{
|
||||
|
||||
public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scheduler);
|
||||
return new PipelineNodeEnumerator(scheduler)
|
||||
{
|
||||
_count = scheduler._allNode.Count,
|
||||
};
|
||||
}
|
||||
|
||||
private class PipelineNodeEnumerator : ICollection<PipelineNode>
|
||||
{
|
||||
|
||||
private readonly List<List<PipelineNode>> _mt, _c;
|
||||
|
||||
public int _count;
|
||||
|
||||
public PipelineNodeEnumerator(PipelineScheduler scheduler)
|
||||
{
|
||||
if (!scheduler.TryGetAllNode(out _mt, out _c)) {
|
||||
throw new InvalidOperationException("Binded scheduler is not sorted.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public int Count => _count;
|
||||
|
||||
public IEnumerator<PipelineNode> GetEnumerator()
|
||||
{
|
||||
for (int i = 1; i < _mt.Count; i++)
|
||||
{
|
||||
foreach (PipelineNode node in _mt[i]) {
|
||||
yield return node;
|
||||
}
|
||||
foreach (PipelineNode node in _c[i]) {
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.Add(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<PipelineNode>.Contains(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<PipelineNode>.Remove(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineMarshal"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public static class PipelineMarshal
|
||||
{
|
||||
|
||||
public static IEnumerable<PipelineNode> GetNodeSortedEnumeration(PipelineScheduler scheduler)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(scheduler);
|
||||
return new PipelineNodeEnumerator(scheduler)
|
||||
{
|
||||
_count = scheduler._allNode.Count,
|
||||
};
|
||||
}
|
||||
|
||||
private class PipelineNodeEnumerator : ICollection<PipelineNode>
|
||||
{
|
||||
|
||||
private readonly List<List<PipelineNode>> _mt, _c;
|
||||
|
||||
public int _count;
|
||||
|
||||
public PipelineNodeEnumerator(PipelineScheduler scheduler)
|
||||
{
|
||||
if (!scheduler.TryGetAllNode(out _mt, out _c)) {
|
||||
throw new InvalidOperationException("Binded scheduler is not sorted.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public int Count => _count;
|
||||
|
||||
public IEnumerator<PipelineNode> GetEnumerator()
|
||||
{
|
||||
for (int i = 1; i < _mt.Count; i++)
|
||||
{
|
||||
foreach (PipelineNode node in _mt[i]) {
|
||||
yield return node;
|
||||
}
|
||||
foreach (PipelineNode node in _c[i]) {
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.Add(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<PipelineNode>.Contains(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<PipelineNode>.CopyTo(PipelineNode[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<PipelineNode>.Remove(PipelineNode item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,266 +1,266 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <risk>
|
||||
/// Reset pipeline has risk, this operation is a direct operation to port in current node, the
|
||||
/// port on the other node cannot get the change.
|
||||
/// </risk>
|
||||
#pragma warning disable CA1036
|
||||
public class PipelineNode
|
||||
#pragma warning restore CA1036
|
||||
{
|
||||
|
||||
private static readonly Dictionary<string, PipelineConnector> _emptyLayer = [];
|
||||
private static PipelineNode _emptyNode = new PipelineNode(
|
||||
PipelineNodeLabel.Empty, null!, false);
|
||||
private IConvertableToPipelineNode _mainOperation;
|
||||
private PipelineNodeLabel _label;
|
||||
private protected bool _nativeRequested;
|
||||
private protected Dictionary<string, PipelineNodeInPort> _input;
|
||||
private protected Dictionary<string, PipelineNodeOutPort> _output;
|
||||
private protected int _level, _count;
|
||||
|
||||
internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative)
|
||||
{
|
||||
_label = label;
|
||||
_nativeRequested = isNative;
|
||||
_mainOperation = node;
|
||||
InputPorts = new();
|
||||
OutputPorts = new();
|
||||
foreach (string port in node.InputPortName) {
|
||||
InputPorts.Add(port, new PipelineNodeInPort(this, port));
|
||||
}
|
||||
foreach (string port in node.OutputPortName) {
|
||||
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618
|
||||
internal PipelineNode(
|
||||
PipelineNodeOperation operation,
|
||||
PipelineNodeLabel name,
|
||||
bool isNative,
|
||||
string[] inputNames,
|
||||
string[] outputNames)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
if ((inputNames == null) || (inputNames.Length == 0)) {
|
||||
throw new ArgumentNullException(nameof(inputNames));
|
||||
}
|
||||
if ((outputNames == null) || (outputNames.Length == 0)) {
|
||||
throw new ArgumentNullException(nameof(outputNames));
|
||||
}
|
||||
_label = name;
|
||||
_nativeRequested = isNative;
|
||||
if (operation == null) {
|
||||
operation = DefaultOperation;
|
||||
}
|
||||
_mainOperation = new DefaultConvertableToBlueprintNode(
|
||||
operation, inputNames, outputNames);
|
||||
InputPorts = new Dictionary<string, PipelineNodeInPort>();
|
||||
OutputPorts = new Dictionary<string, PipelineNodeOutPort>();
|
||||
foreach (string port in inputNames) {
|
||||
InputPorts.Add(port, new PipelineNodeInPort(this, port));
|
||||
}
|
||||
foreach (string port in outputNames) {
|
||||
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNative
|
||||
{
|
||||
get => _nativeRequested;
|
||||
internal set => _nativeRequested = value;
|
||||
}
|
||||
|
||||
public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer;
|
||||
|
||||
public IConvertableToPipelineNode NodeOperation => _mainOperation;
|
||||
|
||||
public int Level
|
||||
{
|
||||
get => _level;
|
||||
private set => _level = value;
|
||||
}
|
||||
|
||||
public static PipelineNode Empty => _emptyNode;
|
||||
|
||||
public PipelineNodeLabel Label => _label;
|
||||
|
||||
internal Dictionary<string, PipelineNodeInPort> InputPorts
|
||||
{
|
||||
get => _input;
|
||||
private set => _input = value;
|
||||
}
|
||||
|
||||
internal Dictionary<string, PipelineNodeOutPort> OutputPorts
|
||||
{
|
||||
get => _output;
|
||||
private set => _output = value;
|
||||
}
|
||||
|
||||
internal HashSet<PipelineNode> Previous { get; } = [];
|
||||
|
||||
internal HashSet<PipelineNode> Next { get; } = [];
|
||||
|
||||
internal IConvertableToPipelineNode Value => _mainOperation;
|
||||
|
||||
internal int RemainingIncreaseCount => Previous.Count - _count;
|
||||
|
||||
public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(front);
|
||||
ArgumentNullException.ThrowIfNull(back);
|
||||
if (!front.OutputPorts
|
||||
.TryGetValue(frontNodePort,
|
||||
out PipelineNodeOutPort? frontPort)) {
|
||||
throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}");
|
||||
}
|
||||
if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) {
|
||||
throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}");
|
||||
}
|
||||
backPort.Connect(frontPort);
|
||||
frontPort.Connect(backPort);
|
||||
}
|
||||
|
||||
public IEnumerator<PipelineNode> GetFormerNodes()
|
||||
{
|
||||
foreach (PipelineNodeInPort port in InputPorts.Values)
|
||||
{
|
||||
IEnumerator<PipelineNode> enumerator = port.GetFormerNodes();
|
||||
while (enumerator.MoveNext()) {
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Console.WriteLine(_label);
|
||||
PipelineNodeInPort.WaitAll(InputPorts);
|
||||
if (_mainOperation != null) {
|
||||
_mainOperation.NodeMain(in _input, in _output);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool TryIncreaseLevelTo(int level)
|
||||
{
|
||||
_count++;
|
||||
if (_count > Previous.Count) {
|
||||
return false;
|
||||
}
|
||||
Level = int.Max(level, this.Level);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static void DefaultOperation(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
#region port operation
|
||||
|
||||
public PipelineNodeInPort GetInputPort(string lable)
|
||||
{
|
||||
if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Cannot find the interface named {lable} in node {Label}.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort GetOutputPort(string lable)
|
||||
{
|
||||
if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Cannot find the interface named {lable} in node {Label}.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PipelineNodeInPort? DefineCertainInputPort(string label)
|
||||
{
|
||||
if (InputPorts.ContainsKey(label)) {
|
||||
return null;
|
||||
}
|
||||
PipelineNodeInPort port = new(this, label);
|
||||
InputPorts[label] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort? DefineCertainOutputPort(string label)
|
||||
{
|
||||
if (OutputPorts.ContainsKey(label)) {
|
||||
return null;
|
||||
}
|
||||
PipelineNodeOutPort port = new(this, label);
|
||||
OutputPorts[label] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}//--------------------------------- end of class -----------------------------------
|
||||
|
||||
public class PipelineNodeRunException : Exception
|
||||
{
|
||||
|
||||
public PipelineNodeRunException() { }
|
||||
|
||||
public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { }
|
||||
|
||||
public PipelineNodeRunException(string message) : base(message) { }
|
||||
|
||||
public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { }
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <risk>
|
||||
/// Reset pipeline has risk, this operation is a direct operation to port in current node, the
|
||||
/// port on the other node cannot get the change.
|
||||
/// </risk>
|
||||
#pragma warning disable CA1036
|
||||
public class PipelineNode
|
||||
#pragma warning restore CA1036
|
||||
{
|
||||
|
||||
private static readonly Dictionary<string, PipelineConnector> _emptyLayer = [];
|
||||
private static PipelineNode _emptyNode = new PipelineNode(
|
||||
PipelineNodeLabel.Empty, null!, false);
|
||||
private IConvertableToPipelineNode _mainOperation;
|
||||
private PipelineNodeLabel _label;
|
||||
private protected bool _nativeRequested;
|
||||
private protected Dictionary<string, PipelineNodeInPort> _input;
|
||||
private protected Dictionary<string, PipelineNodeOutPort> _output;
|
||||
private protected int _level, _count;
|
||||
|
||||
internal PipelineNode(PipelineNodeLabel label, IConvertableToPipelineNode node, bool isNative)
|
||||
{
|
||||
_label = label;
|
||||
_nativeRequested = isNative;
|
||||
_mainOperation = node;
|
||||
InputPorts = new();
|
||||
OutputPorts = new();
|
||||
foreach (string port in node.InputPortName) {
|
||||
InputPorts.Add(port, new PipelineNodeInPort(this, port));
|
||||
}
|
||||
foreach (string port in node.OutputPortName) {
|
||||
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS8618
|
||||
internal PipelineNode(
|
||||
PipelineNodeOperation operation,
|
||||
PipelineNodeLabel name,
|
||||
bool isNative,
|
||||
string[] inputNames,
|
||||
string[] outputNames)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
if ((inputNames == null) || (inputNames.Length == 0)) {
|
||||
throw new ArgumentNullException(nameof(inputNames));
|
||||
}
|
||||
if ((outputNames == null) || (outputNames.Length == 0)) {
|
||||
throw new ArgumentNullException(nameof(outputNames));
|
||||
}
|
||||
_label = name;
|
||||
_nativeRequested = isNative;
|
||||
if (operation == null) {
|
||||
operation = DefaultOperation;
|
||||
}
|
||||
_mainOperation = new DefaultConvertableToBlueprintNode(
|
||||
operation, inputNames, outputNames);
|
||||
InputPorts = new Dictionary<string, PipelineNodeInPort>();
|
||||
OutputPorts = new Dictionary<string, PipelineNodeOutPort>();
|
||||
foreach (string port in inputNames) {
|
||||
InputPorts.Add(port, new PipelineNodeInPort(this, port));
|
||||
}
|
||||
foreach (string port in outputNames) {
|
||||
OutputPorts.Add(port, new PipelineNodeOutPort(this, port));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNative
|
||||
{
|
||||
get => _nativeRequested;
|
||||
internal set => _nativeRequested = value;
|
||||
}
|
||||
|
||||
public static Dictionary<string, PipelineConnector> EmptyPortLayer => _emptyLayer;
|
||||
|
||||
public IConvertableToPipelineNode NodeOperation => _mainOperation;
|
||||
|
||||
public int Level
|
||||
{
|
||||
get => _level;
|
||||
private set => _level = value;
|
||||
}
|
||||
|
||||
public static PipelineNode Empty => _emptyNode;
|
||||
|
||||
public PipelineNodeLabel Label => _label;
|
||||
|
||||
internal Dictionary<string, PipelineNodeInPort> InputPorts
|
||||
{
|
||||
get => _input;
|
||||
private set => _input = value;
|
||||
}
|
||||
|
||||
internal Dictionary<string, PipelineNodeOutPort> OutputPorts
|
||||
{
|
||||
get => _output;
|
||||
private set => _output = value;
|
||||
}
|
||||
|
||||
internal HashSet<PipelineNode> Previous { get; } = [];
|
||||
|
||||
internal HashSet<PipelineNode> Next { get; } = [];
|
||||
|
||||
internal IConvertableToPipelineNode Value => _mainOperation;
|
||||
|
||||
internal int RemainingIncreaseCount => Previous.Count - _count;
|
||||
|
||||
public static void Connect(PipelineNode front, string frontNodePort, string backNodePort, PipelineNode back)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(front);
|
||||
ArgumentNullException.ThrowIfNull(back);
|
||||
if (!front.OutputPorts
|
||||
.TryGetValue(frontNodePort,
|
||||
out PipelineNodeOutPort? frontPort)) {
|
||||
throw new ArgumentOutOfRangeException($"Cannot find port named {frontNodePort}");
|
||||
}
|
||||
if (!back.InputPorts.TryGetValue(backNodePort, out PipelineNodeInPort? backPort)) {
|
||||
throw new ArgumentOutOfRangeException($"Cannot find port named {backNodePort}");
|
||||
}
|
||||
backPort.Connect(frontPort);
|
||||
frontPort.Connect(backPort);
|
||||
}
|
||||
|
||||
public IEnumerator<PipelineNode> GetFormerNodes()
|
||||
{
|
||||
foreach (PipelineNodeInPort port in InputPorts.Values)
|
||||
{
|
||||
IEnumerator<PipelineNode> enumerator = port.GetFormerNodes();
|
||||
while (enumerator.MoveNext()) {
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Console.WriteLine(_label);
|
||||
PipelineNodeInPort.WaitAll(InputPorts);
|
||||
if (_mainOperation != null) {
|
||||
_mainOperation.NodeMain(in _input, in _output);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool TryIncreaseLevelTo(int level)
|
||||
{
|
||||
_count++;
|
||||
if (_count > Previous.Count) {
|
||||
return false;
|
||||
}
|
||||
Level = int.Max(level, this.Level);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static void DefaultOperation(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
#region port operation
|
||||
|
||||
public PipelineNodeInPort GetInputPort(string lable)
|
||||
{
|
||||
if (!InputPorts.TryGetValue(lable, out PipelineNodeInPort? ret)) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Cannot find the interface named {lable} in node {Label}.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort GetOutputPort(string lable)
|
||||
{
|
||||
if (!OutputPorts.TryGetValue(lable, out PipelineNodeOutPort? ret)) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
$"Cannot find the interface named {lable} in node {Label}.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PipelineNodeInPort? DefineCertainInputPort(string label)
|
||||
{
|
||||
if (InputPorts.ContainsKey(label)) {
|
||||
return null;
|
||||
}
|
||||
PipelineNodeInPort port = new(this, label);
|
||||
InputPorts[label] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort? DefineCertainOutputPort(string label)
|
||||
{
|
||||
if (OutputPorts.ContainsKey(label)) {
|
||||
return null;
|
||||
}
|
||||
PipelineNodeOutPort port = new(this, label);
|
||||
OutputPorts[label] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}//--------------------------------- end of class -----------------------------------
|
||||
|
||||
public class PipelineNodeRunException : Exception
|
||||
{
|
||||
|
||||
public PipelineNodeRunException() { }
|
||||
|
||||
public PipelineNodeRunException(Exception ex) : base(string.Empty, ex) { }
|
||||
|
||||
public PipelineNodeRunException(string message) : base(message) { }
|
||||
|
||||
public PipelineNodeRunException(string message, Exception innerException) : base(message, innerException) { }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNodeLabel"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public abstract record class PipelineNodeLabel
|
||||
{
|
||||
|
||||
public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only;
|
||||
|
||||
public abstract int ComputeHashCode();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ComputeHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public record class PipelineNodeStringLabel : PipelineNodeLabel
|
||||
{
|
||||
|
||||
public PipelineNodeStringLabel(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public override int ComputeHashCode()
|
||||
{
|
||||
return string.GetHashCode(Name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal record class PipelineNodeLabelEmpty : PipelineNodeLabel
|
||||
{
|
||||
|
||||
public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty();
|
||||
|
||||
public override int ComputeHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNodeLabel"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public abstract record class PipelineNodeLabel
|
||||
{
|
||||
|
||||
public static PipelineNodeLabel Empty => PipelineNodeLabelEmpty.Only;
|
||||
|
||||
public abstract int ComputeHashCode();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ComputeHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public record class PipelineNodeStringLabel : PipelineNodeLabel
|
||||
{
|
||||
|
||||
public PipelineNodeStringLabel(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public override int ComputeHashCode()
|
||||
{
|
||||
return string.GetHashCode(Name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal record class PipelineNodeLabelEmpty : PipelineNodeLabel
|
||||
{
|
||||
|
||||
public static PipelineNodeLabelEmpty Only { get; } = new PipelineNodeLabelEmpty();
|
||||
|
||||
public override int ComputeHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,187 +1,187 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNodePort"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class PipelineNodeInPort
|
||||
{
|
||||
|
||||
private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>();
|
||||
|
||||
public PipelineNodeInPort(PipelineNode container, string label)
|
||||
{
|
||||
Container = container;
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet;
|
||||
|
||||
public PipelineNode Container { get; init; }
|
||||
|
||||
public SemaphoreSlim Waiter { get; init; }
|
||||
|
||||
public string Label { get; init; }
|
||||
|
||||
internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet;
|
||||
|
||||
public void Connect(PipelineNodeOutPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
if (_linkerSet.Add(port))
|
||||
{
|
||||
port.Linkers.Add(this);
|
||||
Container.Previous.Add(port.Container);
|
||||
port.Container.Next.Add(Container);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<PipelineNode> GetFormerNodes()
|
||||
{
|
||||
foreach (PipelineNodeOutPort port in _linkerSet) {
|
||||
yield return port.Container;
|
||||
}
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
PipelineNodeOutPort.WaitAll(_linkerSet);
|
||||
}
|
||||
|
||||
public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeInPort port in ports.Values) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<PipelineNodeInPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeInPort port in ports) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PipelineNodeOutPort
|
||||
{
|
||||
|
||||
private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>();
|
||||
private volatile int _passCount;
|
||||
private SemaphoreSlim formerCompleteSemaphore;
|
||||
|
||||
public PipelineNodeOutPort(PipelineNode container, string label)
|
||||
{
|
||||
Container = container;
|
||||
Label = label;
|
||||
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
|
||||
}
|
||||
|
||||
public PipelineNode Container { get; init; }
|
||||
|
||||
public string Label { get; init; }
|
||||
|
||||
internal HashSet<PipelineNodeInPort> Linkers => _linkerSet;
|
||||
|
||||
private SemaphoreSlim Waiter => formerCompleteSemaphore;
|
||||
|
||||
public void Connect(PipelineNodeInPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
if (_linkerSet.Add(port))
|
||||
{
|
||||
port.Linkers.Add(this);
|
||||
Container.Next.Add(port.Container);
|
||||
port.Container.Previous.Add(Container);
|
||||
}
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (_passCount == 0)
|
||||
{
|
||||
Waiter.Release();
|
||||
} else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeOutPort port in ports.Values) {
|
||||
port.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void TransmitValue(IPipeLineConnectionPayload value)
|
||||
{
|
||||
foreach (PipelineNodeInPort item in _linkerSet) { }
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeOutPort port in ports) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Wait()
|
||||
{
|
||||
Waiter.Wait();
|
||||
if (_passCount >= _linkerSet.Count)
|
||||
{
|
||||
Interlocked.Exchange(ref _passCount, 0);
|
||||
} else
|
||||
{
|
||||
Interlocked.Increment(ref _passCount);
|
||||
Waiter.Release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineNodePort"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class PipelineNodeInPort
|
||||
{
|
||||
|
||||
private HashSet<PipelineNodeOutPort> _linkerSet = new HashSet<PipelineNodeOutPort>();
|
||||
|
||||
public PipelineNodeInPort(PipelineNode container, string label)
|
||||
{
|
||||
Container = container;
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public IEnumerable<PipelineNodeOutPort> LinkedPorts => _linkerSet;
|
||||
|
||||
public PipelineNode Container { get; init; }
|
||||
|
||||
public SemaphoreSlim Waiter { get; init; }
|
||||
|
||||
public string Label { get; init; }
|
||||
|
||||
internal HashSet<PipelineNodeOutPort> Linkers => _linkerSet;
|
||||
|
||||
public void Connect(PipelineNodeOutPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
if (_linkerSet.Add(port))
|
||||
{
|
||||
port.Linkers.Add(this);
|
||||
Container.Previous.Add(port.Container);
|
||||
port.Container.Next.Add(Container);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<PipelineNode> GetFormerNodes()
|
||||
{
|
||||
foreach (PipelineNodeOutPort port in _linkerSet) {
|
||||
yield return port.Container;
|
||||
}
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
PipelineNodeOutPort.WaitAll(_linkerSet);
|
||||
}
|
||||
|
||||
public static void WaitAll(IDictionary<string, PipelineNodeInPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeInPort port in ports.Values) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<PipelineNodeInPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeInPort port in ports) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PipelineNodeOutPort
|
||||
{
|
||||
|
||||
private HashSet<PipelineNodeInPort> _linkerSet = new HashSet<PipelineNodeInPort>();
|
||||
private volatile int _passCount;
|
||||
private SemaphoreSlim formerCompleteSemaphore;
|
||||
|
||||
public PipelineNodeOutPort(PipelineNode container, string label)
|
||||
{
|
||||
Container = container;
|
||||
Label = label;
|
||||
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
|
||||
}
|
||||
|
||||
public PipelineNode Container { get; init; }
|
||||
|
||||
public string Label { get; init; }
|
||||
|
||||
internal HashSet<PipelineNodeInPort> Linkers => _linkerSet;
|
||||
|
||||
private SemaphoreSlim Waiter => formerCompleteSemaphore;
|
||||
|
||||
public void Connect(PipelineNodeInPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
if (_linkerSet.Add(port))
|
||||
{
|
||||
port.Linkers.Add(this);
|
||||
Container.Next.Add(port.Container);
|
||||
port.Container.Previous.Add(Container);
|
||||
}
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (_passCount == 0)
|
||||
{
|
||||
Waiter.Release();
|
||||
} else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SkipAll(Dictionary<string, PipelineNodeOutPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeOutPort port in ports.Values) {
|
||||
port.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void TransmitValue(IPipeLineConnectionPayload value)
|
||||
{
|
||||
foreach (PipelineNodeInPort item in _linkerSet) { }
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<PipelineNodeOutPort> ports)
|
||||
{
|
||||
if (ports is null) {
|
||||
return;
|
||||
}
|
||||
foreach (PipelineNodeOutPort port in ports) {
|
||||
port.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Wait()
|
||||
{
|
||||
Waiter.Wait();
|
||||
if (_passCount >= _linkerSet.Count)
|
||||
{
|
||||
Interlocked.Exchange(ref _passCount, 0);
|
||||
} else
|
||||
{
|
||||
Interlocked.Increment(ref _passCount);
|
||||
Waiter.Release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,295 +1,295 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineScheduler"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
/// <summary>
|
||||
/// A sets of complex logic designed for running in multi threads environment. Warning: do not
|
||||
/// run a <see cref="PipelineScheduler" /> instance in multi threads more than once.
|
||||
/// </summary>
|
||||
public partial class PipelineScheduler : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private BeginningNode _beginning;
|
||||
private bool _isAvailable = false;
|
||||
private EndingNode _ending;
|
||||
|
||||
private int mtLevel, cLevel,mtIndex, cIndex;
|
||||
private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ;
|
||||
private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1);
|
||||
internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode;
|
||||
|
||||
public PipelineScheduler()
|
||||
{
|
||||
_allNode = new Dictionary<PipelineNodeLabel, PipelineNode>();
|
||||
runningStat = new SemaphoreSlim(1, 1);
|
||||
_beginning = new BeginningNode(this);
|
||||
_ending = new EndingNode(this);
|
||||
}
|
||||
/**/
|
||||
|
||||
public PipelineNode this[PipelineNodeLabel label]
|
||||
{
|
||||
get { return _allNode[label]; }
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get => runningStat.CurrentCount == 0;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName
|
||||
{
|
||||
get => _beginning.InputPorts.Keys;
|
||||
}
|
||||
|
||||
public IEnumerable<string> OutputPortName
|
||||
{
|
||||
get => _beginning.OutputPorts.Keys;
|
||||
}
|
||||
|
||||
public PipelineNode BeginLayer => _beginning;
|
||||
|
||||
public PipelineNode EndLayer => _ending;
|
||||
|
||||
public PipelineNodeOperation Operation
|
||||
{
|
||||
get => (this as IConvertableToPipelineNode).NodeMain;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
internal SemaphoreSlim RunningStat
|
||||
{
|
||||
get => runningStat;
|
||||
}
|
||||
|
||||
public void AddNode(PipelineNode node, bool isNative)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(node);
|
||||
node.IsNative = isNative;
|
||||
_allNode.TryAdd(node.Label, node);
|
||||
}
|
||||
|
||||
public void ArrangeNodes()
|
||||
{
|
||||
if (_isAvailable) {
|
||||
return;
|
||||
}
|
||||
int maxLevel = 1;
|
||||
Queue<PipelineNode> completedNodes = new Queue<PipelineNode>();
|
||||
foreach (PipelineNode item in _beginning.Next)
|
||||
{
|
||||
item.TryIncreaseLevelTo(1);
|
||||
completedNodes.Enqueue(item);
|
||||
}
|
||||
|
||||
// calc level of nodes O(V)
|
||||
while (completedNodes.Count > 0)
|
||||
{
|
||||
PipelineNode u = completedNodes.Dequeue();
|
||||
foreach (PipelineNode v in u.Next)
|
||||
{
|
||||
if (!v.TryIncreaseLevelTo(u.Level + 1)) {
|
||||
throw new InvalidOperationException("Ring in DAG detected.");
|
||||
}
|
||||
if (v.RemainingIncreaseCount == 0)
|
||||
{
|
||||
completedNodes.Enqueue(v);
|
||||
maxLevel = int.Max(maxLevel, v.Level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bucket sort of nodes, O(E)
|
||||
_concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1);
|
||||
_mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1);
|
||||
for (int i = 0; i <= maxLevel; i++)
|
||||
{
|
||||
_concurrentNodes.Add([]);
|
||||
_mainThreadNodes.Add([]);
|
||||
}
|
||||
foreach (PipelineNode item in _allNode.Values)
|
||||
{
|
||||
if (item.IsNative)
|
||||
{
|
||||
_mainThreadNodes[item.Level].Add(item);
|
||||
} else
|
||||
{
|
||||
_concurrentNodes[item.Level].Add(item);
|
||||
}
|
||||
}
|
||||
foreach (List<PipelineNode> item in _concurrentNodes) {
|
||||
item.TrimExcess();
|
||||
}
|
||||
foreach (List<PipelineNode> item in _mainThreadNodes) {
|
||||
item.TrimExcess();
|
||||
}
|
||||
_isAvailable = true;
|
||||
}
|
||||
|
||||
public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body)
|
||||
{
|
||||
if (_allNode.TryGetValue(label, out PipelineNode? node)) {
|
||||
throw new InvalidOperationException("Label has exist in scheduler.");
|
||||
}
|
||||
node = new PipelineNode(label, body, false);
|
||||
_allNode.Add(label, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public PipelineNode GetNode(PipelineNodeLabel label)
|
||||
{
|
||||
if (_allNode.TryGetValue(label, out PipelineNode? node))
|
||||
{
|
||||
return node;
|
||||
} else
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public void LinkToInput(PipelineNodeInPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
PipelineNodeOutPort p = _beginning.DefaultOut;
|
||||
p.Connect(port);
|
||||
}
|
||||
|
||||
public void LinkToInput(PipelineNodeInPort port, string inputPortName)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName);
|
||||
p.Connect(port);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters)
|
||||
{
|
||||
_beginning.SetInputData(parameters);
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
_concurrentLock.Wait();
|
||||
(mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
_concurrentLock.Release();
|
||||
}
|
||||
|
||||
public bool TryGetAllNode(
|
||||
out List<List<PipelineNode>> mainThreadNodes,
|
||||
out List<List<PipelineNode>> concurrentNodes)
|
||||
{
|
||||
if (_isAvailable)
|
||||
{
|
||||
mainThreadNodes = _mainThreadNodes;
|
||||
concurrentNodes = _concurrentNodes;
|
||||
return true;
|
||||
}
|
||||
mainThreadNodes = null;
|
||||
concurrentNodes = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool RequestNexConcurrentNode(out PipelineNode node)
|
||||
{
|
||||
int index, level;
|
||||
if (cLevel >= _concurrentNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
_concurrentLock.Wait();
|
||||
while (cIndex >= _concurrentNodes[cLevel].Count)
|
||||
{
|
||||
Interlocked.Increment(ref cLevel);
|
||||
Interlocked.Exchange(ref cIndex, 0);
|
||||
if (cLevel >= _concurrentNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
index = cIndex;
|
||||
level = cLevel;
|
||||
Interlocked.Increment(ref cIndex);
|
||||
_concurrentLock.Release();
|
||||
node = _concurrentNodes[level][index];
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool RequestNextMainThreadNode(out PipelineNode node)
|
||||
{
|
||||
if (mtLevel >= _mainThreadNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
while (mtIndex >= _mainThreadNodes[cLevel].Count)
|
||||
{
|
||||
Interlocked.Increment(ref mtLevel);
|
||||
Interlocked.Exchange(ref mtIndex, 0);
|
||||
if (mtLevel >= _mainThreadNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
node = _mainThreadNodes[mtLevel][mtIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void ResetIndex()
|
||||
{
|
||||
(mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0);
|
||||
_concurrentLock.Release();
|
||||
}
|
||||
|
||||
void IConvertableToPipelineNode.NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
PipeLineRunner.Run(this);
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
~PipelineScheduler()
|
||||
{
|
||||
runningStat.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PipelineScheduler"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
/// <summary>
|
||||
/// A sets of complex logic designed for running in multi threads environment. Warning: do not
|
||||
/// run a <see cref="PipelineScheduler" /> instance in multi threads more than once.
|
||||
/// </summary>
|
||||
public partial class PipelineScheduler : IConvertableToPipelineNode
|
||||
{
|
||||
|
||||
private BeginningNode _beginning;
|
||||
private bool _isAvailable = false;
|
||||
private EndingNode _ending;
|
||||
|
||||
private int mtLevel, cLevel,mtIndex, cIndex;
|
||||
private List<List<PipelineNode>> _concurrentNodes, _mainThreadNodes ;
|
||||
private SemaphoreSlim runningStat,_concurrentLock = new SemaphoreSlim(1, 1);
|
||||
internal Dictionary<PipelineNodeLabel, PipelineNode> _allNode;
|
||||
|
||||
public PipelineScheduler()
|
||||
{
|
||||
_allNode = new Dictionary<PipelineNodeLabel, PipelineNode>();
|
||||
runningStat = new SemaphoreSlim(1, 1);
|
||||
_beginning = new BeginningNode(this);
|
||||
_ending = new EndingNode(this);
|
||||
}
|
||||
/**/
|
||||
|
||||
public PipelineNode this[PipelineNodeLabel label]
|
||||
{
|
||||
get { return _allNode[label]; }
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get => runningStat.CurrentCount == 0;
|
||||
}
|
||||
|
||||
public IEnumerable<string> InputPortName
|
||||
{
|
||||
get => _beginning.InputPorts.Keys;
|
||||
}
|
||||
|
||||
public IEnumerable<string> OutputPortName
|
||||
{
|
||||
get => _beginning.OutputPorts.Keys;
|
||||
}
|
||||
|
||||
public PipelineNode BeginLayer => _beginning;
|
||||
|
||||
public PipelineNode EndLayer => _ending;
|
||||
|
||||
public PipelineNodeOperation Operation
|
||||
{
|
||||
get => (this as IConvertableToPipelineNode).NodeMain;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
internal SemaphoreSlim RunningStat
|
||||
{
|
||||
get => runningStat;
|
||||
}
|
||||
|
||||
public void AddNode(PipelineNode node, bool isNative)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(node);
|
||||
node.IsNative = isNative;
|
||||
_allNode.TryAdd(node.Label, node);
|
||||
}
|
||||
|
||||
public void ArrangeNodes()
|
||||
{
|
||||
if (_isAvailable) {
|
||||
return;
|
||||
}
|
||||
int maxLevel = 1;
|
||||
Queue<PipelineNode> completedNodes = new Queue<PipelineNode>();
|
||||
foreach (PipelineNode item in _beginning.Next)
|
||||
{
|
||||
item.TryIncreaseLevelTo(1);
|
||||
completedNodes.Enqueue(item);
|
||||
}
|
||||
|
||||
// calc level of nodes O(V)
|
||||
while (completedNodes.Count > 0)
|
||||
{
|
||||
PipelineNode u = completedNodes.Dequeue();
|
||||
foreach (PipelineNode v in u.Next)
|
||||
{
|
||||
if (!v.TryIncreaseLevelTo(u.Level + 1)) {
|
||||
throw new InvalidOperationException("Ring in DAG detected.");
|
||||
}
|
||||
if (v.RemainingIncreaseCount == 0)
|
||||
{
|
||||
completedNodes.Enqueue(v);
|
||||
maxLevel = int.Max(maxLevel, v.Level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bucket sort of nodes, O(E)
|
||||
_concurrentNodes = new List<List<PipelineNode>>(maxLevel + 1);
|
||||
_mainThreadNodes = new List<List<PipelineNode>>(maxLevel + 1);
|
||||
for (int i = 0; i <= maxLevel; i++)
|
||||
{
|
||||
_concurrentNodes.Add([]);
|
||||
_mainThreadNodes.Add([]);
|
||||
}
|
||||
foreach (PipelineNode item in _allNode.Values)
|
||||
{
|
||||
if (item.IsNative)
|
||||
{
|
||||
_mainThreadNodes[item.Level].Add(item);
|
||||
} else
|
||||
{
|
||||
_concurrentNodes[item.Level].Add(item);
|
||||
}
|
||||
}
|
||||
foreach (List<PipelineNode> item in _concurrentNodes) {
|
||||
item.TrimExcess();
|
||||
}
|
||||
foreach (List<PipelineNode> item in _mainThreadNodes) {
|
||||
item.TrimExcess();
|
||||
}
|
||||
_isAvailable = true;
|
||||
}
|
||||
|
||||
public PipelineNode Create(PipelineNodeLabel label, [NotNull] IConvertableToPipelineNode body)
|
||||
{
|
||||
if (_allNode.TryGetValue(label, out PipelineNode? node)) {
|
||||
throw new InvalidOperationException("Label has exist in scheduler.");
|
||||
}
|
||||
node = new PipelineNode(label, body, false);
|
||||
_allNode.Add(label, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public PipelineNode GetNode(PipelineNodeLabel label)
|
||||
{
|
||||
if (_allNode.TryGetValue(label, out PipelineNode? node))
|
||||
{
|
||||
return node;
|
||||
} else
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public void LinkToInput(PipelineNodeInPort port)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
PipelineNodeOutPort p = _beginning.DefaultOut;
|
||||
p.Connect(port);
|
||||
}
|
||||
|
||||
public void LinkToInput(PipelineNodeInPort port, string inputPortName)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(port);
|
||||
PipelineNodeOutPort p = _beginning.DefineCertainOutputPort(inputPortName);
|
||||
p.Connect(port);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetInput(params (string label, IPipeLineConnectionPayload arg)[] parameters)
|
||||
{
|
||||
_beginning.SetInputData(parameters);
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
_concurrentLock.Wait();
|
||||
(mtLevel, cLevel, mtIndex, cIndex) = (int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
_concurrentLock.Release();
|
||||
}
|
||||
|
||||
public bool TryGetAllNode(
|
||||
out List<List<PipelineNode>> mainThreadNodes,
|
||||
out List<List<PipelineNode>> concurrentNodes)
|
||||
{
|
||||
if (_isAvailable)
|
||||
{
|
||||
mainThreadNodes = _mainThreadNodes;
|
||||
concurrentNodes = _concurrentNodes;
|
||||
return true;
|
||||
}
|
||||
mainThreadNodes = null;
|
||||
concurrentNodes = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool RequestNexConcurrentNode(out PipelineNode node)
|
||||
{
|
||||
int index, level;
|
||||
if (cLevel >= _concurrentNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
_concurrentLock.Wait();
|
||||
while (cIndex >= _concurrentNodes[cLevel].Count)
|
||||
{
|
||||
Interlocked.Increment(ref cLevel);
|
||||
Interlocked.Exchange(ref cIndex, 0);
|
||||
if (cLevel >= _concurrentNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
index = cIndex;
|
||||
level = cLevel;
|
||||
Interlocked.Increment(ref cIndex);
|
||||
_concurrentLock.Release();
|
||||
node = _concurrentNodes[level][index];
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool RequestNextMainThreadNode(out PipelineNode node)
|
||||
{
|
||||
if (mtLevel >= _mainThreadNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
while (mtIndex >= _mainThreadNodes[cLevel].Count)
|
||||
{
|
||||
Interlocked.Increment(ref mtLevel);
|
||||
Interlocked.Exchange(ref mtIndex, 0);
|
||||
if (mtLevel >= _mainThreadNodes.Count)
|
||||
{
|
||||
node = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
node = _mainThreadNodes[mtLevel][mtIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void ResetIndex()
|
||||
{
|
||||
(mtLevel, cLevel, mtIndex, cIndex) = (0, 0, 0, 0);
|
||||
_concurrentLock.Release();
|
||||
}
|
||||
|
||||
void IConvertableToPipelineNode.NodeMain(
|
||||
in Dictionary<string, PipelineNodeInPort> input,
|
||||
in Dictionary<string, PipelineNodeOutPort> output)
|
||||
{
|
||||
PipeLineRunner.Run(this);
|
||||
PipelineNodeOutPort.SkipAll(output);
|
||||
}
|
||||
|
||||
~PipelineScheduler()
|
||||
{
|
||||
runningStat.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +1,123 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PortArgs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Authentication.ExtendedProtection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class PipelineConnector
|
||||
{
|
||||
|
||||
private protected IPipeLineConnectionPayload args;
|
||||
private protected PipelineNodeInPort output;
|
||||
private protected PipelineNodeOutPort input;
|
||||
private protected SemaphoreSlim formerCompleteSemaphore;
|
||||
|
||||
public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after)
|
||||
{
|
||||
this.input = former;
|
||||
this.output = after;
|
||||
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
|
||||
}
|
||||
|
||||
public IPipeLineConnectionPayload Args
|
||||
{
|
||||
get => args;
|
||||
set => args = value;
|
||||
}
|
||||
|
||||
public PipelineNodeInPort Output => output;
|
||||
|
||||
public PipelineNodeOutPort Input => input;
|
||||
|
||||
public static void SkipAll(Dictionary<string, PipelineConnector> ports)
|
||||
{
|
||||
if (ports == null) {
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<string, PipelineConnector> item in ports)
|
||||
{
|
||||
if (item.Value != null) {
|
||||
item.Value.CompleteAndSkip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WaitAll(Dictionary<string, PipelineConnector> ports)
|
||||
{
|
||||
if (ports == null) {
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<string, PipelineConnector> item in ports)
|
||||
{
|
||||
if (item.Value != null) {
|
||||
item.Value.WaitForStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public virtual void WaitForStart()
|
||||
{
|
||||
// Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}");
|
||||
formerCompleteSemaphore.Wait();
|
||||
}
|
||||
|
||||
~PipelineConnector()
|
||||
{
|
||||
formerCompleteSemaphore.Dispose();
|
||||
}
|
||||
|
||||
#region pipline op
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CompleteAndSkip()
|
||||
{
|
||||
// Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}");
|
||||
formerCompleteSemaphore.Release();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CompleteAndWriteData(IPipeLineConnectionPayload args)
|
||||
{
|
||||
this.args = args;
|
||||
CompleteAndSkip();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class IPipeLineConnectionPayload { }
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="PortArgs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Authentication.ExtendedProtection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
public class PipelineConnector
|
||||
{
|
||||
|
||||
private protected IPipeLineConnectionPayload args;
|
||||
private protected PipelineNodeInPort output;
|
||||
private protected PipelineNodeOutPort input;
|
||||
private protected SemaphoreSlim formerCompleteSemaphore;
|
||||
|
||||
public PipelineConnector(PipelineNodeOutPort former, PipelineNodeInPort after)
|
||||
{
|
||||
this.input = former;
|
||||
this.output = after;
|
||||
formerCompleteSemaphore = new SemaphoreSlim(initialCount:0, maxCount:1);
|
||||
}
|
||||
|
||||
public IPipeLineConnectionPayload Args
|
||||
{
|
||||
get => args;
|
||||
set => args = value;
|
||||
}
|
||||
|
||||
public PipelineNodeInPort Output => output;
|
||||
|
||||
public PipelineNodeOutPort Input => input;
|
||||
|
||||
public static void SkipAll(Dictionary<string, PipelineConnector> ports)
|
||||
{
|
||||
if (ports == null) {
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<string, PipelineConnector> item in ports)
|
||||
{
|
||||
if (item.Value != null) {
|
||||
item.Value.CompleteAndSkip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WaitAll(Dictionary<string, PipelineConnector> ports)
|
||||
{
|
||||
if (ports == null) {
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<string, PipelineConnector> item in ports)
|
||||
{
|
||||
if (item.Value != null) {
|
||||
item.Value.WaitForStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public virtual void WaitForStart()
|
||||
{
|
||||
// Console.WriteLine($"Waiting \t{input.ContextName}\t->\t{output.ContextName}");
|
||||
formerCompleteSemaphore.Wait();
|
||||
}
|
||||
|
||||
~PipelineConnector()
|
||||
{
|
||||
formerCompleteSemaphore.Dispose();
|
||||
}
|
||||
|
||||
#region pipline op
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CompleteAndSkip()
|
||||
{
|
||||
// Console.WriteLine($"Complete\t{input.Name}\t->\t{output.Name}");
|
||||
formerCompleteSemaphore.Release();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CompleteAndWriteData(IPipeLineConnectionPayload args)
|
||||
{
|
||||
this.args = args;
|
||||
CompleteAndSkip();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class IPipeLineConnectionPayload { }
|
||||
}
|
||||
|
||||
@@ -1,93 +1,93 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SideNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
internal sealed class BeginningNode : PipelineNode
|
||||
{
|
||||
|
||||
public const string OutName = "default";
|
||||
|
||||
private PipelineScheduler _bp;
|
||||
|
||||
public BeginningNode(PipelineScheduler bp)
|
||||
: base(
|
||||
DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ])
|
||||
{
|
||||
_bp = bp;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort DefaultOut => OutputPorts[OutName];
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
_bp.RunningStat.Wait();
|
||||
PipelineNodeOutPort.SkipAll(_output);
|
||||
}
|
||||
|
||||
internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data)
|
||||
{
|
||||
if (data is null) {
|
||||
return;
|
||||
}
|
||||
foreach ((string, IPipeLineConnectionPayload) item in data) {
|
||||
OutputPorts[item.Item1].TransmitValue(item.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class EndingNode : PipelineNode
|
||||
{
|
||||
|
||||
private PipelineScheduler _bp;
|
||||
|
||||
public EndingNode(PipelineScheduler bp)
|
||||
: base(
|
||||
DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ])
|
||||
{
|
||||
_bp = bp;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
PipelineNodeInPort.WaitAll(_input);
|
||||
_bp.ResetIndex();
|
||||
_bp.RunningStat.Release();
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SideNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.PipeLine
|
||||
{
|
||||
internal sealed class BeginningNode : PipelineNode
|
||||
{
|
||||
|
||||
public const string OutName = "default";
|
||||
|
||||
private PipelineScheduler _bp;
|
||||
|
||||
public BeginningNode(PipelineScheduler bp)
|
||||
: base(
|
||||
DefaultOperation, new PipelineNodeStringLabel("BeginningNode"), true, [ "default" ],[ OutName ])
|
||||
{
|
||||
_bp = bp;
|
||||
}
|
||||
|
||||
public PipelineNodeOutPort DefaultOut => OutputPorts[OutName];
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
_bp.RunningStat.Wait();
|
||||
PipelineNodeOutPort.SkipAll(_output);
|
||||
}
|
||||
|
||||
internal void SetInputData(IEnumerable<(string, IPipeLineConnectionPayload)> data)
|
||||
{
|
||||
if (data is null) {
|
||||
return;
|
||||
}
|
||||
foreach ((string, IPipeLineConnectionPayload) item in data) {
|
||||
OutputPorts[item.Item1].TransmitValue(item.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class EndingNode : PipelineNode
|
||||
{
|
||||
|
||||
private PipelineScheduler _bp;
|
||||
|
||||
public EndingNode(PipelineScheduler bp)
|
||||
: base(
|
||||
DefaultOperation, name:new PipelineNodeStringLabel("EndingNode"), true, [ "default" ],[ "default" ])
|
||||
{
|
||||
_bp = bp;
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
PipelineNodeInPort.WaitAll(_input);
|
||||
_bp.ResetIndex();
|
||||
_bp.RunningStat.Release();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
<PublishDir>C:\Users\Nitlo\OneDrive - MSFT\桌面\新建文件夹</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<History>False|2025-08-24T07:15:39.9650284Z||;</History>
|
||||
<LastFailureDetails />
|
||||
</PropertyGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<History>False|2025-08-24T07:15:39.9650284Z||;</History>
|
||||
<LastFailureDetails />
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,56 +1,56 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ISimpleStateMachine"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext>
|
||||
where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext>
|
||||
where TStateLabel : unmanaged, IEquatable<TStateLabel>
|
||||
where TStateContext : StateContext
|
||||
{
|
||||
|
||||
TStateNode this[
|
||||
TStateLabel label
|
||||
] { get; }
|
||||
|
||||
ref readonly TStateNode GetCurrentStateNode();
|
||||
|
||||
void Reset();
|
||||
|
||||
bool TryMoveNext(
|
||||
TStateContext ctx
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="ISimpleStateMachine"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public interface ISimpleStateMachine<TStateNode, TStateLabel, TStateContext>
|
||||
where TStateNode : SimpleStateMachineNode<TStateNode, TStateLabel, TStateContext>
|
||||
where TStateLabel : unmanaged, IEquatable<TStateLabel>
|
||||
where TStateContext : StateContext
|
||||
{
|
||||
|
||||
TStateNode this[
|
||||
TStateLabel label
|
||||
] { get; }
|
||||
|
||||
ref readonly TStateNode GetCurrentStateNode();
|
||||
|
||||
void Reset();
|
||||
|
||||
bool TryMoveNext(
|
||||
TStateContext ctx
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SimpleStateMachineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext>
|
||||
where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext>
|
||||
where TLabel : unmanaged, IEquatable<TLabel>
|
||||
where TStateContext : StateContext
|
||||
{
|
||||
|
||||
private readonly List<int> _jumpTable = [];
|
||||
|
||||
public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable);
|
||||
|
||||
public abstract bool IsValidateFrom(
|
||||
TSelf former,
|
||||
TStateContext context
|
||||
);
|
||||
|
||||
public abstract bool TryTransaction(
|
||||
ReadOnlySpan<TSelf> nodeSpan,
|
||||
TStateContext context,
|
||||
out TLabel label
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SimpleStateMachineNode"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public abstract class SimpleStateMachineNode<TSelf, TLabel, TStateContext>
|
||||
where TSelf : SimpleStateMachineNode<TSelf, TLabel, TStateContext>
|
||||
where TLabel : unmanaged, IEquatable<TLabel>
|
||||
where TStateContext : StateContext
|
||||
{
|
||||
|
||||
private readonly List<int> _jumpTable = [];
|
||||
|
||||
public ReadOnlySpan<int> AvailableNodes => CollectionsMarshal.AsSpan(_jumpTable);
|
||||
|
||||
public abstract bool IsValidateFrom(
|
||||
TSelf former,
|
||||
TStateContext context
|
||||
);
|
||||
|
||||
public abstract bool TryTransaction(
|
||||
ReadOnlySpan<TSelf> nodeSpan,
|
||||
TStateContext context,
|
||||
out TLabel label
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="StateContext.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public abstract class StateContext { }
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="StateContext.cs"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.State
|
||||
{
|
||||
public abstract class StateContext { }
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsNativeHost>true</IsNativeHost>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(IsWindows)'=='true'">
|
||||
<ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
|
||||
<GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Graphics\**" />
|
||||
<EmbeddedResource Remove="Graphics\**" />
|
||||
<None Remove="Graphics\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="HighPerformance\RegisterAbstraction\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsNativeHost>true</IsNativeHost>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(IsWindows)'=='true'">
|
||||
<ProjectReference Include="..\StgSharp.Native\$(NativeLibName).vcxproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
|
||||
<GlobalPropertiesToRemove>TargetFramework</GlobalPropertiesToRemove>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Graphics\**" />
|
||||
<EmbeddedResource Remove="Graphics\**" />
|
||||
<None Remove="Graphics\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="HighPerformance\RegisterAbstraction\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||
</PropertyGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,366 +1,366 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="AdaptiveBackoffStrategy"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
|
||||
/// system load and success rate
|
||||
/// </summary>
|
||||
public struct AdaptiveBackoffStrategy
|
||||
{
|
||||
|
||||
private const int EVALUATION_INTERVAL_MS = 2000; // Global re-evaluation interval
|
||||
private const int EXPLORATION_PROBABILITY = 10; // 10% probability to explore non-optimal strategies
|
||||
private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold
|
||||
|
||||
// Configuration constants
|
||||
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spin count for first few SpinWait iterations
|
||||
private const int MIN_SAMPLES_FOR_EVALUATION = 100; // Minimum sample count before starting evaluation
|
||||
|
||||
// Global shared state (all threads share learning results)
|
||||
private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3];
|
||||
private static volatile BackoffMethod s_globalBestMethod;
|
||||
private static volatile int s_evaluationInProgress;
|
||||
private static long s_lastGlobalEvaluationTicks;
|
||||
private BackoffMethod _currentMethod;
|
||||
private int _consecutiveFailures;
|
||||
private int _localAttempts;
|
||||
|
||||
// Instance state
|
||||
private SpinWait _spinWait;
|
||||
private uint _fastRng; // Lightweight pseudo-random number generator
|
||||
|
||||
// Static constructor: initialize optimal strategy based on system characteristics
|
||||
static AdaptiveBackoffStrategy()
|
||||
{
|
||||
// Based on your benchmark results:
|
||||
// - Low core count (<= 8): SpinWait has clear advantage
|
||||
// - Medium core count (<= 16): Close performance, slightly favor SpinWait
|
||||
// - High core count (>= 20): Sleep0 has significant advantage under high concurrency
|
||||
int logicalCores = Environment.ProcessorCount;
|
||||
|
||||
if (logicalCores <= 8)
|
||||
{
|
||||
s_globalBestMethod = BackoffMethod.SpinWait;
|
||||
} else if (logicalCores <= 16)
|
||||
{
|
||||
s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice
|
||||
} else
|
||||
{
|
||||
// High core count systems, based on your 64-thread benchmark results
|
||||
s_globalBestMethod = BackoffMethod.Sleep0;
|
||||
}
|
||||
|
||||
s_lastGlobalEvaluationTicks = Environment.TickCount64;
|
||||
|
||||
// Initialize statistics array
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
s_globalStats[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently used backoff method (for debugging/monitoring)
|
||||
/// </summary>
|
||||
public readonly BackoffMethod CurrentMethod => _currentMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the globally considered optimal backoff method
|
||||
/// </summary>
|
||||
public static BackoffMethod GlobalBestMethod => s_globalBestMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new adaptive backoff strategy instance
|
||||
/// </summary>
|
||||
public static AdaptiveBackoffStrategy Create()
|
||||
{
|
||||
return new AdaptiveBackoffStrategy
|
||||
{
|
||||
_spinWait = default,
|
||||
_currentMethod = s_globalBestMethod,
|
||||
_consecutiveFailures = 0,
|
||||
_localAttempts = 0,
|
||||
_fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets global statistics for the specified strategy (for monitoring)
|
||||
/// </summary>
|
||||
public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method)
|
||||
{
|
||||
PerformanceStats stats = s_globalStats[(int)method];
|
||||
return (stats.Attempts, stats.Successes, stats.SuccessRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after CAS failure, performs backoff and may switch strategies
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnFailureAndBackoff()
|
||||
{
|
||||
_consecutiveFailures++;
|
||||
|
||||
// Record failure
|
||||
s_globalStats[(int)_currentMethod].RecordAttempt(success:false);
|
||||
|
||||
// Periodically re-evaluate global optimal strategy
|
||||
if (ShouldEvaluateGlobalStrategy())
|
||||
{
|
||||
EvaluateAndUpdateGlobalStrategy();
|
||||
}
|
||||
|
||||
// Decide whether to switch strategies based on failure count and current strategy
|
||||
if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy())
|
||||
{
|
||||
SwitchToOptimalStrategy();
|
||||
}
|
||||
|
||||
// Perform the actual backoff operation
|
||||
PerformBackoff();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after CAS success, resets state
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnSuccess()
|
||||
{
|
||||
// Record success
|
||||
s_globalStats[(int)_currentMethod].RecordAttempt(success:true);
|
||||
|
||||
// Reset state
|
||||
_spinWait = default;
|
||||
_consecutiveFailures = 0;
|
||||
|
||||
// Fast convergence: tend to keep current strategy when continuously successful
|
||||
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD)
|
||||
{
|
||||
_localAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates and updates the global optimal strategy
|
||||
/// </summary>
|
||||
private static void EvaluateAndUpdateGlobalStrategy()
|
||||
{
|
||||
// Use CAS to ensure only one thread performs evaluation
|
||||
if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
long now = Environment.TickCount64;
|
||||
|
||||
// Double-check to avoid duplicate evaluation
|
||||
if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackoffMethod bestMethod = s_globalBestMethod;
|
||||
double bestSuccessRate = -1.0;
|
||||
|
||||
// Find the strategy with the highest success rate
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
PerformanceStats stats = s_globalStats[i];
|
||||
if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION)
|
||||
{
|
||||
double successRate = stats.SuccessRate;
|
||||
if (successRate > bestSuccessRate)
|
||||
{
|
||||
bestSuccessRate = successRate;
|
||||
bestMethod = (BackoffMethod)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update global optimal strategy
|
||||
s_globalBestMethod = bestMethod;
|
||||
Volatile.Write(ref s_lastGlobalEvaluationTicks, now);
|
||||
|
||||
// Reset statistics (maintain exploratory behavior)
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
s_globalStats[i].Reset();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref s_evaluationInProgress, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast pseudo-random number generator (LCG)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private uint NextRandom()
|
||||
{
|
||||
_fastRng = _fastRng * 1103515245U + 12345U;
|
||||
return _fastRng;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the actual backoff operation
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size
|
||||
private void PerformBackoff()
|
||||
{
|
||||
switch (_currentMethod)
|
||||
{
|
||||
case BackoffMethod.SpinWait:
|
||||
if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD)
|
||||
{
|
||||
_spinWait.SpinOnce(sleep1Threshold:-1);
|
||||
} else if (_spinWait.NextSpinWillYield)
|
||||
{
|
||||
int yields = (int)(NextRandom() & 7) + 1; // 1-8 times
|
||||
for (int i = 0; i < yields; i++) {
|
||||
_ = Thread.Yield();
|
||||
}
|
||||
} else
|
||||
{
|
||||
_spinWait.SpinOnce(sleep1Threshold:-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case BackoffMethod.Yield:
|
||||
_ = Thread.Yield();
|
||||
break;
|
||||
|
||||
case BackoffMethod.Sleep0:
|
||||
Thread.Sleep(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether global strategy evaluation should be performed
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool ShouldEvaluateGlobalStrategy()
|
||||
{
|
||||
long now = Environment.TickCount64;
|
||||
return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether strategy should be switched
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool ShouldSwitchStrategy()
|
||||
{
|
||||
// Fast convergence phase: reduce strategy switching
|
||||
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Small probability exploration: occasionally try other strategies even when current is optimal
|
||||
return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches to the currently considered optimal strategy
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SwitchToOptimalStrategy()
|
||||
{
|
||||
BackoffMethod targetMethod;
|
||||
|
||||
// Most of the time choose the globally optimal
|
||||
if (NextRandom() % 100 >= EXPLORATION_PROBABILITY)
|
||||
{
|
||||
targetMethod = s_globalBestMethod;
|
||||
} else
|
||||
{
|
||||
// Small probability random exploration
|
||||
targetMethod = (BackoffMethod)(NextRandom() % 3);
|
||||
}
|
||||
|
||||
if (targetMethod != _currentMethod)
|
||||
{
|
||||
_currentMethod = targetMethod;
|
||||
_spinWait = default; // Reset SpinWait state when switching strategies
|
||||
_localAttempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum BackoffMethod : byte
|
||||
{
|
||||
|
||||
SpinWait = 0,
|
||||
Yield = 1,
|
||||
Sleep0 = 2
|
||||
|
||||
}
|
||||
|
||||
private struct PerformanceStats
|
||||
{
|
||||
|
||||
public long Attempts;
|
||||
public long LastResetTicks;
|
||||
public long Successes;
|
||||
|
||||
public readonly double SuccessRate
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Attempts == 0 ? 0.0 : (double)Successes / Attempts;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RecordAttempt(bool success)
|
||||
{
|
||||
_ = Interlocked.Increment(ref Attempts);
|
||||
if (success) {
|
||||
_ = Interlocked.Increment(ref Successes);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
{
|
||||
Attempts = 0;
|
||||
Successes = 0;
|
||||
LastResetTicks = Environment.TickCount64;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="AdaptiveBackoffStrategy"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Efficient adaptive backoff strategy that dynamically selects optimal backoff methods based on
|
||||
/// system load and success rate
|
||||
/// </summary>
|
||||
public struct AdaptiveBackoffStrategy
|
||||
{
|
||||
|
||||
private const int EVALUATION_INTERVAL_MS = 2000; // Global re-evaluation interval
|
||||
private const int EXPLORATION_PROBABILITY = 10; // 10% probability to explore non-optimal strategies
|
||||
private const int FAST_CONVERGENCE_THRESHOLD = 1000; // Fast convergence threshold
|
||||
|
||||
// Configuration constants
|
||||
private const int INITIAL_SPINWAIT_THRESHOLD = 8; // Cheap spin count for first few SpinWait iterations
|
||||
private const int MIN_SAMPLES_FOR_EVALUATION = 100; // Minimum sample count before starting evaluation
|
||||
|
||||
// Global shared state (all threads share learning results)
|
||||
private static readonly PerformanceStats[] s_globalStats = new PerformanceStats[3];
|
||||
private static volatile BackoffMethod s_globalBestMethod;
|
||||
private static volatile int s_evaluationInProgress;
|
||||
private static long s_lastGlobalEvaluationTicks;
|
||||
private BackoffMethod _currentMethod;
|
||||
private int _consecutiveFailures;
|
||||
private int _localAttempts;
|
||||
|
||||
// Instance state
|
||||
private SpinWait _spinWait;
|
||||
private uint _fastRng; // Lightweight pseudo-random number generator
|
||||
|
||||
// Static constructor: initialize optimal strategy based on system characteristics
|
||||
static AdaptiveBackoffStrategy()
|
||||
{
|
||||
// Based on your benchmark results:
|
||||
// - Low core count (<= 8): SpinWait has clear advantage
|
||||
// - Medium core count (<= 16): Close performance, slightly favor SpinWait
|
||||
// - High core count (>= 20): Sleep0 has significant advantage under high concurrency
|
||||
int logicalCores = Environment.ProcessorCount;
|
||||
|
||||
if (logicalCores <= 8)
|
||||
{
|
||||
s_globalBestMethod = BackoffMethod.SpinWait;
|
||||
} else if (logicalCores <= 16)
|
||||
{
|
||||
s_globalBestMethod = BackoffMethod.SpinWait; // Default conservative choice
|
||||
} else
|
||||
{
|
||||
// High core count systems, based on your 64-thread benchmark results
|
||||
s_globalBestMethod = BackoffMethod.Sleep0;
|
||||
}
|
||||
|
||||
s_lastGlobalEvaluationTicks = Environment.TickCount64;
|
||||
|
||||
// Initialize statistics array
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
s_globalStats[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently used backoff method (for debugging/monitoring)
|
||||
/// </summary>
|
||||
public readonly BackoffMethod CurrentMethod => _currentMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the globally considered optimal backoff method
|
||||
/// </summary>
|
||||
public static BackoffMethod GlobalBestMethod => s_globalBestMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new adaptive backoff strategy instance
|
||||
/// </summary>
|
||||
public static AdaptiveBackoffStrategy Create()
|
||||
{
|
||||
return new AdaptiveBackoffStrategy
|
||||
{
|
||||
_spinWait = default,
|
||||
_currentMethod = s_globalBestMethod,
|
||||
_consecutiveFailures = 0,
|
||||
_localAttempts = 0,
|
||||
_fastRng = (uint)(Environment.TickCount64 ^ Thread.CurrentThread.ManagedThreadId * 0x9E3779B9U)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets global statistics for the specified strategy (for monitoring)
|
||||
/// </summary>
|
||||
public static (long attempts, long successes, double successRate) GetGlobalStats(BackoffMethod method)
|
||||
{
|
||||
PerformanceStats stats = s_globalStats[(int)method];
|
||||
return (stats.Attempts, stats.Successes, stats.SuccessRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after CAS failure, performs backoff and may switch strategies
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnFailureAndBackoff()
|
||||
{
|
||||
_consecutiveFailures++;
|
||||
|
||||
// Record failure
|
||||
s_globalStats[(int)_currentMethod].RecordAttempt(success:false);
|
||||
|
||||
// Periodically re-evaluate global optimal strategy
|
||||
if (ShouldEvaluateGlobalStrategy())
|
||||
{
|
||||
EvaluateAndUpdateGlobalStrategy();
|
||||
}
|
||||
|
||||
// Decide whether to switch strategies based on failure count and current strategy
|
||||
if (_consecutiveFailures >= INITIAL_SPINWAIT_THRESHOLD && ShouldSwitchStrategy())
|
||||
{
|
||||
SwitchToOptimalStrategy();
|
||||
}
|
||||
|
||||
// Perform the actual backoff operation
|
||||
PerformBackoff();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after CAS success, resets state
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnSuccess()
|
||||
{
|
||||
// Record success
|
||||
s_globalStats[(int)_currentMethod].RecordAttempt(success:true);
|
||||
|
||||
// Reset state
|
||||
_spinWait = default;
|
||||
_consecutiveFailures = 0;
|
||||
|
||||
// Fast convergence: tend to keep current strategy when continuously successful
|
||||
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD)
|
||||
{
|
||||
_localAttempts++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates and updates the global optimal strategy
|
||||
/// </summary>
|
||||
private static void EvaluateAndUpdateGlobalStrategy()
|
||||
{
|
||||
// Use CAS to ensure only one thread performs evaluation
|
||||
if (Interlocked.CompareExchange(ref s_evaluationInProgress, 1, 0) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
long now = Environment.TickCount64;
|
||||
|
||||
// Double-check to avoid duplicate evaluation
|
||||
if (now - Volatile.Read(ref s_lastGlobalEvaluationTicks) <= EVALUATION_INTERVAL_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackoffMethod bestMethod = s_globalBestMethod;
|
||||
double bestSuccessRate = -1.0;
|
||||
|
||||
// Find the strategy with the highest success rate
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
PerformanceStats stats = s_globalStats[i];
|
||||
if (stats.Attempts >= MIN_SAMPLES_FOR_EVALUATION)
|
||||
{
|
||||
double successRate = stats.SuccessRate;
|
||||
if (successRate > bestSuccessRate)
|
||||
{
|
||||
bestSuccessRate = successRate;
|
||||
bestMethod = (BackoffMethod)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update global optimal strategy
|
||||
s_globalBestMethod = bestMethod;
|
||||
Volatile.Write(ref s_lastGlobalEvaluationTicks, now);
|
||||
|
||||
// Reset statistics (maintain exploratory behavior)
|
||||
for (int i = 0; i < s_globalStats.Length; i++)
|
||||
{
|
||||
s_globalStats[i].Reset();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref s_evaluationInProgress, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast pseudo-random number generator (LCG)
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private uint NextRandom()
|
||||
{
|
||||
_fastRng = _fastRng * 1103515245U + 12345U;
|
||||
return _fastRng;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the actual backoff operation
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] // Avoid inlining to reduce hot path code size
|
||||
private void PerformBackoff()
|
||||
{
|
||||
switch (_currentMethod)
|
||||
{
|
||||
case BackoffMethod.SpinWait:
|
||||
if (_consecutiveFailures <= INITIAL_SPINWAIT_THRESHOLD)
|
||||
{
|
||||
_spinWait.SpinOnce(sleep1Threshold:-1);
|
||||
} else if (_spinWait.NextSpinWillYield)
|
||||
{
|
||||
int yields = (int)(NextRandom() & 7) + 1; // 1-8 times
|
||||
for (int i = 0; i < yields; i++) {
|
||||
_ = Thread.Yield();
|
||||
}
|
||||
} else
|
||||
{
|
||||
_spinWait.SpinOnce(sleep1Threshold:-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case BackoffMethod.Yield:
|
||||
_ = Thread.Yield();
|
||||
break;
|
||||
|
||||
case BackoffMethod.Sleep0:
|
||||
Thread.Sleep(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether global strategy evaluation should be performed
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool ShouldEvaluateGlobalStrategy()
|
||||
{
|
||||
long now = Environment.TickCount64;
|
||||
return now - Volatile.Read(ref s_lastGlobalEvaluationTicks) > EVALUATION_INTERVAL_MS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether strategy should be switched
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool ShouldSwitchStrategy()
|
||||
{
|
||||
// Fast convergence phase: reduce strategy switching
|
||||
if (_localAttempts < FAST_CONVERGENCE_THRESHOLD && _consecutiveFailures < 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Small probability exploration: occasionally try other strategies even when current is optimal
|
||||
return NextRandom() % 100 < EXPLORATION_PROBABILITY || _currentMethod != s_globalBestMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches to the currently considered optimal strategy
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SwitchToOptimalStrategy()
|
||||
{
|
||||
BackoffMethod targetMethod;
|
||||
|
||||
// Most of the time choose the globally optimal
|
||||
if (NextRandom() % 100 >= EXPLORATION_PROBABILITY)
|
||||
{
|
||||
targetMethod = s_globalBestMethod;
|
||||
} else
|
||||
{
|
||||
// Small probability random exploration
|
||||
targetMethod = (BackoffMethod)(NextRandom() % 3);
|
||||
}
|
||||
|
||||
if (targetMethod != _currentMethod)
|
||||
{
|
||||
_currentMethod = targetMethod;
|
||||
_spinWait = default; // Reset SpinWait state when switching strategies
|
||||
_localAttempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum BackoffMethod : byte
|
||||
{
|
||||
|
||||
SpinWait = 0,
|
||||
Yield = 1,
|
||||
Sleep0 = 2
|
||||
|
||||
}
|
||||
|
||||
private struct PerformanceStats
|
||||
{
|
||||
|
||||
public long Attempts;
|
||||
public long LastResetTicks;
|
||||
public long Successes;
|
||||
|
||||
public readonly double SuccessRate
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Attempts == 0 ? 0.0 : (double)Successes / Attempts;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RecordAttempt(bool success)
|
||||
{
|
||||
_ = Interlocked.Increment(ref Attempts);
|
||||
if (success) {
|
||||
_ = Interlocked.Increment(ref Successes);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
{
|
||||
Attempts = 0;
|
||||
Successes = 0;
|
||||
LastResetTicks = Environment.TickCount64;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +1,125 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BufferExpansionLock"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide a lock for managing concurrent access to a buffer that can be expanded and use a
|
||||
/// common two-pase-copy expanding mechanism.
|
||||
/// </summary>
|
||||
public class BufferExpansionLock : IDisposable
|
||||
{
|
||||
|
||||
private static readonly ThreadLocal<int> Count = new(() => 0);
|
||||
|
||||
private readonly ReaderWriterLockSlim _copyLock;
|
||||
private readonly ReaderWriterLockSlim _expansionLock;
|
||||
|
||||
public BufferExpansionLock()
|
||||
{
|
||||
_expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
_copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
}
|
||||
|
||||
public bool IsThreadReadingMetaData
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _expansionLock.IsReadLockHeld;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_expansionLock?.Dispose();
|
||||
_copyLock?.Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterBufferCopy()
|
||||
{
|
||||
_copyLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterBufferRead()
|
||||
{
|
||||
int c = Count.Value;
|
||||
if (c == 0) {
|
||||
_copyLock.EnterReadLock();
|
||||
}
|
||||
Count.Value = c + 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterExpansionProcess()
|
||||
{
|
||||
_expansionLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterMetaDataRead()
|
||||
{
|
||||
_expansionLock.EnterReadLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitBufferCopy()
|
||||
{
|
||||
_copyLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitBufferRead()
|
||||
{
|
||||
int c = Count.Value - 1;
|
||||
Count.Value = c;
|
||||
if (c == 0) {
|
||||
_copyLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitExpansionProcess()
|
||||
{
|
||||
_expansionLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitMetaDataRead()
|
||||
{
|
||||
_expansionLock.ExitReadLock();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="BufferExpansionLock"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide a lock for managing concurrent access to a buffer that can be expanded and use a
|
||||
/// common two-pase-copy expanding mechanism.
|
||||
/// </summary>
|
||||
public class BufferExpansionLock : IDisposable
|
||||
{
|
||||
|
||||
private static readonly ThreadLocal<int> Count = new(() => 0);
|
||||
|
||||
private readonly ReaderWriterLockSlim _copyLock;
|
||||
private readonly ReaderWriterLockSlim _expansionLock;
|
||||
|
||||
public BufferExpansionLock()
|
||||
{
|
||||
_expansionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
_copyLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
}
|
||||
|
||||
public bool IsThreadReadingMetaData
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _expansionLock.IsReadLockHeld;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_expansionLock?.Dispose();
|
||||
_copyLock?.Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterBufferCopy()
|
||||
{
|
||||
_copyLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterBufferRead()
|
||||
{
|
||||
int c = Count.Value;
|
||||
if (c == 0) {
|
||||
_copyLock.EnterReadLock();
|
||||
}
|
||||
Count.Value = c + 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterExpansionProcess()
|
||||
{
|
||||
_expansionLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EnterMetaDataRead()
|
||||
{
|
||||
_expansionLock.EnterReadLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitBufferCopy()
|
||||
{
|
||||
_copyLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitBufferRead()
|
||||
{
|
||||
int c = Count.Value - 1;
|
||||
Count.Value = c;
|
||||
if (c == 0) {
|
||||
_copyLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitExpansionProcess()
|
||||
{
|
||||
_expansionLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExitMetaDataRead()
|
||||
{
|
||||
_expansionLock.ExitReadLock();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,154 +1,154 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CancellableTask"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
public class CancellableTask : IDisposable
|
||||
{
|
||||
|
||||
private bool disposedValue;
|
||||
private CancellationTokenSource cts;
|
||||
private protected Action<CancellationToken> _startup;
|
||||
private protected Task _internalTask;
|
||||
|
||||
private protected CancellableTask()
|
||||
{
|
||||
cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private protected CancellableTask(Action<CancellationToken> action)
|
||||
{
|
||||
_internalTask = new Task(InternalAction);
|
||||
cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get => _internalTask.IsCompleted;
|
||||
}
|
||||
|
||||
public CancellationTokenSource Cts
|
||||
{
|
||||
get => cts;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get => _internalTask.Id;
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
Cts.Cancel();
|
||||
}
|
||||
|
||||
public void CancelAndWait()
|
||||
{
|
||||
Cts.Cancel();
|
||||
_internalTask.Wait();
|
||||
}
|
||||
|
||||
// ~CancellableTask()
|
||||
// {
|
||||
// Dispose(disposing: false);
|
||||
// }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing:true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static CancellableTask Run(Action<CancellationToken> startup)
|
||||
{
|
||||
CancellableTask ret = new CancellableTask
|
||||
{
|
||||
_startup = startup
|
||||
};
|
||||
ret._internalTask = Task.Run(ret.InternalAction);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_internalTask.Wait();
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<CancellableTask> tasks)
|
||||
{
|
||||
IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>(
|
||||
ct => ct._internalTask);
|
||||
Task.WaitAll(convertedTasks.ToArray());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void InternalAction()
|
||||
{
|
||||
_startup(Cts.Token);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (IsCompleted)
|
||||
{
|
||||
cts.Dispose();
|
||||
_internalTask.Dispose();
|
||||
} else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot dispose a cancellable task before completed");
|
||||
}
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator Task(CancellableTask task)
|
||||
{
|
||||
return task._internalTask;
|
||||
}
|
||||
|
||||
~CancellableTask()
|
||||
{
|
||||
Cts.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="CancellableTask"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
public class CancellableTask : IDisposable
|
||||
{
|
||||
|
||||
private bool disposedValue;
|
||||
private CancellationTokenSource cts;
|
||||
private protected Action<CancellationToken> _startup;
|
||||
private protected Task _internalTask;
|
||||
|
||||
private protected CancellableTask()
|
||||
{
|
||||
cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private protected CancellableTask(Action<CancellationToken> action)
|
||||
{
|
||||
_internalTask = new Task(InternalAction);
|
||||
cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get => _internalTask.IsCompleted;
|
||||
}
|
||||
|
||||
public CancellationTokenSource Cts
|
||||
{
|
||||
get => cts;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get => _internalTask.Id;
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
Cts.Cancel();
|
||||
}
|
||||
|
||||
public void CancelAndWait()
|
||||
{
|
||||
Cts.Cancel();
|
||||
_internalTask.Wait();
|
||||
}
|
||||
|
||||
// ~CancellableTask()
|
||||
// {
|
||||
// Dispose(disposing: false);
|
||||
// }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing:true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static CancellableTask Run(Action<CancellationToken> startup)
|
||||
{
|
||||
CancellableTask ret = new CancellableTask
|
||||
{
|
||||
_startup = startup
|
||||
};
|
||||
ret._internalTask = Task.Run(ret.InternalAction);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_internalTask.Wait();
|
||||
}
|
||||
|
||||
public static void WaitAll(IEnumerable<CancellableTask> tasks)
|
||||
{
|
||||
IEnumerable<Task> convertedTasks = tasks.Select<CancellableTask, Task>(
|
||||
ct => ct._internalTask);
|
||||
Task.WaitAll(convertedTasks.ToArray());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void InternalAction()
|
||||
{
|
||||
_startup(Cts.Token);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (IsCompleted)
|
||||
{
|
||||
cts.Dispose();
|
||||
_internalTask.Dispose();
|
||||
} else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot dispose a cancellable task before completed");
|
||||
}
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator Task(CancellableTask task)
|
||||
{
|
||||
return task._internalTask;
|
||||
}
|
||||
|
||||
~CancellableTask()
|
||||
{
|
||||
Cts.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="EmptyThread"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
public static partial class ThreadHelper
|
||||
{
|
||||
|
||||
private static readonly Thread _emptyThreadCache = new Thread(() => { });
|
||||
|
||||
static ThreadHelper()
|
||||
{
|
||||
_emptyThreadCache.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see
|
||||
/// langword="property" /> always returns false.
|
||||
/// </summary>
|
||||
public static Thread EmptyThread
|
||||
{
|
||||
get => _emptyThreadCache;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="EmptyThread"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
public static partial class ThreadHelper
|
||||
{
|
||||
|
||||
private static readonly Thread _emptyThreadCache = new Thread(() => { });
|
||||
|
||||
static ThreadHelper()
|
||||
{
|
||||
_emptyThreadCache.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An individual thread doing nothing, and its <see cref="Thread.IsAlive" /> <see
|
||||
/// langword="property" /> always returns false.
|
||||
/// </summary>
|
||||
public static Thread EmptyThread
|
||||
{
|
||||
get => _emptyThreadCache;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="MainThreadAssert"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading
|
||||
{
|
||||
public static partial class ThreadHelper
|
||||
{
|
||||
|
||||
private static readonly int mainThreadId = Environment.CurrentManagedThreadId;
|
||||
private static readonly ThreadLocal<bool> isNotMainThread = new(
|
||||
() => Environment.CurrentManagedThreadId != mainThreadId);
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void MainThreadAssert()
|
||||
{
|
||||
if (isNotMainThread.Value) {
|
||||
throw new InvalidOperationException(
|
||||
$"Attempt to call sensitive operation which can be only called from main thread from thread {Environment.CurrentManagedThreadId}.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="MainThreadAssert"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Threading
|
||||
{
|
||||
public static partial class ThreadHelper
|
||||
{
|
||||
|
||||
private static readonly int mainThreadId = Environment.CurrentManagedThreadId;
|
||||
private static readonly ThreadLocal<bool> isNotMainThread = new(
|
||||
() => Environment.CurrentManagedThreadId != mainThreadId);
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void MainThreadAssert()
|
||||
{
|
||||
if (isNotMainThread.Value) {
|
||||
throw new InvalidOperationException(
|
||||
$"Attempt to call sensitive operation which can be only called from main thread from thread {Environment.CurrentManagedThreadId}.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="NumaBuffer"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Common.Threading
|
||||
{
|
||||
public unsafe struct NumaBuffer<T> where T : unmanaged
|
||||
{
|
||||
|
||||
public readonly T* Buffers;
|
||||
public readonly int BufferColumnCapacity;
|
||||
public readonly int BufferRowCapacity;
|
||||
|
||||
public int CurrentColumnCount;
|
||||
public int CurrentRowCount;
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="NumaBuffer"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Common.Threading
|
||||
{
|
||||
public unsafe struct NumaBuffer<T> where T : unmanaged
|
||||
{
|
||||
|
||||
public readonly T* Buffers;
|
||||
public readonly int BufferColumnCapacity;
|
||||
public readonly int BufferRowCapacity;
|
||||
|
||||
public int CurrentColumnCount;
|
||||
public int CurrentRowCount;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,175 +1,175 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SingleConsumerBell"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Threading;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A single-consumer bell/signal primitive.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para> Producers can call <see cref="Set" /> multiple times to publish signals. The bell
|
||||
/// keeps a pending count of not-yet-consumed signals. The consumer uses <see
|
||||
/// cref="Wait(object, CancellationToken)" /> or <see cref="TryWait(object)" /> to consume
|
||||
/// signals.</para> <para> When a producer increments the pending count from 0 to 1, the
|
||||
/// internal event is set to wake the consumer. Consuming via <see cref="TryWait(object)" />
|
||||
/// or <see cref="Wait(object, CancellationToken)" /> clears the entire pending count
|
||||
/// (aggregate consumption). If new signals arrive while resetting, the event is set again to
|
||||
/// avoid missed signals.</para> <para> Ownership: only the specified <c> owner </c> can call
|
||||
/// the consumer APIs.</para>
|
||||
/// </remarks>
|
||||
public sealed class SingleConsumerBell : IDisposable
|
||||
{
|
||||
|
||||
private volatile bool _disposed;
|
||||
private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420
|
||||
private readonly ManualResetEventSlim _evt;
|
||||
private readonly object _owner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="SingleConsumerBell" />.
|
||||
/// </summary>
|
||||
/// <param name="owner">
|
||||
/// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
|
||||
/// reference.
|
||||
/// </param>
|
||||
/// <param name="spinCount">
|
||||
/// Spin count passed to the underlying <see cref="ManualResetEventSlim" />.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown if <paramref name="owner" /> is null.
|
||||
/// </exception>
|
||||
public SingleConsumerBell(object owner, int spinCount = 100)
|
||||
{
|
||||
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
||||
_evt = new ManualResetEventSlim(false, spinCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the bell, sets the event to wake any waiting consumer, and releases
|
||||
/// resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
_evt.Set();
|
||||
_evt.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a signal by incrementing the not-yet-consumed count and returns the current
|
||||
/// pending count.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the pending count transitions from 0 to 1, the event is set to wake the consumer.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// The current pending count including this call.
|
||||
/// </returns>
|
||||
public int Set()
|
||||
{
|
||||
if (_disposed) {
|
||||
return Volatile.Read(ref _pending);
|
||||
}
|
||||
|
||||
// Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer
|
||||
int newCount = Interlocked.Increment(ref _pending);
|
||||
if (newCount == 1) {
|
||||
_evt.Set();
|
||||
}
|
||||
return newCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to consume any pending signals without blocking.
|
||||
/// </summary>
|
||||
/// <param name="consumer">
|
||||
/// The consumer object; must be the same reference provided at construction.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c> true </c> if a signal was available (pending or event set); otherwise <c> false
|
||||
/// </c>.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="consumer" /> is not the owner.
|
||||
/// </exception>
|
||||
public bool TryWait(object consumer)
|
||||
{
|
||||
if (!ReferenceEquals(_owner, consumer)) {
|
||||
throw new InvalidOperationException("This bell is owned by another consumer.");
|
||||
}
|
||||
|
||||
if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Aggregate consumption: clear pending and reset event
|
||||
_ = Interlocked.Exchange(ref _pending, 0);
|
||||
_evt.Reset();
|
||||
|
||||
// If concurrent Set happened during reset, re-set the event to avoid lost signals
|
||||
if (Volatile.Read(ref _pending) != 0)
|
||||
{
|
||||
_evt.Set();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blocks until a signal is available, then consumes all pending signals.
|
||||
/// </summary>
|
||||
/// <param name="consumer">
|
||||
/// The consumer object; must be the same reference provided at construction.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A token to cancel the wait.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="consumer" /> is not the owner.
|
||||
/// </exception>
|
||||
public void Wait(object consumer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!ReferenceEquals(_owner, consumer)) {
|
||||
throw new InvalidOperationException("This bell is owned by another consumer.");
|
||||
}
|
||||
|
||||
_evt.Wait(cancellationToken);
|
||||
|
||||
// Consume this round of signals: clear pending and handle reset/concurrency
|
||||
_ = Interlocked.Exchange(ref _pending, 0);
|
||||
|
||||
_evt.Reset();
|
||||
|
||||
if (Volatile.Read(ref _pending) != 0) {
|
||||
_evt.Set();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="SingleConsumerBell"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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.Threading;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A single-consumer bell/signal primitive.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para> Producers can call <see cref="Set" /> multiple times to publish signals. The bell
|
||||
/// keeps a pending count of not-yet-consumed signals. The consumer uses <see
|
||||
/// cref="Wait(object, CancellationToken)" /> or <see cref="TryWait(object)" /> to consume
|
||||
/// signals.</para> <para> When a producer increments the pending count from 0 to 1, the
|
||||
/// internal event is set to wake the consumer. Consuming via <see cref="TryWait(object)" />
|
||||
/// or <see cref="Wait(object, CancellationToken)" /> clears the entire pending count
|
||||
/// (aggregate consumption). If new signals arrive while resetting, the event is set again to
|
||||
/// avoid missed signals.</para> <para> Ownership: only the specified <c> owner </c> can call
|
||||
/// the consumer APIs.</para>
|
||||
/// </remarks>
|
||||
public sealed class SingleConsumerBell : IDisposable
|
||||
{
|
||||
|
||||
private volatile bool _disposed;
|
||||
private int _pending; // not volatile: used with Interlocked/Volatile APIs to avoid CS0420
|
||||
private readonly ManualResetEventSlim _evt;
|
||||
private readonly object _owner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="SingleConsumerBell" />.
|
||||
/// </summary>
|
||||
/// <param name="owner">
|
||||
/// The unique consumer identity; subsequent calls to Wait/TryWait must pass the same
|
||||
/// reference.
|
||||
/// </param>
|
||||
/// <param name="spinCount">
|
||||
/// Spin count passed to the underlying <see cref="ManualResetEventSlim" />.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown if <paramref name="owner" /> is null.
|
||||
/// </exception>
|
||||
public SingleConsumerBell(object owner, int spinCount = 100)
|
||||
{
|
||||
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
||||
_evt = new ManualResetEventSlim(false, spinCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the bell, sets the event to wake any waiting consumer, and releases
|
||||
/// resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
_evt.Set();
|
||||
_evt.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a signal by incrementing the not-yet-consumed count and returns the current
|
||||
/// pending count.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the pending count transitions from 0 to 1, the event is set to wake the consumer.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// The current pending count including this call.
|
||||
/// </returns>
|
||||
public int Set()
|
||||
{
|
||||
if (_disposed) {
|
||||
return Volatile.Read(ref _pending);
|
||||
}
|
||||
|
||||
// Increment pending; if we transitioned 0 -> 1, set the event to wake the consumer
|
||||
int newCount = Interlocked.Increment(ref _pending);
|
||||
if (newCount == 1) {
|
||||
_evt.Set();
|
||||
}
|
||||
return newCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to consume any pending signals without blocking.
|
||||
/// </summary>
|
||||
/// <param name="consumer">
|
||||
/// The consumer object; must be the same reference provided at construction.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c> true </c> if a signal was available (pending or event set); otherwise <c> false
|
||||
/// </c>.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="consumer" /> is not the owner.
|
||||
/// </exception>
|
||||
public bool TryWait(object consumer)
|
||||
{
|
||||
if (!ReferenceEquals(_owner, consumer)) {
|
||||
throw new InvalidOperationException("This bell is owned by another consumer.");
|
||||
}
|
||||
|
||||
if (!_evt.IsSet && Volatile.Read(ref _pending) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Aggregate consumption: clear pending and reset event
|
||||
_ = Interlocked.Exchange(ref _pending, 0);
|
||||
_evt.Reset();
|
||||
|
||||
// If concurrent Set happened during reset, re-set the event to avoid lost signals
|
||||
if (Volatile.Read(ref _pending) != 0)
|
||||
{
|
||||
_evt.Set();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blocks until a signal is available, then consumes all pending signals.
|
||||
/// </summary>
|
||||
/// <param name="consumer">
|
||||
/// The consumer object; must be the same reference provided at construction.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A token to cancel the wait.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="consumer" /> is not the owner.
|
||||
/// </exception>
|
||||
public void Wait(object consumer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!ReferenceEquals(_owner, consumer)) {
|
||||
throw new InvalidOperationException("This bell is owned by another consumer.");
|
||||
}
|
||||
|
||||
_evt.Wait(cancellationToken);
|
||||
|
||||
// Consume this round of signals: clear pending and handle reset/concurrency
|
||||
_ = Interlocked.Exchange(ref _pending, 0);
|
||||
|
||||
_evt.Reset();
|
||||
|
||||
if (Volatile.Read(ref _pending) != 0) {
|
||||
_evt.Set();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,316 +1,316 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="VariableCapacitySemaphore"
|
||||
// Project: StgSharp.Common
|
||||
// AuthorGroup: Nitload
|
||||
// License: MIT
|
||||
// -----------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
|
||||
/// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
|
||||
/// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
|
||||
/// counting primitive (not a barrier). Permits accumulate up to <see cref="MaxPermits" />. -
|
||||
/// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
|
||||
/// future releases. - Striped MRES can reduce contention in highly parallel waits.
|
||||
/// </summary>
|
||||
public sealed class VariableCapacitySemaphore : IDisposable
|
||||
{
|
||||
|
||||
// MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
|
||||
private readonly ManualResetEventSlim[] _events;
|
||||
private volatile int _current;
|
||||
private volatile int _max;
|
||||
private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
|
||||
|
||||
/// <summary>
|
||||
/// Create a new variable-capacity semaphore.
|
||||
/// </summary>
|
||||
/// <param name="initialCount">
|
||||
/// Initial available permits (clamped to [0, maxPermits]).
|
||||
/// </param>
|
||||
/// <param name="maxPermits">
|
||||
/// Logical maximum permits (>= 1).
|
||||
/// </param>
|
||||
/// <param name="stripes">
|
||||
/// Optional stripe count for MRES (<= 0 to auto choose by CPU).
|
||||
/// </param>
|
||||
public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
|
||||
|
||||
_max = maxPermits;
|
||||
_current = Math.Min(initialCount, maxPermits);
|
||||
|
||||
int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
|
||||
_events = new ManualResetEventSlim[stripeCount];
|
||||
bool set = _current > 0;
|
||||
for (int i = 0; i < stripeCount; i++) {
|
||||
_events[i] = new ManualResetEventSlim(set, 0);
|
||||
}
|
||||
|
||||
_stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current number of available permits.
|
||||
/// </summary>
|
||||
public int CurrentCount => Volatile.Read(ref _current);
|
||||
|
||||
/// <summary>
|
||||
/// Logical maximum number of permits. Can be changed at runtime.
|
||||
/// </summary>
|
||||
public int MaxPermits
|
||||
{
|
||||
get => Volatile.Read(ref _max);
|
||||
set
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
|
||||
|
||||
Volatile.Write(ref _max, value);
|
||||
|
||||
// Clamp current to not exceed the new max (do not block; let Wait() consume down).
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur <= value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reflect availability to gates
|
||||
if (Volatile.Read(ref _current) > 0)
|
||||
{
|
||||
SetAll();
|
||||
} else
|
||||
{
|
||||
ResetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Release()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
int max = Volatile.Read(ref _max);
|
||||
if (cur >= max) {
|
||||
return cur;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
|
||||
{
|
||||
if (cur == 0) {
|
||||
SetAll();
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Release(int permits)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
int max = Volatile.Read(ref _max);
|
||||
if (cur >= max) {
|
||||
return cur;
|
||||
}
|
||||
|
||||
long target = (long)cur + permits;
|
||||
if (target > max) {
|
||||
target = max;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
|
||||
{
|
||||
if (cur == 0) {
|
||||
SetAll();
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to take a permit without blocking. Returns true if acquired.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryWait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available, then consume one.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Wait()
|
||||
{
|
||||
ManualResetEventSlim gate = GetStripeEvent();
|
||||
SpinWait spinner = new SpinWait();
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (!spinner.NextSpinWillYield)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
} else
|
||||
{
|
||||
gate.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available or canceled, then consume one.
|
||||
/// </summary>
|
||||
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ManualResetEventSlim gate = GetStripeEvent();
|
||||
SpinWait spinner = new SpinWait();
|
||||
int start = Environment.TickCount;
|
||||
int remaining = millisecondsTimeout;
|
||||
while (true)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (!spinner.NextSpinWillYield)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
if (millisecondsTimeout < 0)
|
||||
{
|
||||
gate.Wait(cancellationToken);
|
||||
continue;
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gate.Wait(remaining, cancellationToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
remaining = millisecondsTimeout - (Environment.TickCount - start);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ClampToPow2(int x)
|
||||
{
|
||||
if (x < 1) {
|
||||
x = 1;
|
||||
}
|
||||
|
||||
if (x > 8) {
|
||||
x = 8;
|
||||
}
|
||||
|
||||
int p = 1;
|
||||
while ((p << 1) <= x) {
|
||||
p <<= 1;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ManualResetEventSlim GetStripeEvent()
|
||||
{
|
||||
int id = Environment.CurrentManagedThreadId & 0x7fffffff;
|
||||
if (_stripeMask != -1) {
|
||||
return _events[id & _stripeMask];
|
||||
}
|
||||
|
||||
int idx = id % _events.Length;
|
||||
return _events[idx];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsPow2(int x) => (x & (x - 1)) == 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ResetAll()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetAll()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Set();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="VariableCapacitySemaphore"
|
||||
// Project: StgSharp.Common
|
||||
// AuthorGroup: Nitload
|
||||
// License: MIT
|
||||
// -----------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A counting semaphore with adjustable maximum capacity at runtime. Implemented using one or
|
||||
/// more ManualResetEventSlim (MRES) gates and atomic counters to avoid kernel semaphore
|
||||
/// overhead while supporting high-frequency Release/Wait patterns. Notes: - This is a
|
||||
/// counting primitive (not a barrier). Permits accumulate up to <see cref="MaxPermits" />. -
|
||||
/// Reducing <see cref="MaxPermits" /> does not revoke already issued permits; it only clamps
|
||||
/// future releases. - Striped MRES can reduce contention in highly parallel waits.
|
||||
/// </summary>
|
||||
public sealed class VariableCapacitySemaphore : IDisposable
|
||||
{
|
||||
|
||||
// MRES stripes (one gate per stripe). All stripes are set when transitioning 0 -> >0 and reset at >0 -> 0.
|
||||
private readonly ManualResetEventSlim[] _events;
|
||||
private volatile int _current;
|
||||
private volatile int _max;
|
||||
private readonly int _stripeMask; // for power-of-two stripe counts; otherwise -1
|
||||
|
||||
/// <summary>
|
||||
/// Create a new variable-capacity semaphore.
|
||||
/// </summary>
|
||||
/// <param name="initialCount">
|
||||
/// Initial available permits (clamped to [0, maxPermits]).
|
||||
/// </param>
|
||||
/// <param name="maxPermits">
|
||||
/// Logical maximum permits (>= 1).
|
||||
/// </param>
|
||||
/// <param name="stripes">
|
||||
/// Optional stripe count for MRES (<= 0 to auto choose by CPU).
|
||||
/// </param>
|
||||
public VariableCapacitySemaphore(int initialCount = 0, int maxPermits = 1, int stripes = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(maxPermits, 1);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(initialCount);
|
||||
|
||||
_max = maxPermits;
|
||||
_current = Math.Min(initialCount, maxPermits);
|
||||
|
||||
int stripeCount = stripes > 0 ? stripes : ClampToPow2(Math.Min(Environment.ProcessorCount, 8));
|
||||
_events = new ManualResetEventSlim[stripeCount];
|
||||
bool set = _current > 0;
|
||||
for (int i = 0; i < stripeCount; i++) {
|
||||
_events[i] = new ManualResetEventSlim(set, 0);
|
||||
}
|
||||
|
||||
_stripeMask = IsPow2(stripeCount) ? (stripeCount - 1) : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current number of available permits.
|
||||
/// </summary>
|
||||
public int CurrentCount => Volatile.Read(ref _current);
|
||||
|
||||
/// <summary>
|
||||
/// Logical maximum number of permits. Can be changed at runtime.
|
||||
/// </summary>
|
||||
public int MaxPermits
|
||||
{
|
||||
get => Volatile.Read(ref _max);
|
||||
set
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(value, 1);
|
||||
|
||||
Volatile.Write(ref _max, value);
|
||||
|
||||
// Clamp current to not exceed the new max (do not block; let Wait() consume down).
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur <= value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, value, cur) == cur)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reflect availability to gates
|
||||
if (Volatile.Read(ref _current) > 0)
|
||||
{
|
||||
SetAll();
|
||||
} else
|
||||
{
|
||||
ResetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a single permit if under <see cref="MaxPermits" />. Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Release()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
int max = Volatile.Read(ref _max);
|
||||
if (cur >= max) {
|
||||
return cur;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, cur + 1, cur) == cur)
|
||||
{
|
||||
if (cur == 0) {
|
||||
SetAll();
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release multiple permits (capped by <see cref="MaxPermits" />). Returns the previous
|
||||
/// count.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Release(int permits)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(permits);
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
int max = Volatile.Read(ref _max);
|
||||
if (cur >= max) {
|
||||
return cur;
|
||||
}
|
||||
|
||||
long target = (long)cur + permits;
|
||||
if (target > max) {
|
||||
target = max;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, (int)target, cur) == cur)
|
||||
{
|
||||
if (cur == 0) {
|
||||
SetAll();
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to take a permit without blocking. Returns true if acquired.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryWait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available, then consume one.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Wait()
|
||||
{
|
||||
ManualResetEventSlim gate = GetStripeEvent();
|
||||
SpinWait spinner = new SpinWait();
|
||||
while (true)
|
||||
{
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (!spinner.NextSpinWillYield)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
} else
|
||||
{
|
||||
gate.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Block until a permit is available or canceled, then consume one.
|
||||
/// </summary>
|
||||
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ManualResetEventSlim gate = GetStripeEvent();
|
||||
SpinWait spinner = new SpinWait();
|
||||
int start = Environment.TickCount;
|
||||
int remaining = millisecondsTimeout;
|
||||
while (true)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
int cur = Volatile.Read(ref _current);
|
||||
if (cur > 0 && Interlocked.CompareExchange(ref _current, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (!spinner.NextSpinWillYield)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
continue;
|
||||
}
|
||||
if (millisecondsTimeout < 0)
|
||||
{
|
||||
gate.Wait(cancellationToken);
|
||||
continue;
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gate.Wait(remaining, cancellationToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
remaining = millisecondsTimeout - (Environment.TickCount - start);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int ClampToPow2(int x)
|
||||
{
|
||||
if (x < 1) {
|
||||
x = 1;
|
||||
}
|
||||
|
||||
if (x > 8) {
|
||||
x = 8;
|
||||
}
|
||||
|
||||
int p = 1;
|
||||
while ((p << 1) <= x) {
|
||||
p <<= 1;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ManualResetEventSlim GetStripeEvent()
|
||||
{
|
||||
int id = Environment.CurrentManagedThreadId & 0x7fffffff;
|
||||
if (_stripeMask != -1) {
|
||||
return _events[id & _stripeMask];
|
||||
}
|
||||
|
||||
int idx = id % _events.Length;
|
||||
return _events[idx];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsPow2(int x) => (x & (x - 1)) == 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ResetAll()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetAll()
|
||||
{
|
||||
foreach (ManualResetEventSlim e in _events) {
|
||||
e.Set();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +1,135 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="StgSharpTime"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Collections;
|
||||
using StgSharp.Timing;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static partial class World
|
||||
{
|
||||
|
||||
public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
|
||||
|
||||
}
|
||||
|
||||
public sealed class StgSharpTime : TimeSourceProviderBase
|
||||
{
|
||||
|
||||
private static readonly LinearBag<TimeSpanProvider> _subscribers = new();
|
||||
private static readonly Stopwatch mainProvider = new();
|
||||
private static Thread timeProvideThread;
|
||||
|
||||
private StgSharpTime() { }
|
||||
|
||||
// Use Stopwatch frequency as base tick frequency (ticks per second).
|
||||
public override long Frequency => Stopwatch.Frequency;
|
||||
|
||||
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
|
||||
|
||||
public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
|
||||
|
||||
public sealed override void StartProvidingTime()
|
||||
{
|
||||
if (mainProvider.IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainProvider.Start();
|
||||
timeProvideThread = new Thread(ProvideTime)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "StgSharpTime"
|
||||
};
|
||||
timeProvideThread.Start();
|
||||
}
|
||||
|
||||
public override void StopProvidingTime()
|
||||
{
|
||||
lock (mainProvider) {
|
||||
mainProvider.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
lock (mainProvider) {
|
||||
mainProvider.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
|
||||
{
|
||||
_subscribers.Add(subscriber);
|
||||
}
|
||||
|
||||
protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
|
||||
{
|
||||
_subscribers.Remove(subscriber);
|
||||
}
|
||||
|
||||
private void ProvideTime()
|
||||
{
|
||||
long internalFrequency = Stopwatch.Frequency; // ticks per second
|
||||
lock (mainProvider) {
|
||||
mainProvider.Restart();
|
||||
}
|
||||
|
||||
while (mainProvider.IsRunning)
|
||||
{
|
||||
long elapsedTicks;
|
||||
lock (mainProvider) {
|
||||
elapsedTicks = mainProvider.ElapsedTicks;
|
||||
}
|
||||
if (_subscribers.Count == 0)
|
||||
{
|
||||
Thread.SpinWait(50);
|
||||
continue;
|
||||
}
|
||||
lock (SubscribeLock)
|
||||
{
|
||||
for (int i = 0; i < _subscribers.Count; i++)
|
||||
{
|
||||
if (!_subscribers[i].OnSpanTick(elapsedTicks))
|
||||
{
|
||||
// Console.WriteLine("removed");
|
||||
_subscribers.RemoveAt(i);
|
||||
i--; // adjust index after removal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: brief spin to avoid tight loop if frame lengths are large.
|
||||
Thread.SpinWait(20);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="StgSharpTime"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Collections;
|
||||
using StgSharp.Timing;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp
|
||||
{
|
||||
public static partial class World
|
||||
{
|
||||
|
||||
public static TimeSourceProviderBase MainTimeProvider => StgSharpTime.OnlyInstance;
|
||||
|
||||
}
|
||||
|
||||
public sealed class StgSharpTime : TimeSourceProviderBase
|
||||
{
|
||||
|
||||
private static readonly LinearBag<TimeSpanProvider> _subscribers = new();
|
||||
private static readonly Stopwatch mainProvider = new();
|
||||
private static Thread timeProvideThread;
|
||||
|
||||
private StgSharpTime() { }
|
||||
|
||||
// Use Stopwatch frequency as base tick frequency (ticks per second).
|
||||
public override long Frequency => Stopwatch.Frequency;
|
||||
|
||||
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
|
||||
|
||||
public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
|
||||
|
||||
public sealed override void StartProvidingTime()
|
||||
{
|
||||
if (mainProvider.IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainProvider.Start();
|
||||
timeProvideThread = new Thread(ProvideTime)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "StgSharpTime"
|
||||
};
|
||||
timeProvideThread.Start();
|
||||
}
|
||||
|
||||
public override void StopProvidingTime()
|
||||
{
|
||||
lock (mainProvider) {
|
||||
mainProvider.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
lock (mainProvider) {
|
||||
mainProvider.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
|
||||
{
|
||||
_subscribers.Add(subscriber);
|
||||
}
|
||||
|
||||
protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
|
||||
{
|
||||
_subscribers.Remove(subscriber);
|
||||
}
|
||||
|
||||
private void ProvideTime()
|
||||
{
|
||||
long internalFrequency = Stopwatch.Frequency; // ticks per second
|
||||
lock (mainProvider) {
|
||||
mainProvider.Restart();
|
||||
}
|
||||
|
||||
while (mainProvider.IsRunning)
|
||||
{
|
||||
long elapsedTicks;
|
||||
lock (mainProvider) {
|
||||
elapsedTicks = mainProvider.ElapsedTicks;
|
||||
}
|
||||
if (_subscribers.Count == 0)
|
||||
{
|
||||
Thread.SpinWait(50);
|
||||
continue;
|
||||
}
|
||||
lock (SubscribeLock)
|
||||
{
|
||||
for (int i = 0; i < _subscribers.Count; i++)
|
||||
{
|
||||
if (!_subscribers[i].OnSpanTick(elapsedTicks))
|
||||
{
|
||||
// Console.WriteLine("removed");
|
||||
_subscribers.RemoveAt(i);
|
||||
i--; // adjust index after removal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: brief spin to avoid tight loop if frame lengths are large.
|
||||
Thread.SpinWait(20);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,309 +1,309 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSequence"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Timing;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Common.Timing
|
||||
{
|
||||
public abstract class TimeSequence
|
||||
{
|
||||
|
||||
public long BeginningTick { get; protected set; }
|
||||
|
||||
public long EndingTick { get; protected set; }
|
||||
|
||||
public long PreviousFrameTick { get; protected set; }
|
||||
|
||||
public static TimeSequence CreateLinear(
|
||||
double totalSeconds,
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
if (double.IsPositiveInfinity(totalSeconds)) {
|
||||
return new InfiniteLinearTimeSequence(frameSeconds);
|
||||
}
|
||||
return new LinearTimeSequence(totalSeconds, frameSeconds);
|
||||
}
|
||||
|
||||
public static TimeSequence CreateLinearInfinite(
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
return new InfiniteLinearTimeSequence(frameSeconds);
|
||||
}
|
||||
|
||||
public static TimeSequence CreateLogarithmic(
|
||||
double initialSliceSeconds,
|
||||
double totalSeconds,
|
||||
double growthMultiplier,
|
||||
double minSliceSeconds
|
||||
) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
|
||||
minSliceSeconds);
|
||||
|
||||
public abstract bool EndsAt(
|
||||
long currentTick
|
||||
);
|
||||
|
||||
public abstract bool IsNextFrameReady(
|
||||
long currentTick
|
||||
);
|
||||
|
||||
public abstract void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
internal sealed class LinearTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private readonly double _frameSeconds;
|
||||
private readonly double _totalSeconds;
|
||||
private long _frameLengthTicks;
|
||||
|
||||
public LinearTimeSequence(
|
||||
double totalSeconds,
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
|
||||
if (frameSeconds > totalSeconds) {
|
||||
throw new ArgumentException("Frame length greater than total length.");
|
||||
}
|
||||
_totalSeconds = totalSeconds;
|
||||
_frameSeconds = frameSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
) => currentTick >= EndingTick;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
long span = currentTick - PreviousFrameTick;
|
||||
if (span >= _frameLengthTicks && currentTick < EndingTick)
|
||||
{
|
||||
PreviousFrameTick += _frameLengthTicks;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
|
||||
long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
EndingTick = BeginningTick + totalTicks;
|
||||
EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
|
||||
PreviousFrameTick = BeginningTick;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logarithmic time sequence with growth factor and minimum slice length enforcement.
|
||||
/// </summary>
|
||||
internal sealed class LogTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private bool _finalEmitted;
|
||||
private readonly double _initialSeconds;
|
||||
private double _lastBoundarySecondsAdjusted;
|
||||
private readonly double _minSliceSeconds;
|
||||
private readonly double _multiplier;
|
||||
private double _nextBoundarySecondsAdjusted;
|
||||
private readonly double _totalSeconds;
|
||||
private int _sliceIndex;
|
||||
private long _freq;
|
||||
private long _nextBoundaryTick;
|
||||
private long _totalTicks;
|
||||
|
||||
public LogTimeSequence(
|
||||
double initialSliceSeconds,
|
||||
double totalSeconds,
|
||||
double growthMultiplier,
|
||||
double minSliceSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
|
||||
if (growthMultiplier < 1.0) {
|
||||
throw new ArgumentException("growthMultiplier must be >= 1");
|
||||
}
|
||||
if (initialSliceSeconds > totalSeconds) {
|
||||
throw new ArgumentException("Initial slice length greater than total length.");
|
||||
}
|
||||
_initialSeconds = initialSliceSeconds;
|
||||
_totalSeconds = totalSeconds;
|
||||
_multiplier = growthMultiplier;
|
||||
_minSliceSeconds = minSliceSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
return currentTick >= EndingTick;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
// Emit final once when reaching or passing end.
|
||||
if (currentTick >= EndingTick)
|
||||
{
|
||||
if (_finalEmitted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PreviousFrameTick = EndingTick;
|
||||
_finalEmitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not reached end: only when we cross the next boundary tick do we emit.
|
||||
if (currentTick < _nextBoundaryTick)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance to this boundary.
|
||||
PreviousFrameTick = _nextBoundaryTick;
|
||||
_lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
|
||||
_sliceIndex++;
|
||||
|
||||
// Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap.
|
||||
double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
|
||||
if (next > _totalSeconds) {
|
||||
next = _totalSeconds;
|
||||
}
|
||||
|
||||
double delta = next - _lastBoundarySecondsAdjusted;
|
||||
if (delta < _minSliceSeconds)
|
||||
{
|
||||
next = _lastBoundarySecondsAdjusted + _minSliceSeconds;
|
||||
if (next > _totalSeconds) {
|
||||
next = _totalSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
_nextBoundarySecondsAdjusted = next;
|
||||
_nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ?
|
||||
EndingTick :
|
||||
BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
_freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
_totalTicks = SecondsToTicks(_totalSeconds);
|
||||
EndingTick = BeginningTick + _totalTicks;
|
||||
_sliceIndex = 0;
|
||||
_lastBoundarySecondsAdjusted = 0.0;
|
||||
_nextBoundarySecondsAdjusted = _initialSeconds;
|
||||
_nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
|
||||
PreviousFrameTick = BeginningTick;
|
||||
_finalEmitted = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private long SecondsToTicks(
|
||||
double seconds
|
||||
) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Infinite linear time sequence: emits frames at fixed interval and never ends.
|
||||
/// </summary>
|
||||
internal sealed class InfiniteLinearTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private readonly double _frameSeconds;
|
||||
private long _frameLengthTicks;
|
||||
|
||||
public InfiniteLinearTimeSequence(
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
|
||||
_frameSeconds = frameSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
) => false; // never ends
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
long span = currentTick - PreviousFrameTick;
|
||||
if (span >= _frameLengthTicks)
|
||||
{
|
||||
PreviousFrameTick += _frameLengthTicks;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
EndingTick = long.MaxValue; // sentinel
|
||||
PreviousFrameTick = BeginningTick;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSequence"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Timing;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace StgSharp.Common.Timing
|
||||
{
|
||||
public abstract class TimeSequence
|
||||
{
|
||||
|
||||
public long BeginningTick { get; protected set; }
|
||||
|
||||
public long EndingTick { get; protected set; }
|
||||
|
||||
public long PreviousFrameTick { get; protected set; }
|
||||
|
||||
public static TimeSequence CreateLinear(
|
||||
double totalSeconds,
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
if (double.IsPositiveInfinity(totalSeconds)) {
|
||||
return new InfiniteLinearTimeSequence(frameSeconds);
|
||||
}
|
||||
return new LinearTimeSequence(totalSeconds, frameSeconds);
|
||||
}
|
||||
|
||||
public static TimeSequence CreateLinearInfinite(
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
return new InfiniteLinearTimeSequence(frameSeconds);
|
||||
}
|
||||
|
||||
public static TimeSequence CreateLogarithmic(
|
||||
double initialSliceSeconds,
|
||||
double totalSeconds,
|
||||
double growthMultiplier,
|
||||
double minSliceSeconds
|
||||
) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
|
||||
minSliceSeconds);
|
||||
|
||||
public abstract bool EndsAt(
|
||||
long currentTick
|
||||
);
|
||||
|
||||
public abstract bool IsNextFrameReady(
|
||||
long currentTick
|
||||
);
|
||||
|
||||
public abstract void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
internal sealed class LinearTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private readonly double _frameSeconds;
|
||||
private readonly double _totalSeconds;
|
||||
private long _frameLengthTicks;
|
||||
|
||||
public LinearTimeSequence(
|
||||
double totalSeconds,
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
|
||||
if (frameSeconds > totalSeconds) {
|
||||
throw new ArgumentException("Frame length greater than total length.");
|
||||
}
|
||||
_totalSeconds = totalSeconds;
|
||||
_frameSeconds = frameSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
) => currentTick >= EndingTick;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
long span = currentTick - PreviousFrameTick;
|
||||
if (span >= _frameLengthTicks && currentTick < EndingTick)
|
||||
{
|
||||
PreviousFrameTick += _frameLengthTicks;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
|
||||
long totalTicks = (long)Math.Max(_frameLengthTicks, Math.Round(_totalSeconds * f, MidpointRounding.AwayFromZero));
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
EndingTick = BeginningTick + totalTicks;
|
||||
EndingTick = EndingTick < 0 ? long.MaxValue : EndingTick;
|
||||
PreviousFrameTick = BeginningTick;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logarithmic time sequence with growth factor and minimum slice length enforcement.
|
||||
/// </summary>
|
||||
internal sealed class LogTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private bool _finalEmitted;
|
||||
private readonly double _initialSeconds;
|
||||
private double _lastBoundarySecondsAdjusted;
|
||||
private readonly double _minSliceSeconds;
|
||||
private readonly double _multiplier;
|
||||
private double _nextBoundarySecondsAdjusted;
|
||||
private readonly double _totalSeconds;
|
||||
private int _sliceIndex;
|
||||
private long _freq;
|
||||
private long _nextBoundaryTick;
|
||||
private long _totalTicks;
|
||||
|
||||
public LogTimeSequence(
|
||||
double initialSliceSeconds,
|
||||
double totalSeconds,
|
||||
double growthMultiplier,
|
||||
double minSliceSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialSliceSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalSeconds);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(growthMultiplier);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minSliceSeconds);
|
||||
if (growthMultiplier < 1.0) {
|
||||
throw new ArgumentException("growthMultiplier must be >= 1");
|
||||
}
|
||||
if (initialSliceSeconds > totalSeconds) {
|
||||
throw new ArgumentException("Initial slice length greater than total length.");
|
||||
}
|
||||
_initialSeconds = initialSliceSeconds;
|
||||
_totalSeconds = totalSeconds;
|
||||
_multiplier = growthMultiplier;
|
||||
_minSliceSeconds = minSliceSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
return currentTick >= EndingTick;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
// Emit final once when reaching or passing end.
|
||||
if (currentTick >= EndingTick)
|
||||
{
|
||||
if (_finalEmitted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PreviousFrameTick = EndingTick;
|
||||
_finalEmitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not reached end: only when we cross the next boundary tick do we emit.
|
||||
if (currentTick < _nextBoundaryTick)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance to this boundary.
|
||||
PreviousFrameTick = _nextBoundaryTick;
|
||||
_lastBoundarySecondsAdjusted = _nextBoundarySecondsAdjusted;
|
||||
_sliceIndex++;
|
||||
|
||||
// Compute next cumulative boundary seconds with multiplier, then enforce min-slice and total cap.
|
||||
double next = _initialSeconds * Math.Pow(_multiplier, _sliceIndex);
|
||||
if (next > _totalSeconds) {
|
||||
next = _totalSeconds;
|
||||
}
|
||||
|
||||
double delta = next - _lastBoundarySecondsAdjusted;
|
||||
if (delta < _minSliceSeconds)
|
||||
{
|
||||
next = _lastBoundarySecondsAdjusted + _minSliceSeconds;
|
||||
if (next > _totalSeconds) {
|
||||
next = _totalSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
_nextBoundarySecondsAdjusted = next;
|
||||
_nextBoundaryTick = (_nextBoundarySecondsAdjusted >= _totalSeconds) ?
|
||||
EndingTick :
|
||||
BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
_freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
_totalTicks = SecondsToTicks(_totalSeconds);
|
||||
EndingTick = BeginningTick + _totalTicks;
|
||||
_sliceIndex = 0;
|
||||
_lastBoundarySecondsAdjusted = 0.0;
|
||||
_nextBoundarySecondsAdjusted = _initialSeconds;
|
||||
_nextBoundaryTick = BeginningTick + SecondsToTicks(_nextBoundarySecondsAdjusted);
|
||||
PreviousFrameTick = BeginningTick;
|
||||
_finalEmitted = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private long SecondsToTicks(
|
||||
double seconds
|
||||
) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Infinite linear time sequence: emits frames at fixed interval and never ends.
|
||||
/// </summary>
|
||||
internal sealed class InfiniteLinearTimeSequence : TimeSequence
|
||||
{
|
||||
|
||||
private readonly double _frameSeconds;
|
||||
private long _frameLengthTicks;
|
||||
|
||||
public InfiniteLinearTimeSequence(
|
||||
double frameSeconds
|
||||
)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(frameSeconds);
|
||||
_frameSeconds = frameSeconds;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool EndsAt(
|
||||
long currentTick
|
||||
) => false; // never ends
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool IsNextFrameReady(
|
||||
long currentTick
|
||||
)
|
||||
{
|
||||
long span = currentTick - PreviousFrameTick;
|
||||
if (span >= _frameLengthTicks)
|
||||
{
|
||||
PreviousFrameTick += _frameLengthTicks;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void StartFrom(
|
||||
TimeSourceProviderBase source
|
||||
)
|
||||
{
|
||||
long f = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
|
||||
_frameLengthTicks = (long)Math.Max(1, _frameSeconds * f);
|
||||
BeginningTick = source.GetCurrentTimeSpanTick();
|
||||
EndingTick = long.MaxValue; // sentinel
|
||||
PreviousFrameTick = BeginningTick;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSourceProvider"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
public abstract class TimeSourceProviderBase
|
||||
{
|
||||
|
||||
public abstract long Frequency { get; }
|
||||
|
||||
protected object SubscribeLock { get; } = new();
|
||||
|
||||
public void AddSubscriber(TimeSpanProvider subscriber)
|
||||
{
|
||||
lock (SubscribeLock) {
|
||||
AddSubscriberUnsynced(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract long GetCurrentTimeSpanTick();
|
||||
|
||||
public void RemoveSubscriber(TimeSpanProvider subscriber)
|
||||
{
|
||||
lock (SubscribeLock) {
|
||||
RemoveSubscriberUnsynced(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void StartProvidingTime();
|
||||
|
||||
public abstract void StopProvidingTime();
|
||||
|
||||
protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber);
|
||||
|
||||
protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber);
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSourceProvider"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
public abstract class TimeSourceProviderBase
|
||||
{
|
||||
|
||||
public abstract long Frequency { get; }
|
||||
|
||||
protected object SubscribeLock { get; } = new();
|
||||
|
||||
public void AddSubscriber(TimeSpanProvider subscriber)
|
||||
{
|
||||
lock (SubscribeLock) {
|
||||
AddSubscriberUnsynced(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract long GetCurrentTimeSpanTick();
|
||||
|
||||
public void RemoveSubscriber(TimeSpanProvider subscriber)
|
||||
{
|
||||
lock (SubscribeLock) {
|
||||
RemoveSubscriberUnsynced(subscriber);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void StartProvidingTime();
|
||||
|
||||
public abstract void StopProvidingTime();
|
||||
|
||||
protected abstract void AddSubscriberUnsynced(TimeSpanProvider subscriber);
|
||||
|
||||
protected abstract void RemoveSubscriberUnsynced(TimeSpanProvider subscriber);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSpanProvider.Awaiting"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics.Memory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
public partial class TimeSpanProvider
|
||||
{
|
||||
|
||||
private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8);
|
||||
|
||||
private static int s_maxID;
|
||||
|
||||
private EventHandler? _refreshHandler, _missHandler;
|
||||
private int _size, _id;
|
||||
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
|
||||
|
||||
private readonly ManualResetEventSlim _event = new();
|
||||
private readonly object _participantObject = new();
|
||||
|
||||
public event EventHandler MissTimeSpanRefreshed
|
||||
{
|
||||
add => _missHandler += value;
|
||||
remove => _missHandler -= value;
|
||||
}
|
||||
|
||||
public event EventHandler TimeSpanRefreshed
|
||||
{
|
||||
add => _refreshHandler += value;
|
||||
remove => _refreshHandler -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if there is at least one unconsumed span tick (PendingSpanCount > 0).
|
||||
/// </summary>
|
||||
public bool HasPendingSpan => PendingSpanCount > 0;
|
||||
|
||||
public int TokenID
|
||||
{
|
||||
get => _id;
|
||||
private set => _id = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of span ticks not yet consumed by Wait ( > 0 means last tick not waited).
|
||||
/// </summary>
|
||||
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
|
||||
|
||||
internal ManualResetEventSlim AwaitingEvent
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _event;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// mark provider as ended so external users can observe terminal state
|
||||
Volatile.Write(ref _endedFlag, 1);
|
||||
s_unusedID.Push(TokenID);
|
||||
_event.Set();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _id;
|
||||
|
||||
public void Participant()
|
||||
{
|
||||
lock (_participantObject)
|
||||
{
|
||||
// Increase the maximum concurrent consumers for the next tick.
|
||||
_size++;
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitNextSpan()
|
||||
{
|
||||
_event.Wait();
|
||||
|
||||
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0)
|
||||
{
|
||||
// Last consumer resets the gate for the next tick.
|
||||
_event.Reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WaitNextSpanAsync()
|
||||
{
|
||||
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
_event.Reset();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws
|
||||
/// OperationCanceledException when token is cancelled. If the provider is disposed during
|
||||
/// wait an OperationCanceledException is thrown to indicate end.
|
||||
/// </summary>
|
||||
public Task WaitNextSpanAsync(CancellationToken ct)
|
||||
{
|
||||
// Block the current thread until the event is signaled or cancellation requested.
|
||||
_event.Wait(ct);
|
||||
|
||||
// consume one pending span (same logic as non-cancellable variant)
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
_event.Reset();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="currentTick">
|
||||
/// Tick passed since binded time source.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the span provider is working. False if time is over (the sequence ended).
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal bool OnSpanTick(long currentTick)
|
||||
{
|
||||
if (_sequence.EndsAt(currentTick))
|
||||
{
|
||||
// mark ended due to sequence timeout, then dispose
|
||||
Volatile.Write(ref _endedFlag, 1);
|
||||
|
||||
// Console.WriteLine("Time span ended.");
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
if (!_sequence.IsNextFrameReady(currentTick)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Console.WriteLine("Frame hit!");
|
||||
|
||||
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
|
||||
if (Volatile.Read(ref _pendingSpans) == 0)
|
||||
{
|
||||
int toIssue = Math.Max(1, Volatile.Read(ref _size));
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
|
||||
{
|
||||
// Open the gate for all current waiters (up to `_size`).
|
||||
_event.Set();
|
||||
_refreshHandler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
} else
|
||||
{
|
||||
// A tick is already pending (or was just set by another thread): mark as miss.
|
||||
_missHandler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
~TimeSpanProvider() => Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSpanProvider.Awaiting"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload Space
|
||||
// Copyright (c) Nitload Space. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Mathematics.Memory;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
public partial class TimeSpanProvider
|
||||
{
|
||||
|
||||
private static readonly ConcurrentBufferStack<int> s_unusedID = new ConcurrentBufferStack<int>(8);
|
||||
|
||||
private static int s_maxID;
|
||||
|
||||
private EventHandler? _refreshHandler, _missHandler;
|
||||
private int _size, _id;
|
||||
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
|
||||
|
||||
private readonly ManualResetEventSlim _event = new();
|
||||
private readonly object _participantObject = new();
|
||||
|
||||
public event EventHandler MissTimeSpanRefreshed
|
||||
{
|
||||
add => _missHandler += value;
|
||||
remove => _missHandler -= value;
|
||||
}
|
||||
|
||||
public event EventHandler TimeSpanRefreshed
|
||||
{
|
||||
add => _refreshHandler += value;
|
||||
remove => _refreshHandler -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if there is at least one unconsumed span tick (PendingSpanCount > 0).
|
||||
/// </summary>
|
||||
public bool HasPendingSpan => PendingSpanCount > 0;
|
||||
|
||||
public int TokenID
|
||||
{
|
||||
get => _id;
|
||||
private set => _id = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of span ticks not yet consumed by Wait ( > 0 means last tick not waited).
|
||||
/// </summary>
|
||||
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
|
||||
|
||||
internal ManualResetEventSlim AwaitingEvent
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _event;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// mark provider as ended so external users can observe terminal state
|
||||
Volatile.Write(ref _endedFlag, 1);
|
||||
s_unusedID.Push(TokenID);
|
||||
_event.Set();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _id;
|
||||
|
||||
public void Participant()
|
||||
{
|
||||
lock (_participantObject)
|
||||
{
|
||||
// Increase the maximum concurrent consumers for the next tick.
|
||||
_size++;
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitNextSpan()
|
||||
{
|
||||
_event.Wait();
|
||||
|
||||
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0)
|
||||
{
|
||||
// Last consumer resets the gate for the next tick.
|
||||
_event.Reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WaitNextSpanAsync()
|
||||
{
|
||||
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
_event.Reset();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws
|
||||
/// OperationCanceledException when token is cancelled. If the provider is disposed during
|
||||
/// wait an OperationCanceledException is thrown to indicate end.
|
||||
/// </summary>
|
||||
public Task WaitNextSpanAsync(CancellationToken ct)
|
||||
{
|
||||
// Block the current thread until the event is signaled or cancellation requested.
|
||||
_event.Wait(ct);
|
||||
|
||||
// consume one pending span (same logic as non-cancellable variant)
|
||||
while (true)
|
||||
{
|
||||
long cur = Volatile.Read(ref _pendingSpans);
|
||||
if (cur == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||
{
|
||||
if (cur - 1 == 0) {
|
||||
_event.Reset();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="currentTick">
|
||||
/// Tick passed since binded time source.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the span provider is working. False if time is over (the sequence ended).
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal bool OnSpanTick(long currentTick)
|
||||
{
|
||||
if (_sequence.EndsAt(currentTick))
|
||||
{
|
||||
// mark ended due to sequence timeout, then dispose
|
||||
Volatile.Write(ref _endedFlag, 1);
|
||||
|
||||
// Console.WriteLine("Time span ended.");
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
if (!_sequence.IsNextFrameReady(currentTick)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Console.WriteLine("Frame hit!");
|
||||
|
||||
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
|
||||
if (Volatile.Read(ref _pendingSpans) == 0)
|
||||
{
|
||||
int toIssue = Math.Max(1, Volatile.Read(ref _size));
|
||||
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
|
||||
{
|
||||
// Open the gate for all current waiters (up to `_size`).
|
||||
_event.Set();
|
||||
_refreshHandler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
} else
|
||||
{
|
||||
// A tick is already pending (or was just set by another thread): mark as miss.
|
||||
_missHandler?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
~TimeSpanProvider() => Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSpanProvider"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Common.Timing;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
|
||||
/// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
|
||||
/// </summary>
|
||||
public partial class TimeSpanProvider
|
||||
{
|
||||
|
||||
// private bool _subscribed;
|
||||
private int _emittedSpanCount; // legacy counter
|
||||
|
||||
// internal ended flag (0 = running, 1 = ended)
|
||||
private int _endedFlag;
|
||||
private readonly TimeSequence _sequence;
|
||||
private readonly TimeSourceProviderBase _timeSource;
|
||||
|
||||
public TimeSpanProvider(
|
||||
TimeSequence sequence,
|
||||
TimeSourceProviderBase provider,
|
||||
int maxWaitCount = 1)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(provider);
|
||||
ArgumentNullException.ThrowIfNull(sequence);
|
||||
_timeSource = provider;
|
||||
_sequence = sequence;
|
||||
_sequence.StartFrom(provider);
|
||||
provider.AddSubscriber(this);
|
||||
|
||||
// _subscribed = true;
|
||||
if (!s_unusedID.TryPop(out int id))
|
||||
{
|
||||
id = Interlocked.Increment(ref s_maxID);
|
||||
}
|
||||
TokenID = id;
|
||||
_size = maxWaitCount > 0 ? maxWaitCount : 1;
|
||||
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
|
||||
_pendingSpans = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True when the provider has reached its end and will not emit further spans.
|
||||
/// </summary>
|
||||
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Current emitted time in seconds relative to the sequence beginning.
|
||||
/// </summary>
|
||||
public double CurrentSecond
|
||||
{
|
||||
get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
|
||||
}
|
||||
|
||||
public int CurrentSpan => _emittedSpanCount;
|
||||
|
||||
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
|
||||
|
||||
public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
|
||||
0;
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="TimeSpanProvider"
|
||||
// Project: StgSharp
|
||||
// AuthorGroup: Nitload
|
||||
// Copyright (c) Nitload. All rights reserved.
|
||||
//
|
||||
// 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 StgSharp.Common.Timing;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace StgSharp.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// TimeSpanProvider: uses a bound TimeSequence and TimeSourceProviderBase to publish span
|
||||
/// ticks. Each OnSpanTick advances the sequence; when sequence ends provider unsubscribes.
|
||||
/// </summary>
|
||||
public partial class TimeSpanProvider
|
||||
{
|
||||
|
||||
// private bool _subscribed;
|
||||
private int _emittedSpanCount; // legacy counter
|
||||
|
||||
// internal ended flag (0 = running, 1 = ended)
|
||||
private int _endedFlag;
|
||||
private readonly TimeSequence _sequence;
|
||||
private readonly TimeSourceProviderBase _timeSource;
|
||||
|
||||
public TimeSpanProvider(
|
||||
TimeSequence sequence,
|
||||
TimeSourceProviderBase provider,
|
||||
int maxWaitCount = 1)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(provider);
|
||||
ArgumentNullException.ThrowIfNull(sequence);
|
||||
_timeSource = provider;
|
||||
_sequence = sequence;
|
||||
_sequence.StartFrom(provider);
|
||||
provider.AddSubscriber(this);
|
||||
|
||||
// _subscribed = true;
|
||||
if (!s_unusedID.TryPop(out int id))
|
||||
{
|
||||
id = Interlocked.Increment(ref s_maxID);
|
||||
}
|
||||
TokenID = id;
|
||||
_size = maxWaitCount > 0 ? maxWaitCount : 1;
|
||||
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
|
||||
_pendingSpans = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True when the provider has reached its end and will not emit further spans.
|
||||
/// </summary>
|
||||
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Current emitted time in seconds relative to the sequence beginning.
|
||||
/// </summary>
|
||||
public double CurrentSecond
|
||||
{
|
||||
get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
|
||||
}
|
||||
|
||||
public int CurrentSpan => _emittedSpanCount;
|
||||
|
||||
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
|
||||
|
||||
public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
|
||||
0;
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user