@@ -466,10 +466,7 @@ namespace Svelto.DataStructures | |||
readonly FasterList<T> _list; | |||
} | |||
public interface IFasterList | |||
{} | |||
public class FasterList<T> : IList<T>, IFasterList | |||
public class FasterList<T> : IList<T> | |||
{ | |||
const int MIN_SIZE = 4; | |||
@@ -562,7 +559,7 @@ namespace Svelto.DataStructures | |||
while (items.MoveNext()) | |||
_buffer[_count++] = items.Current; | |||
} | |||
public void AddRange(ICollection<T> items) | |||
{ | |||
AddRange(items.GetEnumerator(), items.Count); | |||
@@ -803,5 +800,18 @@ namespace Svelto.DataStructures | |||
T[] _buffer; | |||
int _count; | |||
public static class NoVirt | |||
{ | |||
public static int Count(FasterList<T> fasterList) | |||
{ | |||
return fasterList._count; | |||
} | |||
public static T[] ToArrayFast(FasterList<T> fasterList) | |||
{ | |||
return fasterList._buffer; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.18408 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
using System; | |||
public interface IGraphNode<T> | |||
{ | |||
void VisitNeighbours(System.Action<T> onVisiting); | |||
} | |||
@@ -0,0 +1,627 @@ | |||
// Uncomment this to enable the following debugging aids: | |||
// LeftLeaningRedBlackTree.HtmlFragment | |||
// LeftLeaningRedBlackTree.Node.HtmlFragment | |||
// LeftLeaningRedBlackTree.AssertInvariants | |||
// #define DEBUGGING | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
/// <summary> | |||
/// Implements a left-leaning red-black tree. | |||
/// </summary> | |||
/// <remarks> | |||
/// Based on the research paper "Left-leaning Red-Black Trees" | |||
/// by Robert Sedgewick. More information available at: | |||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf | |||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf | |||
/// </remarks> | |||
/// <typeparam name="TKey">Type of keys.</typeparam> | |||
public class LeftLeaningKeyedRedBlackTree<TKey> where TKey: IComparable<TKey> | |||
{ | |||
/// <summary> | |||
/// Stores the root node of the tree. | |||
/// </summary> | |||
private Node _rootNode; | |||
/// <summary> | |||
/// Represents a node of the tree. | |||
/// </summary> | |||
/// <remarks> | |||
/// Using fields instead of properties drops execution time by about 40%. | |||
/// </remarks> | |||
[DebuggerDisplay("Key={Key}")] | |||
private class Node | |||
{ | |||
/// <summary> | |||
/// Gets or sets the node's key. | |||
/// </summary> | |||
public TKey Key; | |||
/// <summary> | |||
/// Gets or sets the left node. | |||
/// </summary> | |||
public Node Left; | |||
/// <summary> | |||
/// Gets or sets the right node. | |||
/// </summary> | |||
public Node Right; | |||
/// <summary> | |||
/// Gets or sets the color of the node. | |||
/// </summary> | |||
public bool IsBlack; | |||
#if DEBUGGING | |||
/// <summary> | |||
/// Gets an HTML fragment representing the node and its children. | |||
/// </summary> | |||
public string HtmlFragment | |||
{ | |||
get | |||
{ | |||
return | |||
"<table border='1'>" + | |||
"<tr>" + | |||
"<td colspan='2' align='center' bgcolor='" + (IsBlack ? "gray" : "red") + "'>" + Key + ", " + Value + " [" + Siblings + "]</td>" + | |||
"</tr>" + | |||
"<tr>" + | |||
"<td valign='top'>" + (null != Left ? Left.HtmlFragment : "[null]") + "</td>" + | |||
"<td valign='top'>" + (null != Right ? Right.HtmlFragment : "[null]") + "</td>" + | |||
"</tr>" + | |||
"</table>"; | |||
} | |||
} | |||
#endif | |||
} | |||
/// <summary> | |||
/// Adds a key/value pair to the tree. | |||
/// </summary> | |||
/// <param name="key">Key to add.</param> | |||
public void Add(TKey key) | |||
{ | |||
_rootNode = Add(_rootNode, key); | |||
_rootNode.IsBlack = true; | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
} | |||
/// <summary> | |||
/// Removes a key/value pair from the tree. | |||
/// </summary> | |||
/// <param name="key">Key to remove.</param> | |||
/// <returns>True if key/value present and removed.</returns> | |||
public bool Remove(TKey key) | |||
{ | |||
int initialCount = Count; | |||
if (null != _rootNode) | |||
{ | |||
_rootNode = Remove(_rootNode, key); | |||
if (null != _rootNode) | |||
{ | |||
_rootNode.IsBlack = true; | |||
} | |||
} | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
return initialCount != Count; | |||
} | |||
/// <summary> | |||
/// Removes all nodes in the tree. | |||
/// </summary> | |||
public void Clear() | |||
{ | |||
_rootNode = null; | |||
Count = 0; | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
} | |||
/// <summary> | |||
/// Gets a sorted list of keys in the tree. | |||
/// </summary> | |||
/// <returns>Sorted list of keys.</returns> | |||
public IEnumerable<TKey> GetKeys() | |||
{ | |||
TKey lastKey = default(TKey); | |||
bool lastKeyValid = false; | |||
return Traverse( | |||
_rootNode, | |||
n => !lastKeyValid || !object.Equals(lastKey, n.Key), | |||
n => | |||
{ | |||
lastKey = n.Key; | |||
lastKeyValid = true; | |||
return lastKey; | |||
}); | |||
} | |||
/// <summary> | |||
/// Gets the count of key/value pairs in the tree. | |||
/// </summary> | |||
public int Count { get; private set; } | |||
/// <summary> | |||
/// Gets the minimum key in the tree. | |||
/// </summary> | |||
public TKey MinimumKey | |||
{ | |||
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } | |||
} | |||
/// <summary> | |||
/// Gets the maximum key in the tree. | |||
/// </summary> | |||
public TKey MaximumKey | |||
{ | |||
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } | |||
} | |||
/// <summary> | |||
/// Returns true if the specified node is red. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>True if specified node is red.</returns> | |||
private static bool IsRed(Node node) | |||
{ | |||
if (null == node) | |||
{ | |||
// "Virtual" leaf nodes are always black | |||
return false; | |||
} | |||
return !node.IsBlack; | |||
} | |||
/// <summary> | |||
/// Adds the specified key/value pair below the specified root node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <param name="key">Key to add.</param> | |||
/// <param name="value">Value to add.</param> | |||
/// <returns>New root node.</returns> | |||
private Node Add(Node node, TKey key) | |||
{ | |||
if (null == node) | |||
{ | |||
// Insert new node | |||
Count++; | |||
return new Node { Key = key }; | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Right)) | |||
{ | |||
// Split node with two red children | |||
FlipColor(node); | |||
} | |||
// Find right place for new node | |||
int comparisonResult = KeyComparison(key, node.Key); | |||
if (comparisonResult < 0) | |||
{ | |||
node.Left = Add(node.Left, key); | |||
} | |||
else if (0 < comparisonResult) | |||
{ | |||
node.Right = Add(node.Right, key); | |||
} | |||
if (IsRed(node.Right)) | |||
{ | |||
// Rotate to prevent red node on right | |||
node = RotateLeft(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Left.Left)) | |||
{ | |||
// Rotate to prevent consecutive red nodes | |||
node = RotateRight(node); | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Removes the specified key/value pair from below the specified node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <param name="key">Key to remove.</param> | |||
/// <returns>True if key/value present and removed.</returns> | |||
private Node Remove(Node node, TKey key) | |||
{ | |||
int comparisonResult = KeyComparison(key, node.Key); | |||
if (comparisonResult < 0) | |||
{ | |||
// * Continue search if left is present | |||
if (null != node.Left) | |||
{ | |||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) | |||
{ | |||
// Move a red node over | |||
node = MoveRedLeft(node); | |||
} | |||
// Remove from left | |||
node.Left = Remove(node.Left, key); | |||
} | |||
} | |||
else | |||
{ | |||
if (IsRed(node.Left)) | |||
{ | |||
// Flip a 3 node or unbalance a 4 node | |||
node = RotateRight(node); | |||
} | |||
if ((0 == KeyComparison(key, node.Key)) && (null == node.Right)) | |||
{ | |||
// Remove leaf node | |||
Debug.Assert(null == node.Left, "About to remove an extra node."); | |||
Count--; | |||
// Leaf node is gone | |||
return null; | |||
} | |||
// * Continue search if right is present | |||
if (null != node.Right) | |||
{ | |||
if (!IsRed(node.Right) && !IsRed(node.Right.Left)) | |||
{ | |||
// Move a red node over | |||
node = MoveRedRight(node); | |||
} | |||
if (0 == KeyComparison(key, node.Key)) | |||
{ | |||
// Remove leaf node | |||
Count--; | |||
// Find the smallest node on the right, swap, and remove it | |||
Node m = GetExtreme(node.Right, n => n.Left, n => n); | |||
node.Key = m.Key; | |||
node.Right = DeleteMinimum(node.Right); | |||
} | |||
else | |||
{ | |||
// Remove from right | |||
node.Right = Remove(node.Right, key); | |||
} | |||
} | |||
} | |||
// Maintain invariants | |||
return FixUp(node); | |||
} | |||
/// <summary> | |||
/// Flip the colors of the specified node and its direct children. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
private static void FlipColor(Node node) | |||
{ | |||
node.IsBlack = !node.IsBlack; | |||
node.Left.IsBlack = !node.Left.IsBlack; | |||
node.Right.IsBlack = !node.Right.IsBlack; | |||
} | |||
/// <summary> | |||
/// Rotate the specified node "left". | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node RotateLeft(Node node) | |||
{ | |||
Node x = node.Right; | |||
node.Right = x.Left; | |||
x.Left = node; | |||
x.IsBlack = node.IsBlack; | |||
node.IsBlack = false; | |||
return x; | |||
} | |||
/// <summary> | |||
/// Rotate the specified node "right". | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node RotateRight(Node node) | |||
{ | |||
Node x = node.Left; | |||
node.Left = x.Right; | |||
x.Right = node; | |||
x.IsBlack = node.IsBlack; | |||
node.IsBlack = false; | |||
return x; | |||
} | |||
/// <summary> | |||
/// Moves a red node from the right child to the left child. | |||
/// </summary> | |||
/// <param name="node">Parent node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node MoveRedLeft(Node node) | |||
{ | |||
FlipColor(node); | |||
if (IsRed(node.Right.Left)) | |||
{ | |||
node.Right = RotateRight(node.Right); | |||
node = RotateLeft(node); | |||
FlipColor(node); | |||
// * Avoid creating right-leaning nodes | |||
if (IsRed(node.Right.Right)) | |||
{ | |||
node.Right = RotateLeft(node.Right); | |||
} | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Moves a red node from the left child to the right child. | |||
/// </summary> | |||
/// <param name="node">Parent node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node MoveRedRight(Node node) | |||
{ | |||
FlipColor(node); | |||
if (IsRed(node.Left.Left)) | |||
{ | |||
node = RotateRight(node); | |||
FlipColor(node); | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Deletes the minimum node under the specified node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private Node DeleteMinimum(Node node) | |||
{ | |||
if (null == node.Left) | |||
{ | |||
// Nothing to do | |||
return null; | |||
} | |||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) | |||
{ | |||
// Move red node left | |||
node = MoveRedLeft(node); | |||
} | |||
// Recursively delete | |||
node.Left = DeleteMinimum(node.Left); | |||
// Maintain invariants | |||
return FixUp(node); | |||
} | |||
/// <summary> | |||
/// Maintains invariants by adjusting the specified nodes children. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node FixUp(Node node) | |||
{ | |||
if (IsRed(node.Right)) | |||
{ | |||
// Avoid right-leaning node | |||
node = RotateLeft(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Left.Left)) | |||
{ | |||
// Balance 4-node | |||
node = RotateRight(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Right)) | |||
{ | |||
// Push red up | |||
FlipColor(node); | |||
} | |||
// * Avoid leaving behind right-leaning nodes | |||
if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) | |||
{ | |||
node.Left = RotateLeft(node.Left); | |||
if (IsRed(node.Left)) | |||
{ | |||
// Balance 4-node | |||
node = RotateRight(node); | |||
} | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Gets the (first) node corresponding to the specified key. | |||
/// </summary> | |||
/// <param name="key">Key to search for.</param> | |||
/// <returns>Corresponding node or null if none found.</returns> | |||
private Node GetNodeForKey(TKey key) | |||
{ | |||
// Initialize | |||
Node node = _rootNode; | |||
while (null != node) | |||
{ | |||
// Compare keys and go left/right | |||
int comparisonResult = key.CompareTo(node.Key); | |||
if (comparisonResult < 0) | |||
{ | |||
node = node.Left; | |||
} | |||
else if (0 < comparisonResult) | |||
{ | |||
node = node.Right; | |||
} | |||
else | |||
{ | |||
// Match; return node | |||
return node; | |||
} | |||
} | |||
// No match found | |||
return null; | |||
} | |||
/// <summary> | |||
/// Gets an extreme (ex: minimum/maximum) value. | |||
/// </summary> | |||
/// <typeparam name="T">Type of value.</typeparam> | |||
/// <param name="node">Node to start from.</param> | |||
/// <param name="successor">Successor function.</param> | |||
/// <param name="selector">Selector function.</param> | |||
/// <returns>Extreme value.</returns> | |||
private static T GetExtreme<T>(Node node, Func<Node, Node> successor, Func<Node, T> selector) | |||
{ | |||
// Initialize | |||
T extreme = default(T); | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
// Go to extreme | |||
extreme = selector(current); | |||
current = successor(current); | |||
} | |||
return extreme; | |||
} | |||
/// <summary> | |||
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes. | |||
/// </summary> | |||
/// <typeparam name="T">Type of elements.</typeparam> | |||
/// <param name="node">Starting node.</param> | |||
/// <param name="condition">Condition method.</param> | |||
/// <param name="selector">Selector method.</param> | |||
/// <returns>Sequence of selected nodes.</returns> | |||
private IEnumerable<T> Traverse<T>(Node node, Func<Node, bool> condition, Func<Node, T> selector) | |||
{ | |||
// Create a stack to avoid recursion | |||
Stack<Node> stack = new Stack<Node>(); | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
if (null != current.Left) | |||
{ | |||
// Save current state and go left | |||
stack.Push(current); | |||
current = current.Left; | |||
} | |||
else | |||
{ | |||
do | |||
{ | |||
// Select current node if relevant | |||
if (condition(current)) | |||
{ | |||
yield return selector(current); | |||
} | |||
// Go right - or up if nothing to the right | |||
current = current.Right; | |||
} | |||
while ((null == current) && | |||
(0 < stack.Count) && | |||
(null != (current = stack.Pop()))); | |||
} | |||
} | |||
} | |||
/// <summary> | |||
/// Compares the specified keys (primary) and values (secondary). | |||
/// </summary> | |||
/// <param name="leftKey">The left key.</param> | |||
/// <param name="rightKey">The right key.</param> | |||
/// <returns>CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right.</returns> | |||
private int KeyComparison(TKey leftKey, TKey rightKey) | |||
{ | |||
return leftKey.CompareTo(rightKey); | |||
} | |||
#if DEBUGGING | |||
/// <summary> | |||
/// Asserts that tree invariants are not violated. | |||
/// </summary> | |||
private void AssertInvariants() | |||
{ | |||
// Root is black | |||
Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); | |||
// Every path contains the same number of black nodes | |||
Dictionary<Node, Node> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.Node, LeftLeaningRedBlackTree<TKey, TValue>.Node>(); | |||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) | |||
{ | |||
if (null != node.Left) | |||
{ | |||
parents[node.Left] = node; | |||
} | |||
if (null != node.Right) | |||
{ | |||
parents[node.Right] = node; | |||
} | |||
} | |||
if (null != _rootNode) | |||
{ | |||
parents[_rootNode] = null; | |||
} | |||
int treeCount = -1; | |||
foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) | |||
{ | |||
int pathCount = 0; | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
if (current.IsBlack) | |||
{ | |||
pathCount++; | |||
} | |||
current = parents[current]; | |||
} | |||
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); | |||
treeCount = pathCount; | |||
} | |||
// Verify node properties... | |||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) | |||
{ | |||
// Left node is less | |||
if (null != node.Left) | |||
{ | |||
Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); | |||
} | |||
// Right node is greater | |||
if (null != node.Right) | |||
{ | |||
Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); | |||
} | |||
// Both children of a red node are black | |||
Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); | |||
// Always left-leaning | |||
Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); | |||
// No consecutive reds (subset of previous rule) | |||
//Debug.Assert(!(IsRed(node) && IsRed(node.Left))); | |||
} | |||
} | |||
/// <summary> | |||
/// Gets an HTML fragment representing the tree. | |||
/// </summary> | |||
public string HtmlDocument | |||
{ | |||
get | |||
{ | |||
return | |||
"<html>" + | |||
"<body>" + | |||
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") + | |||
"</body>" + | |||
"</html>"; | |||
} | |||
} | |||
#endif | |||
} |
@@ -0,0 +1,789 @@ | |||
// Uncomment this to enable the following debugging aids: | |||
// LeftLeaningRedBlackTree.HtmlFragment | |||
// LeftLeaningRedBlackTree.Node.HtmlFragment | |||
// LeftLeaningRedBlackTree.AssertInvariants | |||
// #define DEBUGGING | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
/// <summary> | |||
/// Implements a left-leaning red-black tree. | |||
/// </summary> | |||
/// <remarks> | |||
/// Based on the research paper "Left-leaning Red-Black Trees" | |||
/// by Robert Sedgewick. More information available at: | |||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf | |||
/// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf | |||
/// </remarks> | |||
/// <typeparam name="TKey">Type of keys.</typeparam> | |||
/// <typeparam name="TValue">Type of values.</typeparam> | |||
public class LeftLeaningRedBlackTree<TKey, TValue> | |||
{ | |||
/// <summary> | |||
/// Stores the key comparison function. | |||
/// </summary> | |||
private Comparison<TKey> _keyComparison; | |||
/// <summary> | |||
/// Stores the value comparison function. | |||
/// </summary> | |||
private Comparison<TValue> _valueComparison; | |||
/// <summary> | |||
/// Stores the root node of the tree. | |||
/// </summary> | |||
private Node _rootNode; | |||
/// <summary> | |||
/// Represents a node of the tree. | |||
/// </summary> | |||
/// <remarks> | |||
/// Using fields instead of properties drops execution time by about 40%. | |||
/// </remarks> | |||
[DebuggerDisplay("Key={Key}, Value={Value}, Siblings={Siblings}")] | |||
private class Node | |||
{ | |||
/// <summary> | |||
/// Gets or sets the node's key. | |||
/// </summary> | |||
public TKey Key; | |||
/// <summary> | |||
/// Gets or sets the node's value. | |||
/// </summary> | |||
public TValue Value; | |||
/// <summary> | |||
/// Gets or sets the left node. | |||
/// </summary> | |||
public Node Left; | |||
/// <summary> | |||
/// Gets or sets the right node. | |||
/// </summary> | |||
public Node Right; | |||
/// <summary> | |||
/// Gets or sets the color of the node. | |||
/// </summary> | |||
public bool IsBlack; | |||
/// <summary> | |||
/// Gets or sets the number of "siblings" (nodes with the same key/value). | |||
/// </summary> | |||
public int Siblings; | |||
#if DEBUGGING | |||
/// <summary> | |||
/// Gets an HTML fragment representing the node and its children. | |||
/// </summary> | |||
public string HtmlFragment | |||
{ | |||
get | |||
{ | |||
return | |||
"<table border='1'>" + | |||
"<tr>" + | |||
"<td colspan='2' align='center' bgcolor='" + (IsBlack ? "gray" : "red") + "'>" + Key + ", " + Value + " [" + Siblings + "]</td>" + | |||
"</tr>" + | |||
"<tr>" + | |||
"<td valign='top'>" + (null != Left ? Left.HtmlFragment : "[null]") + "</td>" + | |||
"<td valign='top'>" + (null != Right ? Right.HtmlFragment : "[null]") + "</td>" + | |||
"</tr>" + | |||
"</table>"; | |||
} | |||
} | |||
#endif | |||
} | |||
/// <summary> | |||
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing a normal dictionary. | |||
/// </summary> | |||
/// <param name="keyComparison">The key comparison function.</param> | |||
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison) | |||
{ | |||
if (null == keyComparison) | |||
{ | |||
throw new ArgumentNullException("keyComparison"); | |||
} | |||
_keyComparison = keyComparison; | |||
} | |||
/// <summary> | |||
/// Initializes a new instance of the LeftLeaningRedBlackTree class implementing an ordered multi-dictionary. | |||
/// </summary> | |||
/// <param name="keyComparison">The key comparison function.</param> | |||
/// <param name="valueComparison">The value comparison function.</param> | |||
public LeftLeaningRedBlackTree(Comparison<TKey> keyComparison, Comparison<TValue> valueComparison) | |||
: this(keyComparison) | |||
{ | |||
if (null == valueComparison) | |||
{ | |||
throw new ArgumentNullException("valueComparison"); | |||
} | |||
_valueComparison = valueComparison; | |||
} | |||
/// <summary> | |||
/// Gets a value indicating whether the tree is acting as an ordered multi-dictionary. | |||
/// </summary> | |||
private bool IsMultiDictionary | |||
{ | |||
get { return null != _valueComparison; } | |||
} | |||
/// <summary> | |||
/// Adds a key/value pair to the tree. | |||
/// </summary> | |||
/// <param name="key">Key to add.</param> | |||
/// <param name="value">Value to add.</param> | |||
public void Add(TKey key, TValue value) | |||
{ | |||
_rootNode = Add(_rootNode, key, value); | |||
_rootNode.IsBlack = true; | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
} | |||
/// <summary> | |||
/// Removes a key (and its associated value) from a normal (non-multi) dictionary. | |||
/// </summary> | |||
/// <param name="key">Key to remove.</param> | |||
/// <returns>True if key present and removed.</returns> | |||
public bool Remove(TKey key) | |||
{ | |||
if (IsMultiDictionary) | |||
{ | |||
throw new InvalidOperationException("Remove is only supported when acting as a normal (non-multi) dictionary."); | |||
} | |||
return Remove(key, default(TValue)); | |||
} | |||
/// <summary> | |||
/// Removes a key/value pair from the tree. | |||
/// </summary> | |||
/// <param name="key">Key to remove.</param> | |||
/// <param name="value">Value to remove.</param> | |||
/// <returns>True if key/value present and removed.</returns> | |||
public bool Remove(TKey key, TValue value) | |||
{ | |||
int initialCount = Count; | |||
if (null != _rootNode) | |||
{ | |||
_rootNode = Remove(_rootNode, key, value); | |||
if (null != _rootNode) | |||
{ | |||
_rootNode.IsBlack = true; | |||
} | |||
} | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
return initialCount != Count; | |||
} | |||
/// <summary> | |||
/// Removes all nodes in the tree. | |||
/// </summary> | |||
public void Clear() | |||
{ | |||
_rootNode = null; | |||
Count = 0; | |||
#if DEBUGGING | |||
AssertInvariants(); | |||
#endif | |||
} | |||
/// <summary> | |||
/// Gets a sorted list of keys in the tree. | |||
/// </summary> | |||
/// <returns>Sorted list of keys.</returns> | |||
public IEnumerable<TKey> GetKeys() | |||
{ | |||
TKey lastKey = default(TKey); | |||
bool lastKeyValid = false; | |||
return Traverse( | |||
_rootNode, | |||
n => !lastKeyValid || !object.Equals(lastKey, n.Key), | |||
n => | |||
{ | |||
lastKey = n.Key; | |||
lastKeyValid = true; | |||
return lastKey; | |||
}); | |||
} | |||
/// <summary> | |||
/// Gets the value associated with the specified key in a normal (non-multi) dictionary. | |||
/// </summary> | |||
/// <param name="key">Specified key.</param> | |||
/// <returns>Value associated with the specified key.</returns> | |||
public TValue GetValueForKey(TKey key) | |||
{ | |||
if (IsMultiDictionary) | |||
{ | |||
throw new InvalidOperationException("GetValueForKey is only supported when acting as a normal (non-multi) dictionary."); | |||
} | |||
Node node = GetNodeForKey(key); | |||
if (null != node) | |||
{ | |||
return node.Value; | |||
} | |||
else | |||
{ | |||
throw new KeyNotFoundException(); | |||
} | |||
} | |||
/// <summary> | |||
/// Gets a sequence of the values associated with the specified key. | |||
/// </summary> | |||
/// <param name="key">Specified key.</param> | |||
/// <returns>Sequence of values.</returns> | |||
public IEnumerable<TValue> GetValuesForKey(TKey key) | |||
{ | |||
return Traverse(GetNodeForKey(key), n => 0 == _keyComparison(n.Key, key), n => n.Value); | |||
} | |||
/// <summary> | |||
/// Gets a sequence of all the values in the tree. | |||
/// </summary> | |||
/// <returns>Sequence of all values.</returns> | |||
public IEnumerable<TValue> GetValuesForAllKeys() | |||
{ | |||
return Traverse(_rootNode, n => true, n => n.Value); | |||
} | |||
/// <summary> | |||
/// Gets the count of key/value pairs in the tree. | |||
/// </summary> | |||
public int Count { get; private set; } | |||
/// <summary> | |||
/// Gets the minimum key in the tree. | |||
/// </summary> | |||
public TKey MinimumKey | |||
{ | |||
get { return GetExtreme(_rootNode, n => n.Left, n => n.Key); } | |||
} | |||
/// <summary> | |||
/// Gets the maximum key in the tree. | |||
/// </summary> | |||
public TKey MaximumKey | |||
{ | |||
get { return GetExtreme(_rootNode, n => n.Right, n => n.Key); } | |||
} | |||
/// <summary> | |||
/// Returns true if the specified node is red. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>True if specified node is red.</returns> | |||
private static bool IsRed(Node node) | |||
{ | |||
if (null == node) | |||
{ | |||
// "Virtual" leaf nodes are always black | |||
return false; | |||
} | |||
return !node.IsBlack; | |||
} | |||
/// <summary> | |||
/// Adds the specified key/value pair below the specified root node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <param name="key">Key to add.</param> | |||
/// <param name="value">Value to add.</param> | |||
/// <returns>New root node.</returns> | |||
private Node Add(Node node, TKey key, TValue value) | |||
{ | |||
if (null == node) | |||
{ | |||
// Insert new node | |||
Count++; | |||
return new Node { Key = key, Value = value }; | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Right)) | |||
{ | |||
// Split node with two red children | |||
FlipColor(node); | |||
} | |||
// Find right place for new node | |||
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); | |||
if (comparisonResult < 0) | |||
{ | |||
node.Left = Add(node.Left, key, value); | |||
} | |||
else if (0 < comparisonResult) | |||
{ | |||
node.Right = Add(node.Right, key, value); | |||
} | |||
else | |||
{ | |||
if (IsMultiDictionary) | |||
{ | |||
// Store the presence of a "duplicate" node | |||
node.Siblings++; | |||
Count++; | |||
} | |||
else | |||
{ | |||
// Replace the value of the existing node | |||
node.Value = value; | |||
} | |||
} | |||
if (IsRed(node.Right)) | |||
{ | |||
// Rotate to prevent red node on right | |||
node = RotateLeft(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Left.Left)) | |||
{ | |||
// Rotate to prevent consecutive red nodes | |||
node = RotateRight(node); | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Removes the specified key/value pair from below the specified node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <param name="key">Key to remove.</param> | |||
/// <param name="value">Value to remove.</param> | |||
/// <returns>True if key/value present and removed.</returns> | |||
private Node Remove(Node node, TKey key, TValue value) | |||
{ | |||
int comparisonResult = KeyAndValueComparison(key, value, node.Key, node.Value); | |||
if (comparisonResult < 0) | |||
{ | |||
// * Continue search if left is present | |||
if (null != node.Left) | |||
{ | |||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) | |||
{ | |||
// Move a red node over | |||
node = MoveRedLeft(node); | |||
} | |||
// Remove from left | |||
node.Left = Remove(node.Left, key, value); | |||
} | |||
} | |||
else | |||
{ | |||
if (IsRed(node.Left)) | |||
{ | |||
// Flip a 3 node or unbalance a 4 node | |||
node = RotateRight(node); | |||
} | |||
if ((0 == KeyAndValueComparison(key, value, node.Key, node.Value)) && (null == node.Right)) | |||
{ | |||
// Remove leaf node | |||
Debug.Assert(null == node.Left, "About to remove an extra node."); | |||
Count--; | |||
if (0 < node.Siblings) | |||
{ | |||
// Record the removal of the "duplicate" node | |||
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); | |||
node.Siblings--; | |||
return node; | |||
} | |||
else | |||
{ | |||
// Leaf node is gone | |||
return null; | |||
} | |||
} | |||
// * Continue search if right is present | |||
if (null != node.Right) | |||
{ | |||
if (!IsRed(node.Right) && !IsRed(node.Right.Left)) | |||
{ | |||
// Move a red node over | |||
node = MoveRedRight(node); | |||
} | |||
if (0 == KeyAndValueComparison(key, value, node.Key, node.Value)) | |||
{ | |||
// Remove leaf node | |||
Count--; | |||
if (0 < node.Siblings) | |||
{ | |||
// Record the removal of the "duplicate" node | |||
Debug.Assert(IsMultiDictionary, "Should not have siblings if tree is not a multi-dictionary."); | |||
node.Siblings--; | |||
} | |||
else | |||
{ | |||
// Find the smallest node on the right, swap, and remove it | |||
Node m = GetExtreme(node.Right, n => n.Left, n => n); | |||
node.Key = m.Key; | |||
node.Value = m.Value; | |||
node.Siblings = m.Siblings; | |||
node.Right = DeleteMinimum(node.Right); | |||
} | |||
} | |||
else | |||
{ | |||
// Remove from right | |||
node.Right = Remove(node.Right, key, value); | |||
} | |||
} | |||
} | |||
// Maintain invariants | |||
return FixUp(node); | |||
} | |||
/// <summary> | |||
/// Flip the colors of the specified node and its direct children. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
private static void FlipColor(Node node) | |||
{ | |||
node.IsBlack = !node.IsBlack; | |||
node.Left.IsBlack = !node.Left.IsBlack; | |||
node.Right.IsBlack = !node.Right.IsBlack; | |||
} | |||
/// <summary> | |||
/// Rotate the specified node "left". | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node RotateLeft(Node node) | |||
{ | |||
Node x = node.Right; | |||
node.Right = x.Left; | |||
x.Left = node; | |||
x.IsBlack = node.IsBlack; | |||
node.IsBlack = false; | |||
return x; | |||
} | |||
/// <summary> | |||
/// Rotate the specified node "right". | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node RotateRight(Node node) | |||
{ | |||
Node x = node.Left; | |||
node.Left = x.Right; | |||
x.Right = node; | |||
x.IsBlack = node.IsBlack; | |||
node.IsBlack = false; | |||
return x; | |||
} | |||
/// <summary> | |||
/// Moves a red node from the right child to the left child. | |||
/// </summary> | |||
/// <param name="node">Parent node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node MoveRedLeft(Node node) | |||
{ | |||
FlipColor(node); | |||
if (IsRed(node.Right.Left)) | |||
{ | |||
node.Right = RotateRight(node.Right); | |||
node = RotateLeft(node); | |||
FlipColor(node); | |||
// * Avoid creating right-leaning nodes | |||
if (IsRed(node.Right.Right)) | |||
{ | |||
node.Right = RotateLeft(node.Right); | |||
} | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Moves a red node from the left child to the right child. | |||
/// </summary> | |||
/// <param name="node">Parent node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node MoveRedRight(Node node) | |||
{ | |||
FlipColor(node); | |||
if (IsRed(node.Left.Left)) | |||
{ | |||
node = RotateRight(node); | |||
FlipColor(node); | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Deletes the minimum node under the specified node. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private Node DeleteMinimum(Node node) | |||
{ | |||
if (null == node.Left) | |||
{ | |||
// Nothing to do | |||
return null; | |||
} | |||
if (!IsRed(node.Left) && !IsRed(node.Left.Left)) | |||
{ | |||
// Move red node left | |||
node = MoveRedLeft(node); | |||
} | |||
// Recursively delete | |||
node.Left = DeleteMinimum(node.Left); | |||
// Maintain invariants | |||
return FixUp(node); | |||
} | |||
/// <summary> | |||
/// Maintains invariants by adjusting the specified nodes children. | |||
/// </summary> | |||
/// <param name="node">Specified node.</param> | |||
/// <returns>New root node.</returns> | |||
private static Node FixUp(Node node) | |||
{ | |||
if (IsRed(node.Right)) | |||
{ | |||
// Avoid right-leaning node | |||
node = RotateLeft(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Left.Left)) | |||
{ | |||
// Balance 4-node | |||
node = RotateRight(node); | |||
} | |||
if (IsRed(node.Left) && IsRed(node.Right)) | |||
{ | |||
// Push red up | |||
FlipColor(node); | |||
} | |||
// * Avoid leaving behind right-leaning nodes | |||
if ((null != node.Left) && IsRed(node.Left.Right) && !IsRed(node.Left.Left)) | |||
{ | |||
node.Left = RotateLeft(node.Left); | |||
if (IsRed(node.Left)) | |||
{ | |||
// Balance 4-node | |||
node = RotateRight(node); | |||
} | |||
} | |||
return node; | |||
} | |||
/// <summary> | |||
/// Gets the (first) node corresponding to the specified key. | |||
/// </summary> | |||
/// <param name="key">Key to search for.</param> | |||
/// <returns>Corresponding node or null if none found.</returns> | |||
private Node GetNodeForKey(TKey key) | |||
{ | |||
// Initialize | |||
Node node = _rootNode; | |||
while (null != node) | |||
{ | |||
// Compare keys and go left/right | |||
int comparisonResult = _keyComparison(key, node.Key); | |||
if (comparisonResult < 0) | |||
{ | |||
node = node.Left; | |||
} | |||
else if (0 < comparisonResult) | |||
{ | |||
node = node.Right; | |||
} | |||
else | |||
{ | |||
// Match; return node | |||
return node; | |||
} | |||
} | |||
// No match found | |||
return null; | |||
} | |||
/// <summary> | |||
/// Gets an extreme (ex: minimum/maximum) value. | |||
/// </summary> | |||
/// <typeparam name="T">Type of value.</typeparam> | |||
/// <param name="node">Node to start from.</param> | |||
/// <param name="successor">Successor function.</param> | |||
/// <param name="selector">Selector function.</param> | |||
/// <returns>Extreme value.</returns> | |||
private static T GetExtreme<T>(Node node, Func<Node, Node> successor, Func<Node, T> selector) | |||
{ | |||
// Initialize | |||
T extreme = default(T); | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
// Go to extreme | |||
extreme = selector(current); | |||
current = successor(current); | |||
} | |||
return extreme; | |||
} | |||
/// <summary> | |||
/// Traverses a subset of the sequence of nodes in order and selects the specified nodes. | |||
/// </summary> | |||
/// <typeparam name="T">Type of elements.</typeparam> | |||
/// <param name="node">Starting node.</param> | |||
/// <param name="condition">Condition method.</param> | |||
/// <param name="selector">Selector method.</param> | |||
/// <returns>Sequence of selected nodes.</returns> | |||
private IEnumerable<T> Traverse<T>(Node node, Func<Node, bool> condition, Func<Node, T> selector) | |||
{ | |||
// Create a stack to avoid recursion | |||
Stack<Node> stack = new Stack<Node>(); | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
if (null != current.Left) | |||
{ | |||
// Save current state and go left | |||
stack.Push(current); | |||
current = current.Left; | |||
} | |||
else | |||
{ | |||
do | |||
{ | |||
for (int i = 0; i <= current.Siblings; i++) | |||
{ | |||
// Select current node if relevant | |||
if (condition(current)) | |||
{ | |||
yield return selector(current); | |||
} | |||
} | |||
// Go right - or up if nothing to the right | |||
current = current.Right; | |||
} | |||
while ((null == current) && | |||
(0 < stack.Count) && | |||
(null != (current = stack.Pop()))); | |||
} | |||
} | |||
} | |||
/// <summary> | |||
/// Compares the specified keys (primary) and values (secondary). | |||
/// </summary> | |||
/// <param name="leftKey">The left key.</param> | |||
/// <param name="leftValue">The left value.</param> | |||
/// <param name="rightKey">The right key.</param> | |||
/// <param name="rightValue">The right value.</param> | |||
/// <returns>CompareTo-style results: -1 if left is less, 0 if equal, and 1 if greater than right.</returns> | |||
private int KeyAndValueComparison(TKey leftKey, TValue leftValue, TKey rightKey, TValue rightValue) | |||
{ | |||
// Compare keys | |||
int comparisonResult = _keyComparison(leftKey, rightKey); | |||
if ((0 == comparisonResult) && (null != _valueComparison)) | |||
{ | |||
// Keys match; compare values | |||
comparisonResult = _valueComparison(leftValue, rightValue); | |||
} | |||
return comparisonResult; | |||
} | |||
#if DEBUGGING | |||
/// <summary> | |||
/// Asserts that tree invariants are not violated. | |||
/// </summary> | |||
private void AssertInvariants() | |||
{ | |||
// Root is black | |||
Debug.Assert((null == _rootNode) || _rootNode.IsBlack, "Root is not black"); | |||
// Every path contains the same number of black nodes | |||
Dictionary<Node, Node> parents = new Dictionary<LeftLeaningRedBlackTree<TKey, TValue>.Node, LeftLeaningRedBlackTree<TKey, TValue>.Node>(); | |||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) | |||
{ | |||
if (null != node.Left) | |||
{ | |||
parents[node.Left] = node; | |||
} | |||
if (null != node.Right) | |||
{ | |||
parents[node.Right] = node; | |||
} | |||
} | |||
if (null != _rootNode) | |||
{ | |||
parents[_rootNode] = null; | |||
} | |||
int treeCount = -1; | |||
foreach (Node node in Traverse(_rootNode, n => (null == n.Left) || (null == n.Right), n => n)) | |||
{ | |||
int pathCount = 0; | |||
Node current = node; | |||
while (null != current) | |||
{ | |||
if (current.IsBlack) | |||
{ | |||
pathCount++; | |||
} | |||
current = parents[current]; | |||
} | |||
Debug.Assert((-1 == treeCount) || (pathCount == treeCount), "Not all paths have the same number of black nodes."); | |||
treeCount = pathCount; | |||
} | |||
// Verify node properties... | |||
foreach (Node node in Traverse(_rootNode, n => true, n => n)) | |||
{ | |||
// Left node is less | |||
if (null != node.Left) | |||
{ | |||
Debug.Assert(0 > KeyAndValueComparison(node.Left.Key, node.Left.Value, node.Key, node.Value), "Left node is greater than its parent."); | |||
} | |||
// Right node is greater | |||
if (null != node.Right) | |||
{ | |||
Debug.Assert(0 < KeyAndValueComparison(node.Right.Key, node.Right.Value, node.Key, node.Value), "Right node is less than its parent."); | |||
} | |||
// Both children of a red node are black | |||
Debug.Assert(!IsRed(node) || (!IsRed(node.Left) && !IsRed(node.Right)), "Red node has a red child."); | |||
// Always left-leaning | |||
Debug.Assert(!IsRed(node.Right) || IsRed(node.Left), "Node is not left-leaning."); | |||
// No consecutive reds (subset of previous rule) | |||
//Debug.Assert(!(IsRed(node) && IsRed(node.Left))); | |||
} | |||
} | |||
/// <summary> | |||
/// Gets an HTML fragment representing the tree. | |||
/// </summary> | |||
public string HtmlDocument | |||
{ | |||
get | |||
{ | |||
return | |||
"<html>" + | |||
"<body>" + | |||
(null != _rootNode ? _rootNode.HtmlFragment : "[null]") + | |||
"</body>" + | |||
"</html>"; | |||
} | |||
} | |||
#endif | |||
} |
@@ -0,0 +1,108 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections; | |||
// By James McCaffrey11/02/2012 | |||
sealed public class PriorityQueue<T>:IEnumerable<T> where T : IComparable<T> | |||
{ | |||
private List<T> data; | |||
public PriorityQueue () | |||
{ | |||
this.data = new List<T> (); | |||
} | |||
IEnumerator System.Collections.IEnumerable.GetEnumerator () | |||
{ | |||
// Lets call the generic version here | |||
return this.GetEnumerator (); | |||
} | |||
public IEnumerator<T> GetEnumerator () | |||
{ | |||
return data.GetEnumerator () as IEnumerator<T>; | |||
} | |||
public void Enqueue (T item) | |||
{ | |||
data.Add (item); | |||
int ci = data.Count - 1; // child index; start at end | |||
while (ci > 0) { | |||
int pi = (ci - 1) / 2; // parent index | |||
if (data [ci].CompareTo (data [pi]) >= 0) | |||
break; // child item is larger than (or equal) parent so we're done | |||
T tmp = data [ci]; | |||
data [ci] = data [pi]; | |||
data [pi] = tmp; | |||
ci = pi; | |||
} | |||
} | |||
public T Dequeue() | |||
{ | |||
// assumes pq is not empty; up to calling code | |||
int li = data.Count - 1; // last index (before removal) | |||
T frontItem = data [0]; // fetch the front | |||
data [0] = data [li]; | |||
data.RemoveAt (li); | |||
--li; // last index (after removal) | |||
int pi = 0; // parent index. start at front of pq | |||
while (true) | |||
{ | |||
int ci = pi * 2 + 1; // left child index of parent | |||
if (ci > li) | |||
break; // no children so done | |||
int rc = ci + 1; // right child | |||
if (rc <= li && data [rc].CompareTo (data [ci]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead | |||
ci = rc; | |||
if (data [pi].CompareTo (data [ci]) <= 0) | |||
break; // parent is smaller than (or equal to) smallest child so done | |||
T tmp = data [pi]; | |||
data [pi] = data [ci]; | |||
data [ci] = tmp; // swap parent and child | |||
pi = ci; | |||
} | |||
return frontItem; | |||
} | |||
public T Peek () | |||
{ | |||
T frontItem = data [0]; | |||
return frontItem; | |||
} | |||
public int Count () | |||
{ | |||
return data.Count; | |||
} | |||
public override string ToString () | |||
{ | |||
string s = ""; | |||
for (int i = 0; i < data.Count; ++i) | |||
s += data [i].ToString () + " "; | |||
s += "count = " + data.Count; | |||
return s; | |||
} | |||
public bool IsConsistent () | |||
{ | |||
// is the heap property true for all data? | |||
if (data.Count == 0) | |||
return true; | |||
int li = data.Count - 1; // last index | |||
for (int pi = 0; pi < data.Count; ++pi) { // each parent index | |||
int lci = 2 * pi + 1; // left child index | |||
int rci = 2 * pi + 2; // right child index | |||
if (lci <= li && data [pi].CompareTo (data [lci]) > 0) | |||
return false; // if lc exists and it's greater than parent then bad. | |||
if (rci <= li && data [pi].CompareTo (data [rci]) > 0) | |||
return false; // check the right child too. | |||
} | |||
return true; // passed all checks | |||
} // IsConsistent | |||
} // PriorityQueue |
@@ -0,0 +1,23 @@ | |||
using Svelto.DataStructures; | |||
using System.Collections.Generic; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// This is just a place holder at the moment | |||
/// I always wanted to create my own Dictionary | |||
/// data structure as excercise, but never had the | |||
/// time to. At the moment I need the custom interface | |||
/// wrapped though. | |||
/// </summary> | |||
public interface ITypeSafeDictionary | |||
{ | |||
} | |||
public class TypeSafeDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ITypeSafeDictionary | |||
{ | |||
internal static readonly ReadOnlyDictionary<TKey, TValue> Default = new ReadOnlyDictionary<TKey, TValue>(new TypeSafeDictionary<TKey, TValue>()); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface ITypeSafeList | |||
{ | |||
void Clear(); | |||
void AddRange(ITypeSafeList nodeListValue); | |||
ITypeSafeList Create(); | |||
ITypeSafeDictionary CreateIndexedDictionary(); | |||
void AddToIndexedDictionary(ITypeSafeDictionary nodesDic); | |||
} | |||
public class TypeSafeFasterList<T> : FasterList<T>, ITypeSafeList | |||
{ | |||
public TypeSafeFasterList() | |||
{ | |||
} | |||
public void AddRange(ITypeSafeList nodeListValue) | |||
{ | |||
AddRange(nodeListValue as FasterList<T>); | |||
} | |||
public ITypeSafeList Create() | |||
{ | |||
return new TypeSafeFasterList<T>(); | |||
} | |||
public ITypeSafeDictionary CreateIndexedDictionary() | |||
{ | |||
return new TypeSafeDictionary<int, T>(); | |||
} | |||
public void AddToIndexedDictionary(ITypeSafeDictionary nodesDic) | |||
{ | |||
var dic = nodesDic as TypeSafeDictionary<int, T>; | |||
var buffer = NoVirt.ToArrayFast(this); | |||
for (int i = 0; i < Count; i++) | |||
{ | |||
T node = buffer[i]; | |||
dic[(node as NodeWithID).ID] = node; | |||
} | |||
} | |||
} | |||
} |
@@ -6,104 +6,102 @@ namespace Svelto.ECS | |||
{ | |||
public class EngineNodeDB : IEngineNodeDB | |||
{ | |||
internal EngineNodeDB( Dictionary<Type, FasterList<INode>> nodesDB, | |||
Dictionary<Type, Dictionary<int, INode>> nodesDBdic, | |||
Dictionary<Type, FasterList<INode>> metaNodesDB) | |||
internal EngineNodeDB( Dictionary<Type, ITypeSafeList> nodesDB, | |||
Dictionary<Type, ITypeSafeDictionary> nodesDBdic, | |||
Dictionary<Type, ITypeSafeList> metaNodesDB) | |||
{ | |||
_nodesDB = nodesDB; | |||
_nodesDBdic = nodesDBdic; | |||
_metaNodesDB = metaNodesDB; | |||
} | |||
public FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode | |||
public FasterReadOnlyList<T> QueryNodes<T>() | |||
{ | |||
var type = typeof(T); | |||
FasterList<INode> nodes; | |||
ITypeSafeList nodes; | |||
if (_nodesDB.TryGetValue(type, out nodes) == false) | |||
return RetrieveEmptyNodeList<T>(); | |||
return new FasterReadOnlyListCast<INode, T>(nodes); | |||
return new FasterReadOnlyList<T>((FasterList<T>)nodes); | |||
} | |||
public ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode | |||
public ReadOnlyDictionary<int, T> QueryIndexableNodes<T>() | |||
{ | |||
var type = typeof(T); | |||
Dictionary<int, INode> nodes; | |||
ITypeSafeDictionary nodes; | |||
if (_nodesDBdic.TryGetValue(type, out nodes) == false) | |||
return _defaultEmptyNodeDict; | |||
return TypeSafeDictionary<int, T>.Default; | |||
return new ReadOnlyDictionary<int, INode>(nodes); | |||
return new ReadOnlyDictionary<int, T>(nodes as Dictionary<int, T>); | |||
} | |||
public T QueryMetaNode<T>(int metaEntityID) where T : INode | |||
public T QueryMetaNode<T>(int metaEntityID) | |||
{ | |||
return QueryNode<T>(metaEntityID); | |||
} | |||
public bool TryQueryMetaNode<T>(int metaEntityID, out T node) where T : INode | |||
public bool TryQueryMetaNode<T>(int metaEntityID, out T node) | |||
{ | |||
return TryQueryNode(metaEntityID, out node); | |||
} | |||
public FasterReadOnlyListCast<INode, T> QueryMetaNodes<T>() where T : INode | |||
public FasterReadOnlyList<T> QueryMetaNodes<T>() | |||
{ | |||
var type = typeof(T); | |||
FasterList<INode> nodes; | |||
ITypeSafeList nodes; | |||
if (_metaNodesDB.TryGetValue(type, out nodes) == false) | |||
return RetrieveEmptyNodeList<T>(); | |||
return new FasterReadOnlyListCast<INode, T>(nodes); | |||
return new FasterReadOnlyList<T>((FasterList<T>)nodes); | |||
} | |||
public bool TryQueryNode<T>(int ID, out T node) where T:INode | |||
public bool TryQueryNode<T>(int ID, out T node) | |||
{ | |||
var type = typeof(T); | |||
INode internalNode; | |||
T internalNode; | |||
Dictionary<int, INode> nodes; | |||
ITypeSafeDictionary nodes; | |||
if (_nodesDBdic.TryGetValue(type, out nodes) && | |||
nodes.TryGetValue(ID, out internalNode)) | |||
(nodes as Dictionary<int, T>).TryGetValue(ID, out internalNode)) | |||
{ | |||
node = (T)internalNode; | |||
node = internalNode; | |||
return true; | |||
} | |||
node = default(T); | |||
return false; | |||
} | |||
public T QueryNode<T>(int ID) where T:INode | |||
public T QueryNode<T>(int ID) | |||
{ | |||
var type = typeof(T); | |||
INode internalNode; Dictionary<int, INode> nodes; | |||
T internalNode; ITypeSafeDictionary nodes; | |||
if (_nodesDBdic.TryGetValue(type, out nodes) && | |||
nodes.TryGetValue(ID, out internalNode)) | |||
(nodes as Dictionary<int, T>).TryGetValue(ID, out internalNode)) | |||
return (T)internalNode; | |||
throw new Exception("Node Not Found"); | |||
} | |||
static FasterReadOnlyListCast<INode, T> RetrieveEmptyNodeList<T>() where T : INode | |||
static FasterReadOnlyList<T> RetrieveEmptyNodeList<T>() | |||
{ | |||
return FasterReadOnlyListCast<INode, T>.DefaultList; | |||
return FasterReadOnlyList<T>.DefaultList; | |||
} | |||
readonly Dictionary<Type, FasterList<INode>> _nodesDB; | |||
readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic; | |||
readonly Dictionary<Type, FasterList<INode>> _metaNodesDB; | |||
readonly ReadOnlyDictionary<int, INode> _defaultEmptyNodeDict = new ReadOnlyDictionary<int, INode>(new Dictionary<int, INode>()); | |||
readonly Dictionary<Type, ITypeSafeList> _nodesDB; | |||
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic; | |||
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB; | |||
} | |||
} |
@@ -3,14 +3,24 @@ using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.NodeSchedulers; | |||
using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>; | |||
using System.Reflection; | |||
using Svelto.Utilities; | |||
using UnityEngine.UI; | |||
using UnityEngine.XR.WSA.Persistence; | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
using Svelto.ECS.Profiler; | |||
#endif | |||
#if NETFX_CORE | |||
using System.Reflection; | |||
#endif | |||
namespace Svelto.ECS.Internal | |||
{ | |||
struct BuildNodeCallbackStruct | |||
{ | |||
public Action<ITypeSafeList> _internalRemove; | |||
public Action<ITypeSafeList> _internalEnable; | |||
public Action<ITypeSafeList> _internalDisable; | |||
} | |||
} | |||
namespace Svelto.ECS | |||
{ | |||
@@ -22,22 +32,22 @@ namespace Svelto.ECS | |||
_activableEngines = new Dictionary<Type, FasterList<IEngine>>(); | |||
_otherEngines = new FasterList<IEngine>(); | |||
_engineRootWeakReference = new WeakReference(this); | |||
//_engineRootWeakReference = new DataStructures.WeakReference<EnginesRoot>(this); | |||
_nodesDB = new Dictionary<Type, FasterList<INode>>(); | |||
_nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>(); | |||
_nodesDB = new Dictionary<Type, ITypeSafeList>(); | |||
_metaNodesDB = new Dictionary<Type, ITypeSafeList>(); | |||
_nodesDBdic = new Dictionary<Type, ITypeSafeDictionary>(); | |||
_nodesToAdd = new FasterList<INode>(); | |||
_metaNodesToAdd = new FasterList<INode>(); | |||
_metaNodesDB = new Dictionary<Type, FasterList<INode>>(); | |||
_sharedStructNodeLists = new SharedStructNodeLists(); | |||
_sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists(); | |||
_nodesToAdd = new Dictionary<Type, ITypeSafeList>(); | |||
_metaNodesToAdd = new Dictionary<Type, ITypeSafeList>(); | |||
_groupedNodesToAdd = new Dictionary<Type, Dictionary<int, ITypeSafeList>>(); | |||
_callBackStruct = new BuildNodeCallbackStruct(); | |||
_internalRemove = InternalRemove; | |||
_internalDisable = InternalDisable; | |||
_internalEnable = InternalEnable; | |||
_internalMetaRemove = InternalMetaRemove; | |||
/* _callBackStruct._internalRemove = InternalRemove; | |||
_callBackStruct._internalDisable = InternalDisable; | |||
_callBackStruct._internalEnable = InternalEnable; | |||
_callBackStruct._internalMetaRemove = InternalMetaRemove;*/ | |||
_scheduler = nodeScheduler; | |||
_scheduler.Schedule(SubmitNodes); | |||
@@ -54,77 +64,53 @@ namespace Svelto.ECS | |||
#endif | |||
} | |||
void SubmitNodes() | |||
public void BuildEntity(int ID, EntityDescriptor ed) | |||
{ | |||
int metaNodesCount = _metaNodesToAdd.Count; | |||
int nodesCount = _nodesToAdd.Count; | |||
if (metaNodesCount + nodesCount == 0) return; | |||
bool newNodesHaveBeenAddedWhileIterating; | |||
int startNodes = 0; | |||
int startMetaNodes = 0; | |||
int numberOfReenteringLoops = 0; | |||
do | |||
{ | |||
var nodesToAdd = _nodesToAdd.ToArrayFast(); | |||
for (int i = startNodes; i < nodesCount; i++) | |||
{ | |||
var node = nodesToAdd[i]; | |||
var nodeType = node.GetType(); | |||
AddNodeToTheDB(node, nodeType); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
AddNodeToNodesDictionary(nodeWithId, nodeType); | |||
} | |||
var metaNodesToAdd = _metaNodesToAdd.ToArrayFast(); | |||
for (int i = startMetaNodes; i < metaNodesCount; i++) | |||
{ | |||
var node = metaNodesToAdd[i]; | |||
var nodeType = node.GetType(); | |||
AddNodeToMetaDB(node, nodeType); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
AddNodeToNodesDictionary(nodeWithId, nodeType); | |||
} | |||
for (int i = startNodes; i < nodesCount; i++) | |||
{ | |||
var node = nodesToAdd[i]; | |||
AddNodeToTheSuitableEngines(node, node.GetType()); | |||
} | |||
for (int i = startMetaNodes; i < metaNodesCount; i++) | |||
{ | |||
var node = metaNodesToAdd[i]; | |||
AddNodeToTheSuitableEngines(node, node.GetType()); | |||
} | |||
newNodesHaveBeenAddedWhileIterating = | |||
_metaNodesToAdd.Count > metaNodesCount || | |||
_nodesToAdd.Count > nodesCount; | |||
startNodes = nodesCount; | |||
startMetaNodes = metaNodesCount; | |||
if (numberOfReenteringLoops > 5) | |||
throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); | |||
numberOfReenteringLoops++; | |||
metaNodesCount = _metaNodesToAdd.Count; | |||
nodesCount = _nodesToAdd.Count; | |||
ed.BuildNodes(ID, _nodesToAdd, ref _callBackStruct); | |||
} | |||
} while (newNodesHaveBeenAddedWhileIterating); | |||
/// <summary> | |||
/// A meta entity is a way to manage a set of entitites that are not easily | |||
/// queriable otherwise. For example you may want to group existing entities | |||
/// by size and type and then use the meta entity node to manage the data | |||
/// shared among the single entities of the same type and size. This will | |||
/// prevent the scenario where the coder is forced to parse all the entities to | |||
/// find the ones of the same size and type. | |||
/// Since the entities are managed through the shared node, the same | |||
/// shared node must be found on the single entities of the same type and size. | |||
/// The shared node of the meta entity is then used by engines that are meant | |||
/// to manage a group of entities through a single node. | |||
/// The same engine can manage several meta entities nodes too. | |||
/// The Engine manages the logic of the Meta Node data and other engines | |||
/// can read back this data through the normal entity as the shared node | |||
/// will be present in their descriptor too. | |||
/// It's a way to control a group of Entities through a node only. | |||
/// This set of entities can share exactly the same node reference if | |||
/// built through this function. In this way, if you need to set a variable | |||
/// on a group of entities, instead to inject N nodes and iterate over | |||
/// them to set the same value, you can inject just one node, set the value | |||
/// and be sure that the value is shared between entities. | |||
/// </summary> | |||
/// <param name="metaEntityID"></param> | |||
/// <param name="ed"></param> | |||
public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed) | |||
{ | |||
ed.BuildNodes(metaEntityID, _metaNodesToAdd, ref _callBackStruct); | |||
} | |||
_nodesToAdd.Clear(); | |||
_metaNodesToAdd.Clear(); | |||
/// <summary> | |||
/// Using this function is like building a normal entity, but the nodes | |||
/// are grouped by groupID to be better processed inside engines and | |||
/// improve cache locality. Only IGroupStructNodeWithID nodes are grouped | |||
/// other nodes are managed as usual. | |||
/// </summary> | |||
/// <param name="entityID"></param> | |||
/// <param name="groupID"></param> | |||
/// <param name="ed"></param> | |||
public void BuildEntityInGroup(int entityID, int groupID, | |||
EntityDescriptor ed) | |||
{ | |||
ed.BuildGroupedNodes(entityID, groupID, _groupedNodesToAdd, ref _callBackStruct); | |||
} | |||
public void AddEngine(IEngine engine) | |||
@@ -133,9 +119,9 @@ namespace Svelto.ECS | |||
EngineProfiler.AddEngine(engine); | |||
#endif | |||
var queryableNodeEngine = engine as IQueryableNodeEngine; | |||
if (queryableNodeEngine != null) | |||
queryableNodeEngine.nodesDB = | |||
new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB); | |||
if (queryableNodeEngine != null) | |||
queryableNodeEngine.nodesDB = | |||
new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB); | |||
var engineType = engine.GetType(); | |||
var implementedInterfaces = engineType.GetInterfaces(); | |||
@@ -167,12 +153,8 @@ namespace Svelto.ECS | |||
if (type.IsAssignableFrom(interfaceType) == false) | |||
continue; | |||
#if !NETFX_CORE | |||
if (false == interfaceType.IsGenericType) | |||
#else | |||
if (false == interfaceType.IsConstructedGenericType) | |||
#endif | |||
if (false == interfaceType.IsGenericTypeEx()) | |||
{ | |||
continue; | |||
} | |||
@@ -189,21 +171,9 @@ namespace Svelto.ECS | |||
bool engineAdded = false; | |||
if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType)) | |||
{ | |||
((IStructNodeEngine)engine).CreateStructNodes | |||
(_sharedStructNodeLists); | |||
} | |||
if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType)) | |||
{ | |||
((IGroupedStructNodesEngine)engine).CreateStructNodes | |||
(_sharedGroupedStructNodeLists); | |||
} | |||
Type[] arguments; | |||
if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType, | |||
out arguments)) | |||
out arguments)) | |||
{ | |||
AddEngine(engine, arguments, _activableEngines); | |||
@@ -230,15 +200,9 @@ namespace Svelto.ECS | |||
bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded) | |||
{ | |||
#if !NETFX_CORE | |||
var baseType = engineType.BaseType; | |||
var baseType = engineType.GetBaseType(); | |||
if (baseType.IsGenericType | |||
#else | |||
var baseType = engineType.GetTypeInfo().BaseType; | |||
if (baseType.IsConstructedGenericType | |||
#endif | |||
if (baseType.IsGenericTypeEx() | |||
&& engine is INodeEngine) | |||
{ | |||
AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); | |||
@@ -251,76 +215,8 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public void BuildEntity(int ID, EntityDescriptor ed) | |||
{ | |||
var entityNodes = ed.BuildNodes(ID, | |||
_internalRemove, | |||
_internalEnable, | |||
_internalDisable | |||
); | |||
_nodesToAdd.AddRange(entityNodes); | |||
} | |||
/// <summary> | |||
/// A meta entity is a way to manage a set of entitites that are not easily | |||
/// queriable otherwise. For example you may want to group existing entities | |||
/// by size and type and then use the meta entity node to manage the data | |||
/// shared among the single entities of the same type and size. This will | |||
/// prevent the scenario where the coder is forced to parse all the entities to | |||
/// find the ones of the same size and type. | |||
/// Since the entities are managed through the shared node, the same | |||
/// shared node must be found on the single entities of the same type and size. | |||
/// The shared node of the meta entity is then used by engines that are meant | |||
/// to manage a group of entities through a single node. | |||
/// The same engine can manage several meta entities nodes too. | |||
/// The Engine manages the logic of the Meta Node data and other engines | |||
/// can read back this data through the normal entity as the shared node | |||
/// will be present in their descriptor too. | |||
/// </summary> | |||
/// <param name="metaEntityID"></param> | |||
/// <param name="ed"></param> | |||
public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed) | |||
{ | |||
var entityNodes = ed.BuildNodes(metaEntityID, | |||
_internalMetaRemove, | |||
_internalEnable, | |||
_internalDisable | |||
); | |||
_metaNodesToAdd.AddRange(entityNodes); | |||
} | |||
/// <summary> | |||
/// Using this function is like building a normal entity, but the nodes | |||
/// are grouped by groupID to be better processed inside engines and | |||
/// improve cache locality. Only IGroupStructNodeWithID nodes are grouped | |||
/// other nodes are managed as usual. | |||
/// </summary> | |||
/// <param name="entityID"></param> | |||
/// <param name="groupID"></param> | |||
/// <param name="ed"></param> | |||
public void BuildEntityInGroup(int entityID, int groupID, | |||
EntityDescriptor ed) | |||
{ | |||
var entityNodes = ed.BuildNodes(entityID, | |||
_internalRemove, | |||
_internalEnable, | |||
_internalDisable | |||
); | |||
_nodesToAdd.AddRange(entityNodes); | |||
for (int i = 0; i < entityNodes.Count; i++) | |||
{ | |||
var groupNode = entityNodes[i] as IGroupedStructNodeWithID; | |||
if (groupNode != null) | |||
groupNode.groupID = groupID; | |||
} | |||
} | |||
static void AddEngine(IEngine engine, Type[] types, | |||
Dictionary<Type, FasterList<IEngine>> engines) | |||
Dictionary<Type, FasterList<IEngine>> engines) | |||
{ | |||
for (int i = 0; i < types.Length; i++) | |||
{ | |||
@@ -339,186 +235,229 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void AddNodeToMetaDB(INode node, Type nodeType) | |||
static void AddNodesToTheDBAndSuitableEngines(Dictionary<Type, ITypeSafeList> nodesToAdd, | |||
Dictionary<Type, FasterList<IEngine>> nodeEngines, | |||
Dictionary<Type, ITypeSafeDictionary> nodesDBdic, | |||
Dictionary<Type, ITypeSafeList> nodesDB) | |||
{ | |||
FasterList<INode> nodes; | |||
if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false) | |||
nodes = _metaNodesDB[nodeType] = new FasterList<INode>(); | |||
foreach (var nodeList in nodesToAdd) | |||
{ | |||
ITypeSafeList dbList; | |||
nodes.Add(node); | |||
} | |||
if (nodesDB.TryGetValue(nodeList.Key, out dbList) == false) | |||
dbList = nodesDB[nodeList.Key] = nodeList.Value.Create(); | |||
void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDB.TryGetValue(nodeType, out nodes) == false) | |||
nodes = _nodesDB[nodeType] = new FasterList<INode>(); | |||
dbList.AddRange(nodeList.Value); | |||
nodes.Add(node); | |||
AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key); | |||
AddNodesToTheSuitableEngines(nodeEngines, nodeList.Value, nodeList.Key); | |||
} | |||
} | |||
void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID | |||
static void AddNodeToNodesDictionary(Dictionary<Type, ITypeSafeDictionary> nodesDBdic, ITypeSafeList nodes, Type nodeType) | |||
{ | |||
Dictionary<int, INode> nodesDic; | |||
ITypeSafeDictionary nodesDic; | |||
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) | |||
nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>(); | |||
if (nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) | |||
nodesDic = nodesDBdic[nodeType] = nodes.CreateIndexedDictionary(); | |||
nodesDic.Add(node.ID, node); | |||
nodes.AddToIndexedDictionary(nodesDic); | |||
} | |||
void AddNodeToTheSuitableEngines(INode node, Type nodeType) | |||
static void AddNodesToTheSuitableEngines(Dictionary<Type, FasterList<IEngine>> nodeEngines, ITypeSafeList nodes, Type nodeType) | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
if (nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node); | |||
#else | |||
(enginesForNode[j] as INodeEngine).Add(node); | |||
(enginesForNode[j] as INodeEngine).Add(nodes); | |||
#endif | |||
} | |||
} | |||
} | |||
/* | |||
void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDB.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? | |||
} | |||
void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDB.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? | |||
} | |||
void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? | |||
} | |||
void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero? | |||
} | |||
void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID | |||
{ | |||
Dictionary<int, INode> nodesDic; | |||
void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID | |||
{ | |||
Dictionary<int, INode> nodesDic; | |||
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) | |||
nodesDic.Remove(node.ID); | |||
} | |||
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) | |||
nodesDic.Remove(node.ID); | |||
} | |||
void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); | |||
#else | |||
(enginesForNode[j] as INodeEngine).Remove(node); | |||
#endif | |||
} | |||
} | |||
} | |||
void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
void DisableNodeFromEngines(INode node, Type nodeType) | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
{ | |||
(enginesForNode[j] as IActivableNodeEngine).Disable(node); | |||
} | |||
} | |||
} | |||
if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
void EnableNodeFromEngines(INode node, Type nodeType) | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node); | |||
#else | |||
(enginesForNode[j] as INodeEngine).Remove(node); | |||
#endif | |||
FasterList<IEngine> enginesForNode; | |||
if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
{ | |||
(enginesForNode[j] as IActivableNodeEngine).Enable(node); | |||
} | |||
} | |||
} | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
void AddNodeToEngine(IEngine engine, INode node) | |||
{ | |||
(engine as INodeEngine).Add(node); | |||
} | |||
} | |||
} | |||
void DisableNodeFromEngines(INode node, Type nodeType) | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
void RemoveNodeFromEngine(IEngine engine, INode node) | |||
{ | |||
(engine as INodeEngine).Remove(node); | |||
} | |||
#endif | |||
/* | |||
void InternalDisable(FasterList<INode> nodes) | |||
{ | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
DisableNodeFromEngines(node, nodeType); | |||
} | |||
} | |||
if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
void InternalEnable(FasterList<INode> nodes) | |||
{ | |||
(enginesForNode[j] as IActivableNodeEngine).Disable(node); | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
EnableNodeFromEngines(node, nodeType); | |||
} | |||
} | |||
} | |||
} | |||
void EnableNodeFromEngines(INode node, Type nodeType) | |||
{ | |||
FasterList<IEngine> enginesForNode; | |||
void InternalRemove(IFasterList nodes) | |||
{ | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
if (_activableEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromTheDB(node, node.GetType()); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
RemoveNodeFromNodesDictionary(nodeWithId, nodeType); | |||
} | |||
} | |||
void InternalMetaRemove(FasterList<INode> nodes) | |||
{ | |||
(enginesForNode[j] as IActivableNodeEngine).Enable(node); | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromMetaDB(node, nodeType); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
RemoveNodeFromNodesDictionary(nodeWithId, nodeType); | |||
} | |||
} | |||
} | |||
} | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
void AddNodeToEngine(IEngine engine, INode node) | |||
{ | |||
(engine as INodeEngine).Add(node); | |||
} | |||
void RemoveNodeFromEngine(IEngine engine, INode node) | |||
{ | |||
(engine as INodeEngine).Remove(node); | |||
} | |||
#endif | |||
readonly DataStructures.WeakReference<EnginesRoot> _engineRootWeakReference; | |||
*/ | |||
void InternalDisable(FasterList<INode> nodes) | |||
void SubmitNodes() | |||
{ | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
int metaNodesCount = _metaNodesToAdd.Count; | |||
int nodesCount = _nodesToAdd.Count; | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
DisableNodeFromEngines(node, nodeType); | |||
} | |||
} | |||
if (metaNodesCount + nodesCount == 0) return; | |||
void InternalEnable(FasterList<INode> nodes) | |||
{ | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
bool newNodesHaveBeenAddedWhileIterating; | |||
int startNodes = 0; | |||
int startMetaNodes = 0; | |||
int numberOfReenteringLoops = 0; | |||
for (int i = 0; i < nodes.Count; i++) | |||
do | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
EnableNodeFromEngines(node, nodeType); | |||
} | |||
} | |||
AddNodesToTheDBAndSuitableEngines(_nodesToAdd, _nodeEngines, _nodesDBdic, _nodesDB); | |||
AddNodesToTheDBAndSuitableEngines(_metaNodesToAdd, _nodeEngines, _nodesDBdic, _metaNodesDB); | |||
void InternalRemove(FasterList<INode> nodes) | |||
{ | |||
if (_engineRootWeakReference.IsValid == false) | |||
return; | |||
newNodesHaveBeenAddedWhileIterating = | |||
_metaNodesToAdd.Count > metaNodesCount || | |||
_nodesToAdd.Count > nodesCount; | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
startNodes = nodesCount; | |||
startMetaNodes = metaNodesCount; | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromTheDB(node, node.GetType()); | |||
if (numberOfReenteringLoops > 5) | |||
throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
RemoveNodeFromNodesDictionary(nodeWithId, nodeType); | |||
} | |||
} | |||
numberOfReenteringLoops++; | |||
void InternalMetaRemove(FasterList<INode> nodes) | |||
{ | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
var node = nodes[i]; | |||
Type nodeType = node.GetType(); | |||
metaNodesCount = _metaNodesToAdd.Count; | |||
nodesCount = _nodesToAdd.Count; | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromMetaDB(node, nodeType); | |||
} while (newNodesHaveBeenAddedWhileIterating); | |||
var nodeWithId = node as INodeWithID; | |||
if (nodeWithId != null) | |||
RemoveNodeFromNodesDictionary(nodeWithId, nodeType); | |||
} | |||
_nodesToAdd.Clear(); | |||
_metaNodesToAdd.Clear(); | |||
} | |||
readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines; | |||
@@ -526,30 +465,27 @@ namespace Svelto.ECS | |||
readonly FasterList<IEngine> _otherEngines; | |||
readonly Dictionary<Type, FasterList<INode>> _nodesDB; | |||
readonly Dictionary<Type, FasterList<INode>> _metaNodesDB; | |||
readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic; | |||
readonly FasterList<INode> _nodesToAdd; | |||
readonly FasterList<INode> _metaNodesToAdd; | |||
readonly WeakReference _engineRootWeakReference; | |||
readonly SharedStructNodeLists _sharedStructNodeLists; | |||
readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists; | |||
readonly Dictionary<Type, ITypeSafeList> _nodesDB; | |||
readonly Dictionary<Type, ITypeSafeList> _metaNodesDB; | |||
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupNodesDB; | |||
readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic; | |||
/// <summary> | |||
/// Need to think about how to make BuildEntity thread safe as well | |||
/// </summary> | |||
readonly Dictionary<Type, ITypeSafeList> _nodesToAdd; | |||
readonly Dictionary<Type, ITypeSafeList> _metaNodesToAdd; | |||
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupedNodesToAdd; | |||
readonly NodeSubmissionScheduler _scheduler; | |||
readonly Action<FasterList<INode>> _internalRemove; | |||
readonly Action<FasterList<INode>> _internalEnable; | |||
readonly Action<FasterList<INode>> _internalDisable; | |||
readonly Action<FasterList<INode>> _internalMetaRemove; | |||
readonly Type _structNodeEngineType; | |||
readonly Type _groupedStructNodesEngineType; | |||
readonly Type _activableNodeEngineType; | |||
readonly Dictionary<Type, Type[]> _implementedInterfaceTypes; | |||
BuildNodeCallbackStruct _callBackStruct; | |||
} | |||
} | |||
} |
@@ -2,22 +2,26 @@ using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using Svelto.DataStructures; | |||
#if NETFX_CORE | |||
using BindingFlags = System.Reflection.BindingFlags; | |||
#endif | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public class EntityDescriptor | |||
{ | |||
protected EntityDescriptor() | |||
{ | |||
} | |||
{} | |||
/// <summary> | |||
/// if you want to avoid allocation in run-time, you can prebuild | |||
/// EntityDescriptors and use them to build entities at different | |||
/// times | |||
/// </summary> | |||
protected EntityDescriptor(INodeBuilder[] nodesToBuild) | |||
{ | |||
_nodesToBuild = new FasterList<INodeBuilder>(nodesToBuild); | |||
} | |||
protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor):this(nodesToBuild) | |||
protected EntityDescriptor(INodeBuilder[] nodesToBuild, | |||
params object[] componentsImplementor):this(nodesToBuild) | |||
{ | |||
ProcessImplementors(componentsImplementor); | |||
} | |||
@@ -26,105 +30,160 @@ namespace Svelto.ECS | |||
{ | |||
ProcessImplementors(componentsImplementor); | |||
} | |||
public void AddNodes(params INodeBuilder[] nodesWithID) | |||
{ | |||
_nodesToBuild.AddRange(nodesWithID); | |||
} | |||
void ProcessImplementors(object[] implementors) | |||
internal void BuildGroupedNodes | |||
(int entityID, int groupID, | |||
Dictionary<Type, Dictionary<int, ITypeSafeList>> groupNodes, | |||
ref BuildNodeCallbackStruct callBackstruct) | |||
{ | |||
for (int index = 0; index < implementors.Length; index++) | |||
for (int index = 0; index < _nodesToBuild.Count; index++) | |||
{ | |||
var implementor = implementors[index]; | |||
if (implementor == null) | |||
{ | |||
Utility.Console.LogWarning( | |||
"Null implementor, are you using a wild GetComponents<Monobehaviour> to fetch it? " | |||
.FastConcat(ToString())); | |||
} | |||
else | |||
var nodeBuilder = _nodesToBuild[index]; | |||
var nodeType = nodeBuilder.GetNodeType(); | |||
Dictionary<int, ITypeSafeList> groupedNodesTyped; | |||
if (groupNodes.TryGetValue(nodeType, out groupedNodesTyped) == false) | |||
{ | |||
if (implementor is IRemoveEntityComponent) | |||
_removingImplementors.Add(implementor as IRemoveEntityComponent); | |||
if (implementor is IDisableEntityComponent) | |||
_disablingImplementors.Add(implementor as IDisableEntityComponent); | |||
if (implementor is IEnableEntityComponent) | |||
_enablingImplementors.Add(implementor as IEnableEntityComponent); | |||
groupedNodesTyped = new Dictionary<int, ITypeSafeList>(); | |||
groupNodes.Add(nodeType, groupedNodesTyped); | |||
}; | |||
ITypeSafeList nodes; | |||
var interfaces = implementor.GetType().GetInterfaces(); | |||
for (int iindex = 0; iindex < interfaces.Length; iindex++) | |||
{ | |||
_implementorsByType[interfaces[iindex]] = implementor; | |||
} | |||
var mustAdd = groupedNodesTyped.TryGetValue(groupID, out nodes) == false; | |||
var node = nodeBuilder.BuildAndAddToList(ref nodes, entityID); | |||
if (mustAdd) | |||
groupedNodesTyped[groupID] = nodes; | |||
if (node != null && nodeBuilder.reflects != FillNodeMode.None) | |||
{ | |||
node = FillNode(node, nodeBuilder.reflects); | |||
SetupImplementors(ref callBackstruct, nodes); | |||
} | |||
} | |||
} | |||
public void AddNodes(params INodeBuilder[] nodesWithID) | |||
{ | |||
_nodesToBuild.AddRange(nodesWithID); | |||
/* var groupNode = node as IGroupedNode; | |||
if (groupNode != null) | |||
groupNode.groupID = groupID;*/ | |||
} | |||
} | |||
public virtual FasterList<INode> BuildNodes(int ID) | |||
internal void BuildNodes(int entityID, | |||
Dictionary<Type, ITypeSafeList> nodesToAdd, | |||
ref BuildNodeCallbackStruct callBackstruct) | |||
{ | |||
var nodes = new FasterList<INode>(); | |||
for (int index = 0; index < _nodesToBuild.Count; index++) | |||
{ | |||
var nodeBuilder = _nodesToBuild[index]; | |||
var node = nodeBuilder.Build(ID); | |||
var nodeType = nodeBuilder.GetNodeType(); | |||
if (nodeBuilder.reflects != FillNodeMode.None) | |||
node = FillNode(node, nodeBuilder.reflects); | |||
ITypeSafeList nodes; | |||
nodes.Add(node); | |||
} | |||
var mustAdd = nodesToAdd.TryGetValue(nodeType, out nodes) == false; | |||
var node = nodeBuilder.BuildAndAddToList(ref nodes, entityID); | |||
return nodes; | |||
} | |||
if (mustAdd) | |||
nodesToAdd[nodeType] = nodes; | |||
internal FasterList<INode> BuildNodes(int ID, | |||
Action<FasterList<INode>> removeEntity, | |||
Action<FasterList<INode>> enableEntity, | |||
Action<FasterList<INode>> disableEntity) | |||
if (node != null && nodeBuilder.reflects != FillNodeMode.None) | |||
{ | |||
FillNode(node, nodeBuilder.reflects); | |||
SetupImplementors(ref callBackstruct, nodes); | |||
} | |||
} | |||
} | |||
void ProcessImplementors(object[] implementors) | |||
{ | |||
var nodes = BuildNodes(ID); | |||
SetupImplementors(removeEntity, enableEntity, disableEntity, nodes); | |||
for (int index = 0; index < implementors.Length; index++) | |||
{ | |||
var implementor = implementors[index]; | |||
if (implementor != null) | |||
{ | |||
if (implementor is IRemoveEntityComponent) | |||
_removingImplementors.Add(new DataStructures.WeakReference<IRemoveEntityComponent>(implementor as IRemoveEntityComponent)); | |||
if (implementor is IDisableEntityComponent) | |||
_disablingImplementors.Add(new DataStructures.WeakReference<IDisableEntityComponent>(implementor as IDisableEntityComponent)); | |||
if (implementor is IEnableEntityComponent) | |||
_enablingImplementors.Add(new DataStructures.WeakReference<IEnableEntityComponent>(implementor as IEnableEntityComponent)); | |||
return nodes; | |||
var interfaces = implementor.GetType().GetInterfaces(); | |||
var weakReference = new DataStructures.WeakReference<object>(implementor); | |||
for (int iindex = 0; iindex < interfaces.Length; iindex++) | |||
{ | |||
var componentType = interfaces[iindex]; | |||
_implementorsByType[componentType] = weakReference; | |||
#if DEBUG && !PROFILER | |||
if (_implementorCounterByType.ContainsKey(componentType) == false) | |||
_implementorCounterByType[componentType] = 1; | |||
else | |||
_implementorCounterByType[componentType]++; | |||
#endif | |||
} | |||
} | |||
#if DEBUG && !PROFILER | |||
else | |||
Utility.Console.LogError(NULL_IMPLEMENTOR_ERROR.FastConcat(ToString())); | |||
#endif | |||
} | |||
} | |||
void SetupImplementors( | |||
Action<FasterList<INode>> removeEntity, | |||
Action<FasterList<INode>> enableEntity, | |||
Action<FasterList<INode>> disableEntity, | |||
FasterList<INode> nodes) | |||
ref BuildNodeCallbackStruct callBackstruct, | |||
ITypeSafeList nodes) | |||
{ | |||
Action removeEntityAction = () => { removeEntity(nodes); nodes.Clear(); }; | |||
Action disableEntityAction = () => disableEntity(nodes); | |||
Action enableEntityAction = () => enableEntity(nodes); | |||
for (int index = 0; index < _removingImplementors.Count; index++) | |||
_removingImplementors[index].removeEntity = removeEntityAction; | |||
for (int index = 0; index < _disablingImplementors.Count; index++) | |||
_disablingImplementors[index].disableEntity = disableEntityAction; | |||
for (int index = 0; index < _enablingImplementors.Count; index++) | |||
_enablingImplementors[index].enableEntity = enableEntityAction; | |||
var RemoveEntity = callBackstruct._internalRemove; | |||
var DisableEntity = callBackstruct._internalDisable; | |||
var EnableEntity = callBackstruct._internalEnable; | |||
Action removeEntityAction = () => { RemoveEntity(nodes); nodes.Clear(); }; | |||
Action disableEntityAction = () => DisableEntity(nodes); | |||
Action enableEntityAction = () => EnableEntity(nodes); | |||
int removingImplementorsCount = _removingImplementors.Count; | |||
for (int index = 0; index < removingImplementorsCount; index++) | |||
_removingImplementors[index].Target.removeEntity = removeEntityAction; | |||
int disablingImplementorsCount = _disablingImplementors.Count; | |||
for (int index = 0; index < disablingImplementorsCount; index++) | |||
_disablingImplementors[index].Target.disableEntity = disableEntityAction; | |||
int enablingImplementorsCount = _enablingImplementors.Count; | |||
for (int index = 0; index < enablingImplementorsCount; index++) | |||
_enablingImplementors[index].Target.enableEntity = enableEntityAction; | |||
} | |||
TNode FillNode<TNode>(TNode node, FillNodeMode mode) where TNode : INode | |||
{ | |||
var fields = node.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); | |||
var fields = node.GetType().GetFields(BindingFlags.Public | | |||
BindingFlags.Instance); | |||
for (int i = fields.Length - 1; i >= 0; --i) | |||
{ | |||
var field = fields[i]; | |||
Type fieldType = field.FieldType; | |||
object component; | |||
if ((_implementorsByType.TryGetValue(fieldType, out component)) == false) | |||
DataStructures.WeakReference<object> component; | |||
if (_implementorsByType.TryGetValue(fieldType, out component) == false) | |||
{ | |||
if (mode == FillNodeMode.Strict) | |||
{ | |||
Exception e = | |||
new Exception("Svelto.ECS: Implementor not found for a Node. " + "Implementor Type: " + | |||
new Exception(NOT_FOUND_EXCEPTION + | |||
field.FieldType.Name + " - Node: " + node.GetType().Name + | |||
" - EntityDescriptor " + this); | |||
@@ -132,65 +191,36 @@ namespace Svelto.ECS | |||
} | |||
} | |||
else | |||
field.SetValue(node, component); | |||
} | |||
return node; | |||
} | |||
readonly FasterList<IDisableEntityComponent> _disablingImplementors = new FasterList<IDisableEntityComponent>(); | |||
readonly FasterList<IRemoveEntityComponent> _removingImplementors = new FasterList<IRemoveEntityComponent>(); | |||
readonly FasterList<IEnableEntityComponent> _enablingImplementors = new FasterList<IEnableEntityComponent>(); | |||
readonly Dictionary<Type, object> _implementorsByType = new Dictionary<Type, object>(); | |||
readonly FasterList<INodeBuilder> _nodesToBuild; | |||
} | |||
public interface INodeBuilder | |||
{ | |||
INode Build(int ID); | |||
FillNodeMode reflects { get; } | |||
} | |||
public class NodeBuilder<NodeType> : INodeBuilder where NodeType : NodeWithID, new() | |||
{ | |||
public INode Build(int ID) | |||
{ | |||
NodeWithID node = NodeWithID.BuildNode<NodeType>(ID); | |||
return (NodeType)node; | |||
} | |||
public FillNodeMode reflects { get { return FillNodeMode.Strict; } } | |||
} | |||
field.SetValue(node, component.Target); | |||
#if DEBUG && !PROFILER | |||
{ | |||
if (_implementorCounterByType[fieldType] != 1) | |||
{ | |||
Utility.Console.LogError( | |||
DUPLICATE_IMPLEMENTOR_ERROR.FastConcat("component: ", fieldType.ToString(), | |||
" implementor: ", component.Target.ToString())); | |||
} | |||
} | |||
#endif | |||
public class StructNodeBuilder<NodeType> : INodeBuilder | |||
where NodeType : struct, IStructNodeWithID | |||
{ | |||
public INode Build(int ID) | |||
{ | |||
var shortID = (short)ID; | |||
IStructNodeWithID node = default(NodeType); | |||
node.ID = shortID; | |||
} | |||
return node; | |||
} | |||
public virtual FillNodeMode reflects { get { return FillNodeMode.Relaxed; } } | |||
} | |||
readonly FasterList<DataStructures.WeakReference<IDisableEntityComponent>> _disablingImplementors = new FasterList<DataStructures.WeakReference<IDisableEntityComponent>>(); | |||
readonly FasterList<DataStructures.WeakReference<IRemoveEntityComponent>> _removingImplementors = new FasterList<DataStructures.WeakReference<IRemoveEntityComponent>>(); | |||
readonly FasterList<DataStructures.WeakReference<IEnableEntityComponent>> _enablingImplementors = new FasterList<DataStructures.WeakReference<IEnableEntityComponent>>(); | |||
public class FastStructNodeBuilder<NodeType> : StructNodeBuilder<NodeType> | |||
where NodeType : struct, IStructNodeWithID | |||
{ | |||
public override FillNodeMode reflects { get { return FillNodeMode.None; } } | |||
} | |||
public enum FillNodeMode | |||
{ | |||
Strict, | |||
Relaxed, | |||
readonly Dictionary<Type, DataStructures.WeakReference<object>> _implementorsByType = new Dictionary<Type, DataStructures.WeakReference<object>>(); | |||
#if DEBUG && !PROFILER | |||
readonly Dictionary<Type, int> _implementorCounterByType = new Dictionary<Type, int>(); | |||
#endif | |||
readonly FasterList<INodeBuilder> _nodesToBuild; | |||
None | |||
const string DUPLICATE_IMPLEMENTOR_ERROR = "the same component is implemented with more than one implementor. This is considered an error and MUST be fixed. "; | |||
const string NULL_IMPLEMENTOR_ERROR = "Null implementor, are you using a wild GetComponents<Monobehaviour> to fetch it? "; | |||
const string NOT_FOUND_EXCEPTION = "Svelto.ECS: Implementor not found for a Node. Implementor Type: "; | |||
} | |||
} |
@@ -5,6 +5,14 @@ using UnityEngine; | |||
namespace Svelto.ECS.NodeSchedulers | |||
{ | |||
//The NodeSubmissionScheduler has been introduced to make | |||
//the node submission logic platform indipendent. | |||
//Please don't be tempted to create your own submission to | |||
//adapt to your game level code design. For example, | |||
//you may be tempted to write a submission logic to submit | |||
//the nodes immediatly just because convenient for your game | |||
//logic. This is not how it works. | |||
public class UnitySumbmissionNodeScheduler : NodeSubmissionScheduler | |||
{ | |||
public UnitySumbmissionNodeScheduler() | |||
@@ -140,6 +140,25 @@ namespace Svelto.ECS | |||
static readonly INodeBuilder[] _nodesToBuild; | |||
} | |||
public class GenericMixedEntityDescriptor<T, U> : EntityDescriptor | |||
where T : INodeBuilder, new() | |||
where U : INodeBuilder, new() | |||
{ | |||
static GenericMixedEntityDescriptor() | |||
{ | |||
_nodesToBuild = new INodeBuilder[] | |||
{ | |||
new T(), | |||
new U(), | |||
}; | |||
} | |||
public GenericMixedEntityDescriptor(params object[] componentsImplementor) : | |||
base(_nodesToBuild, componentsImplementor) | |||
{ } | |||
static readonly INodeBuilder[] _nodesToBuild; | |||
} | |||
public class GenericMixedEntityDescriptor<T, U, V> : EntityDescriptor | |||
where T : INodeBuilder, new() | |||
@@ -3,7 +3,8 @@ using System; | |||
namespace Svelto.ECS | |||
{ | |||
public class GenericEntityDescriptorHolder<T, I>: UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor | |||
public class GenericEntityDescriptorHolder<T, I>: | |||
UnityEngine.MonoBehaviour, IEntityDescriptorHolder where T:EntityDescriptor | |||
{ | |||
public EntityDescriptor BuildDescriptorType(object[] externalImplentors) | |||
{ | |||
@@ -1,3 +1,4 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Internal | |||
@@ -14,14 +15,14 @@ namespace Svelto.ECS.Internal | |||
public interface IActivableNodeEngine : IEngine | |||
{ | |||
void Enable(INode obj); | |||
void Disable(INode obj); | |||
void Enable(ITypeSafeList nodes); | |||
void Disable(ITypeSafeList nodes); | |||
} | |||
public interface INodeEngine : IEngine | |||
{ | |||
void Add(INode obj); | |||
void Remove(INode obj); | |||
void Add(ITypeSafeList nodes); | |||
void Remove(ITypeSafeList nodes); | |||
} | |||
public interface INodesEngine : INodeEngine | |||
@@ -56,7 +57,7 @@ namespace Svelto.ECS | |||
/// usually the ID is the owner of the nodes of that | |||
/// group | |||
/// </summary> | |||
public interface IGroupedStructNodesEngine<T> : IGroupedStructNodesEngine where T:struct, IGroupedStructNodeWithID | |||
public interface IGroupedStructNodesEngine<T> : IGroupedStructNodesEngine where T:struct, IGroupedNode | |||
{ } | |||
} | |||
@@ -4,16 +4,15 @@ namespace Svelto.ECS | |||
{ | |||
public interface IEngineNodeDB | |||
{ | |||
ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode; | |||
ReadOnlyDictionary<int, T> QueryIndexableNodes<T>(); | |||
bool TryQueryNode<T>(int ID, out T node) where T:INode; | |||
T QueryNode<T>(int ID) where T:INode; | |||
FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode; | |||
bool TryQueryNode<T>(int ID, out T node); | |||
T QueryNode<T>(int ID); | |||
FasterReadOnlyList<T> QueryNodes<T>(); | |||
bool TryQueryMetaNode<T>(int metaEntityID, out T node) where T : INode; | |||
T QueryMetaNode<T>(int metaEntityID) where T : INode; | |||
FasterReadOnlyListCast<INode, T> QueryMetaNodes<T>() where T : INode; | |||
bool TryQueryMetaNode<T>(int metaEntityID, out T node); | |||
T QueryMetaNode<T>(int metaEntityID); | |||
FasterReadOnlyList<T> QueryMetaNodes<T>(); | |||
} | |||
} | |||
@@ -3,22 +3,17 @@ namespace Svelto.ECS | |||
public interface INode | |||
{} | |||
public interface INodeWithID:INode | |||
{ | |||
int ID { get; } | |||
} | |||
public interface IStructNodeWithID : INode | |||
{ | |||
int ID { get; set; } | |||
} | |||
public interface IGroupedStructNodeWithID : IStructNodeWithID | |||
public interface IGroupedNode | |||
{ | |||
int groupID { get; set; } | |||
} | |||
public class NodeWithID: INodeWithID | |||
public class NodeWithID: INode | |||
{ | |||
public static TNodeType BuildNode<TNodeType>(int ID) where TNodeType: NodeWithID, new() | |||
{ | |||
@@ -1,9 +1,9 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public abstract class MultiNodesEngine<T> | |||
where T : INode | |||
public abstract class MultiNodesEngine<T> where T:class | |||
{ | |||
protected abstract void AddNode(T node); | |||
protected abstract void RemoveNode(T node); | |||
@@ -16,32 +16,89 @@ namespace Svelto.ECS | |||
{ | |||
public abstract System.Type[] AcceptedNodes(); | |||
public abstract void Add(INode node); | |||
public abstract void Remove(INode node); | |||
public abstract void Add(ITypeSafeList nodeWrapper); | |||
public abstract void Remove(ITypeSafeList nodeWrapper); | |||
} | |||
public abstract class MultiNodesEngine<T, U> : MultiNodesEngine<T>, | |||
INodeEngine | |||
where T : INode | |||
where U : INode | |||
INodeEngine where T:class where U:class | |||
{ | |||
protected abstract void AddNode(U node); | |||
protected abstract void RemoveNode(U node); | |||
public void Add(INode node) | |||
public virtual void Add(ITypeSafeList nodes) | |||
{ | |||
if (node is T) | |||
AddNode((T)node); | |||
if (nodes is FasterList<U>) | |||
{ | |||
var strongTypeNodes = (FasterList<U>)nodes; | |||
for (int i = 0; i < strongTypeNodes.Count; i++) | |||
{ | |||
AddNode(strongTypeNodes[i]); | |||
} | |||
} | |||
else | |||
AddNode((U)node); | |||
if (nodes is FasterList<T>) | |||
{ | |||
var strongTypeNodes = (FasterList<T>)nodes; | |||
for (int i = 0; i < strongTypeNodes.Count; i++) | |||
{ | |||
AddNode(strongTypeNodes[i]); | |||
} | |||
} | |||
} | |||
public void Remove(INode node) | |||
public virtual void Remove(ITypeSafeList nodeWrapper) | |||
{ | |||
if (node is T) | |||
RemoveNode((T)node); | |||
/* if (nodeWrapper is NodeWrapper<T>) | |||
{ | |||
T node; | |||
nodeWrapper.GetNode<T>(out node); | |||
RemoveNode(ref node); | |||
} | |||
else | |||
{ | |||
U node; | |||
nodeWrapper.GetNode<U>(out node); | |||
RemoveNode(ref node); | |||
}*/ | |||
} | |||
} | |||
public abstract class MultiNodesEngine<T, U, V> : MultiNodesEngine<T, U> where T: class where U : class | |||
{ | |||
protected abstract void AddNode(V node); | |||
protected abstract void RemoveNode(V node); | |||
public override void Add(ITypeSafeList nodes) | |||
{ | |||
if (nodes is FasterList<V>) | |||
{ | |||
var strongTypeNodes = (FasterList<V>)nodes; | |||
for (int i = 0; i < strongTypeNodes.Count; i++) | |||
{ | |||
AddNode(strongTypeNodes[i]); | |||
} | |||
} | |||
else | |||
base.Add(nodes); | |||
} | |||
public override void Remove(ITypeSafeList nodeWrapper) | |||
{ | |||
/* if (nodeWrapper is V) | |||
{ | |||
V node; | |||
nodeWrapper.GetNode<V>(out node); | |||
RemoveNode(ref node); | |||
} | |||
else | |||
RemoveNode((U)node); | |||
base.Remove(nodeWrapper);*/ | |||
} | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public interface INodeBuilder | |||
{ | |||
INode BuildAndAddToList(ref ITypeSafeList list, int entityID); | |||
Type GetNodeType(); | |||
FillNodeMode reflects { get; } | |||
} | |||
public class NodeBuilder<NodeType> : INodeBuilder where NodeType : NodeWithID, new() | |||
{ | |||
public INode BuildAndAddToList(ref ITypeSafeList list, int entityID) | |||
{ | |||
if (list == null) | |||
list = new TypeSafeFasterList<NodeType>(); | |||
var castedList = list as FasterList<NodeType>; | |||
var node = NodeWithID.BuildNode<NodeType>(entityID); | |||
castedList.Add(node); | |||
return node; | |||
} | |||
public FillNodeMode reflects | |||
{ | |||
get { return FillNodeMode.Strict; } | |||
} | |||
public Type GetNodeType() | |||
{ | |||
return typeof(NodeType); | |||
} | |||
} | |||
public class StructNodeBuilder<NodeType> : INodeBuilder where NodeType : struct, IStructNodeWithID | |||
{ | |||
public INode BuildAndAddToList(ref ITypeSafeList list, int entityID) | |||
{ | |||
var node = default(NodeType); | |||
node.ID = entityID; | |||
if (list == null) | |||
list = new TypeSafeFasterList<NodeType>(); | |||
var castedList = list as FasterList<NodeType>; | |||
castedList.Add(node); | |||
return null; | |||
} | |||
public Type GetNodeType() | |||
{ | |||
return typeof(NodeType); | |||
} | |||
public virtual FillNodeMode reflects | |||
{ | |||
get { return FillNodeMode.None; } | |||
} | |||
} | |||
public enum FillNodeMode | |||
{ | |||
Strict, | |||
None | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
#if UNITY_EDITOR | |||
#if asdUNITY_EDITOR | |||
using System; | |||
using System.Collections.Generic; | |||
using UnityEditor; | |||
@@ -1,3 +1,4 @@ | |||
#if asdDEBUG | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
@@ -12,31 +13,31 @@ namespace Svelto.ECS.Profiler | |||
{ | |||
static readonly Stopwatch _stopwatch = new Stopwatch(); | |||
public static void MonitorAddDuration(Action<INodeEngine, INode> addingFunc, INodeEngine engine, INode node) | |||
public static void MonitorAddDuration(Action<INodeEngine, INodeWrapper> addingFunc, INodeEngine engine, INodeWrapper node) | |||
{ | |||
EngineInfo info; | |||
if (engineInfos.TryGetValue(engine.GetType(), out info)) | |||
{ | |||
_stopwatch.Reset(); | |||
_stopwatch.Start(); | |||
addingFunc(engine, node); | |||
_stopwatch.Stop(); | |||
_stopwatch.Reset(); | |||
info.AddAddDuration(_stopwatch.Elapsed.TotalMilliseconds); | |||
} | |||
} | |||
public static void MonitorRemoveDuration(Action<INodeEngine, INode> removeFunc, INodeEngine engine, INode node) | |||
public static void MonitorRemoveDuration(Action<INodeEngine, INodeWrapper> removeFunc, INodeEngine engine, INodeWrapper node) | |||
{ | |||
EngineInfo info; | |||
if (engineInfos.TryGetValue(engine.GetType(), out info)) | |||
{ | |||
_stopwatch.Reset(); | |||
_stopwatch.Start(); | |||
removeFunc(engine, node); | |||
engine.Remove(node); | |||
_stopwatch.Stop(); | |||
// engine.Remove(node); | |||
_stopwatch.Reset(); | |||
info.AddRemoveDuration(_stopwatch.Elapsed.TotalMilliseconds); | |||
} | |||
} | |||
@@ -60,3 +61,4 @@ namespace Svelto.ECS.Profiler | |||
public static readonly Dictionary<Type, EngineInfo> engineInfos = new Dictionary<Type, EngineInfo>(); | |||
} | |||
} | |||
#endif |
@@ -1,4 +1,4 @@ | |||
#if UNITY_EDITOR | |||
#if asdUNITY_EDITOR | |||
using System; | |||
using System.Collections.Generic; | |||
using UnityEngine; | |||
@@ -1,21 +1,31 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class SingleNodeEngine<TNodeType> : INodeEngine | |||
where TNodeType : INode | |||
public abstract class SingleNodeEngine<T> : INodeEngine where T:class | |||
{ | |||
public void Add(INode obj) | |||
public void Add(ITypeSafeList nodes) | |||
{ | |||
Add((TNodeType) obj); | |||
var strongTypeNodes = (FasterList<T>)nodes; | |||
for (int i = 0; i < strongTypeNodes.Count; i++) | |||
{ | |||
Add(strongTypeNodes[i]); //when byref returns will be vailable, this should be passed by reference, not copy! | |||
} | |||
} | |||
public void Remove(INode obj) | |||
public void Remove(ITypeSafeList nodes) | |||
{ | |||
Remove((TNodeType) obj); | |||
/* | |||
T node; | |||
nodeWrapper.GetNode(out node); | |||
Remove(node);*/ | |||
} | |||
protected abstract void Add(TNodeType node); | |||
protected abstract void Remove(TNodeType node); | |||
protected abstract void Add(T node); | |||
protected abstract void Remove(T node); | |||
} | |||
} |
@@ -5,7 +5,7 @@ using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public class StructNodes<T> where T:struct, IStructNodeWithID | |||
public sealed class StructNodes<T> where T:struct, IStructNodeWithID | |||
{ | |||
public T[] GetList(out int numberOfItems) | |||
{ | |||
@@ -15,7 +15,7 @@ namespace Svelto.ECS | |||
public StructNodes(SharedStructNodeLists container) | |||
{ | |||
_internalList = container.GetList<T>(); | |||
_internalList = SharedStructNodeLists.NoVirt.GetList<T>(container); | |||
} | |||
public void Add(T node) | |||
@@ -28,19 +28,20 @@ namespace Svelto.ECS | |||
readonly FasterList<T> _internalList; | |||
} | |||
public class StructGroupNodes<T> | |||
where T : struct, IGroupedStructNodeWithID | |||
public struct StructGroupNodes<T> | |||
where T : struct, IStructNodeWithID | |||
{ | |||
public StructGroupNodes(SharedGroupedStructNodesLists container) | |||
{ | |||
_container = container; | |||
indices = new Dictionary<int, int>(); | |||
} | |||
public void Add(int groupID, T node) | |||
{ | |||
T convert = (T)node; | |||
var fasterList = (_container.GetList<T>(groupID) as FasterList<T>); | |||
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>); | |||
indices[node.ID] = fasterList.Count; | |||
fasterList.Add(convert); | |||
@@ -48,7 +49,7 @@ namespace Svelto.ECS | |||
public void Remove(int groupID, T node) | |||
{ | |||
var fasterList = (_container.GetList<T>(groupID) as FasterList<T>); | |||
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>); | |||
var index = indices[node.ID]; | |||
indices.Remove(node.ID); | |||
@@ -58,77 +59,83 @@ namespace Svelto.ECS | |||
public T[] GetList(int groupID, out int numberOfItems) | |||
{ | |||
var fasterList = (_container.GetList<T>(groupID) as FasterList<T>); | |||
numberOfItems = fasterList.Count; | |||
return fasterList.ToArrayFast(); | |||
var fasterList = (SharedGroupedStructNodesLists.NoVirt.GetList<T>(_container, groupID) as FasterList<T>); | |||
numberOfItems = FasterList<T>.NoVirt.Count(fasterList); | |||
return FasterList<T>.NoVirt.ToArrayFast(fasterList); | |||
} | |||
readonly SharedGroupedStructNodesLists _container; | |||
readonly Dictionary<int, int> indices = new Dictionary<int, int>(); | |||
readonly Dictionary<int, int> indices; | |||
} | |||
public class SharedStructNodeLists | |||
{ | |||
readonly Dictionary<Type, IFasterList> _collection; | |||
internal SharedStructNodeLists() | |||
{ | |||
_collection = new Dictionary<Type, IFasterList>(); | |||
_collection = new Dictionary<Type, ITypeSafeList>(); | |||
} | |||
internal FasterList<T> GetList<T>() where T:struct | |||
internal static class NoVirt | |||
{ | |||
IFasterList list; | |||
if (_collection.TryGetValue(typeof (T), out list)) | |||
internal static FasterList<T> GetList<T>(SharedStructNodeLists obj) where T : struct | |||
{ | |||
return list as FasterList<T>; | |||
} | |||
ITypeSafeList list; | |||
if (obj._collection.TryGetValue(typeof(T), out list)) | |||
{ | |||
return list as FasterList<T>; | |||
} | |||
list = new FasterList<T>(); | |||
list = new TypeSafeFasterList<T>(); | |||
_collection.Add(typeof (T), list); | |||
obj._collection.Add(typeof(T), list); | |||
return (FasterList<T>) list; | |||
return (FasterList<T>)list; | |||
} | |||
} | |||
readonly Dictionary<Type, ITypeSafeList> _collection; | |||
} | |||
public class SharedGroupedStructNodesLists | |||
{ | |||
internal SharedGroupedStructNodesLists() | |||
{ | |||
_collection = new Dictionary<Type, Dictionary<int, IFasterList>>(); | |||
_collection = new Dictionary<Type, Dictionary<int, ITypeSafeList>>(); | |||
} | |||
internal IFasterList GetList<T>(int groupID) where T : struct | |||
internal static class NoVirt | |||
{ | |||
Dictionary<int, IFasterList> dic = GetGroup<T>(); | |||
IFasterList localList; | |||
if (dic.TryGetValue(groupID, out localList)) | |||
return localList; | |||
internal static ITypeSafeList GetList<T>(SharedGroupedStructNodesLists list, int groupID) where T : struct | |||
{ | |||
Dictionary<int, ITypeSafeList> dic = GetGroup<T>(list); | |||
ITypeSafeList localList; | |||
localList = new FasterList<T>(); | |||
dic.Add(groupID, localList); | |||
if (dic.TryGetValue(groupID, out localList)) | |||
return localList; | |||
return localList; | |||
} | |||
localList = new TypeSafeFasterList<T>(); | |||
dic.Add(groupID, localList); | |||
internal Dictionary<int, IFasterList> GetGroup<T>() where T : struct | |||
{ | |||
Dictionary<int, IFasterList> dic; | |||
return localList; | |||
} | |||
if (_collection.TryGetValue(typeof(T), out dic)) | |||
internal static Dictionary<int, ITypeSafeList> GetGroup<T>(SharedGroupedStructNodesLists list) where T : struct | |||
{ | |||
return dic; | |||
} | |||
Dictionary<int, ITypeSafeList> dic; | |||
if (list._collection.TryGetValue(typeof(T), out dic)) | |||
{ | |||
return dic; | |||
} | |||
dic = new Dictionary<int, IFasterList>(); | |||
dic = new Dictionary<int, ITypeSafeList>(); | |||
_collection.Add(typeof(T), dic); | |||
list._collection.Add(typeof(T), dic); | |||
return dic; | |||
return dic; | |||
} | |||
} | |||
readonly Dictionary<Type, Dictionary<int, IFasterList>> _collection; | |||
readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _collection; | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
#if UNITY_5_3_OR_NEWER || UNITY_5 | |||
using UnityEngine; | |||
namespace Svelto.Factories | |||
@@ -10,3 +11,4 @@ namespace Svelto.Factories | |||
GameObject Build(GameObject prefab); | |||
} | |||
} | |||
#endif |
@@ -1,3 +1,5 @@ | |||
#if UNITY_5_3_OR_NEWER || UNITY_5 | |||
using System; | |||
using UnityEngine; | |||
@@ -9,3 +11,4 @@ namespace Svelto.Factories | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,42 @@ | |||
using System; | |||
namespace Svelto.Observer | |||
{ | |||
public delegate void ObserverAction<DispatchType>(ref DispatchType parameter); | |||
public interface IObservable | |||
{ | |||
event Action Notify; | |||
void Dispatch(); | |||
} | |||
public interface IObservable<DispatchType> | |||
{ | |||
event ObserverAction<DispatchType> Notify; | |||
void Dispatch(ref DispatchType parameter); | |||
} | |||
public class Observable<DispatchType>:IObservable<DispatchType> | |||
{ | |||
public event ObserverAction<DispatchType> Notify; | |||
public void Dispatch(ref DispatchType parameter) | |||
{ | |||
if (Notify != null) | |||
Notify(ref parameter); | |||
} | |||
} | |||
public class Observable:IObservable | |||
{ | |||
public event Action Notify; | |||
public void Dispatch() | |||
{ | |||
if (Notify != null) | |||
Notify(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,111 @@ | |||
using System; | |||
namespace Svelto.Observer.InterNamespace | |||
{ | |||
public abstract class Observer<DispatchType, ActionType> : IObserver<ActionType> | |||
{ | |||
protected Observer(Observable<DispatchType> observable) | |||
{ | |||
observable.Notify += OnObservableDispatched; | |||
_unsubscribe = () => observable.Notify -= OnObservableDispatched; | |||
} | |||
public void AddAction(ObserverAction<ActionType> action) | |||
{ | |||
_actions += action; | |||
} | |||
public void RemoveAction(ObserverAction<ActionType> action) | |||
{ | |||
_actions -= action; | |||
} | |||
public void Unsubscribe() | |||
{ | |||
_unsubscribe(); | |||
} | |||
void OnObservableDispatched(ref DispatchType dispatchNotification) | |||
{ | |||
if (_actions != null) | |||
{ | |||
var actionType = TypeMap(ref dispatchNotification); | |||
_actions(ref actionType); | |||
} | |||
} | |||
protected abstract ActionType TypeMap(ref DispatchType dispatchNotification); | |||
ObserverAction<ActionType> _actions; | |||
Action _unsubscribe; | |||
} | |||
} | |||
namespace Svelto.Observer.IntraNamespace | |||
{ | |||
public class Observer<DispatchType> : InterNamespace.Observer<DispatchType, DispatchType> | |||
{ | |||
public Observer(Observable<DispatchType> observable) : base(observable) | |||
{ } | |||
protected override DispatchType TypeMap(ref DispatchType dispatchNotification) | |||
{ | |||
return dispatchNotification; | |||
} | |||
} | |||
} | |||
namespace Svelto.Observer | |||
{ | |||
public class Observer: IObserver | |||
{ | |||
public Observer(Observable observable) | |||
{ | |||
observable.Notify += OnObservableDispatched; | |||
_unsubscribe = () => observable.Notify -= OnObservableDispatched; | |||
} | |||
public void AddAction(Action action) | |||
{ | |||
_actions += action; | |||
} | |||
public void RemoveAction(Action action) | |||
{ | |||
_actions -= action; | |||
} | |||
public void Unsubscribe() | |||
{ | |||
_unsubscribe(); | |||
} | |||
void OnObservableDispatched() | |||
{ | |||
if (_actions != null) | |||
_actions(); | |||
} | |||
Action _actions; | |||
readonly Action _unsubscribe; | |||
} | |||
public interface IObserver<WatchingType> | |||
{ | |||
void AddAction(ObserverAction<WatchingType> action); | |||
void RemoveAction(ObserverAction<WatchingType> action); | |||
void Unsubscribe(); | |||
} | |||
public interface IObserver | |||
{ | |||
void AddAction(Action action); | |||
void RemoveAction(Action action); | |||
void Unsubscribe(); | |||
} | |||
} |
@@ -11,22 +11,25 @@ namespace Utility | |||
public static class Console | |||
{ | |||
static StringBuilder _stringBuilder = new StringBuilder(256); | |||
#if UNITY_5_3_OR_NEWER || UNITY_5 | |||
public static ILogger logger = new SlowLoggerUnity(); | |||
#else | |||
public static ILogger logger = new SimpleLogger(); | |||
#endif | |||
public static ILogger logger; | |||
public static volatile bool BatchLog = false; | |||
//Hack, have to find the right solution | |||
public static Action<Exception, object, string, string> onException; | |||
public static Action<Exception, string, string> onException; | |||
static Console() | |||
{ | |||
onException = (e, obj, message, stack) => | |||
{ | |||
UnityEngine.Debug.LogException(e, (UnityEngine.Object)obj); | |||
}; | |||
#if UNITY_5_3_OR_NEWER || UNITY_5 | |||
logger = new SlowLoggerUnity(); | |||
onException = (e, message, stack) => | |||
{ | |||
UnityEngine.Debug.LogException(e, null); | |||
}; | |||
#else | |||
logger = new SimpleLogger(); | |||
#endif | |||
} | |||
public static void Log(string txt) | |||
@@ -67,11 +70,6 @@ namespace Utility | |||
} | |||
public static void LogException(Exception e) | |||
{ | |||
LogException(e, null); | |||
} | |||
public static void LogException(Exception e, UnityEngine.Object obj) | |||
{ | |||
string toPrint; | |||
string stackTrace; | |||
@@ -96,7 +94,8 @@ namespace Utility | |||
toPrint = _stringBuilder.ToString(); | |||
} | |||
onException(e, obj, toPrint, stackTrace); | |||
if (onException != null) | |||
onException(e, toPrint, stackTrace); | |||
} | |||
public static void LogWarning(string txt) | |||
@@ -142,8 +141,12 @@ namespace Utility | |||
toPrint = _stringBuilder.ToString(); | |||
} | |||
#if !UNITY_EDITOR && !NETFX_CORE | |||
#if !UNITY_EDITOR | |||
#if !NETFX_CORE | |||
System.Console.WriteLine(toPrint); | |||
#else | |||
//find a way to adopt a logger externally, if this is still needed | |||
#endif | |||
#else | |||
UnityEngine.Debug.Log(toPrint); | |||
#endif | |||
@@ -0,0 +1,125 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.DataStructures; | |||
namespace Svelto.Utilities | |||
{ | |||
public static class NetFXCoreWrappers | |||
{ | |||
public static MethodInfo GetMethodInfoEx(this Delegate delegateEx) | |||
{ | |||
#if NETFX_CORE | |||
var method = delegateEx.GetMethodInfo(); | |||
#else | |||
var method = delegateEx.Method; | |||
#endif | |||
return method; | |||
} | |||
public static Type GetDeclaringType(this MemberInfo memberInfo) | |||
{ | |||
#if NETFX_CORE | |||
return memberInfo.DeclaringType; | |||
#else | |||
return memberInfo.ReflectedType; | |||
#endif | |||
} | |||
public static Type GetBaseType(this Type type) | |||
{ | |||
#if NETFX_CORE | |||
return type.BaseType(); | |||
#else | |||
return type.BaseType; | |||
#endif | |||
} | |||
public static IEnumerable<Attribute> GetCustomAttributes(this Type type, bool inherit) | |||
{ | |||
#if !NETFX_CORE | |||
return Attribute.GetCustomAttributes(type, inherit); | |||
#else | |||
return type.GetTypeInfo().GetCustomAttributes(inherit); | |||
#endif | |||
} | |||
public static bool ContainsCustomAttribute(this MemberInfo memberInfo, Type customAttribute, bool inherit) | |||
{ | |||
#if !NETFX_CORE | |||
return Attribute.IsDefined(memberInfo, customAttribute, inherit); | |||
#else | |||
return memberInfo.GetCustomAttribute(customAttribute, inherit) != null; | |||
#endif | |||
} | |||
public static bool IsGenericTypeEx(this Type type) | |||
{ | |||
#if !NETFX_CORE | |||
return type.IsGenericType; | |||
#else | |||
return type.IsConstructedGenericType; | |||
#endif | |||
} | |||
public static MemberInfo[] FindWritablePropertiesWithCustomAttribute(this Type contract, | |||
Type customAttributeType) | |||
{ | |||
FasterList<MemberInfo> propertyList = new FasterList<MemberInfo>(8); | |||
do | |||
{ | |||
var propertyInfos = contract.GetProperties(System.Reflection.BindingFlags.Public | | |||
System.Reflection.BindingFlags.NonPublic | | |||
System.Reflection.BindingFlags.DeclaredOnly | | |||
System.Reflection.BindingFlags.Instance); | |||
for (int i = 0; i < propertyInfos.Length; i++) | |||
{ | |||
PropertyInfo propertyInfo = propertyInfos[i]; | |||
if (propertyInfo.CanWrite && | |||
propertyInfo.ContainsCustomAttribute(customAttributeType, false) == true) | |||
propertyList.Add(propertyInfo); | |||
} | |||
contract = contract.GetBaseType(); | |||
} while (contract != null); | |||
if (propertyList.Count > 0) | |||
return propertyList.ToArray(); | |||
return null; | |||
} | |||
public static bool IsCompilerGenerated(this Type t) | |||
{ | |||
#if NETFX_CORE | |||
var attr = t.GetTypeInfo().GetCustomAttribute(typeof(CompilerGeneratedAttribute)); | |||
return attr != null; | |||
#else | |||
var attr = Attribute.IsDefined(t, typeof(CompilerGeneratedAttribute)); | |||
return attr; | |||
#endif | |||
} | |||
public static bool IsCompilerGenerated(this MemberInfo memberInfo) | |||
{ | |||
#if NETFX_CORE | |||
var attr = memberInfo.DeclaringType.GetTypeInfo().GetCustomAttribute(_compilerType); | |||
return attr != null; | |||
#else | |||
var attr = Attribute.IsDefined(memberInfo, _compilerType); | |||
return attr; | |||
#endif | |||
} | |||
static readonly Type _compilerType = typeof(CompilerGeneratedAttribute); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System.Threading; | |||
namespace Svelto.Utilities | |||
{ | |||
public static class ThreadUtility | |||
{ | |||
public static void MemoryBarrier() | |||
{ | |||
#if NETFX_CORE || NET_4_6 | |||
Interlocked.MemoryBarrier(); | |||
#else | |||
Thread.MemoryBarrier(); | |||
#endif | |||
} | |||
} | |||
} |
@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Utilities; | |||
namespace Svelto.WeakEvents | |||
{ | |||
@@ -71,14 +72,8 @@ namespace Svelto.WeakEvents | |||
Method = method; | |||
#if NETFX_CORE | |||
var attributes = (CompilerGeneratedAttribute[])method.GetType().GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false); | |||
if (attributes.Length != 0) | |||
throw new ArgumentException("Cannot create weak event to anonymous method with closure."); | |||
#else | |||
if (method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) | |||
if (method.IsCompilerGenerated() == true) | |||
throw new ArgumentException("Cannot create weak event to anonymous method with closure."); | |||
#endif | |||
} | |||
protected void Invoke_Internal(object[] data) | |||
@@ -2,6 +2,7 @@ using System; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using Svelto.Utilities; | |||
//careful, you must handle the destruction of the GCHandles! | |||
namespace Svelto.WeakEvents | |||
@@ -116,15 +117,8 @@ namespace Svelto.WeakEvents | |||
methodOut = method; | |||
#if DEBUG && !PROFILER | |||
#if NETFX_CORE | |||
Method = listener.GetMethodInfo(); | |||
var attributes = (CompilerGeneratedAttribute[])Method.GetType().GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false); | |||
if(attributes.Length != 0) | |||
if (method.IsCompilerGenerated() == true) | |||
throw new ArgumentException("Cannot create weak event to anonymous method with closure."); | |||
#else | |||
if (method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) | |||
throw new ArgumentException("Cannot create weak event to anonymous method with closure."); | |||
#endif | |||
#endif | |||
} | |||
@@ -1,6 +1,7 @@ | |||
using Svelto.DataStructures; | |||
using System; | |||
using System.Reflection; | |||
using Svelto.Utilities; | |||
namespace Svelto.WeakEvents | |||
{ | |||
@@ -9,7 +10,7 @@ namespace Svelto.WeakEvents | |||
public static WeakEvent operator+(WeakEvent c1, Action x) | |||
{ | |||
if (c1 == null) c1 = new WeakEvent(); | |||
c1._subscribers.Add(new WeakActionStruct(x)); | |||
c1.Add(x); | |||
return c1; | |||
} | |||
@@ -17,11 +18,21 @@ namespace Svelto.WeakEvents | |||
public static WeakEvent operator-(WeakEvent c1, Action x) | |||
{ | |||
DesignByContract.Check.Require(x != null); | |||
c1.Remove(x.Target, x.GetMethodInfoEx()); | |||
c1.Remove(x); | |||
return c1; | |||
} | |||
public void Add(Action x) | |||
{ | |||
_subscribers.Add(new WeakActionStruct(x)); | |||
} | |||
public void Remove(Action x) | |||
{ | |||
RemoveInternal(x.Target, x.GetMethodInfoEx()); | |||
} | |||
public void Invoke() | |||
{ | |||
for (int i = 0; i < _subscribers.Count; i++) | |||
@@ -29,7 +40,7 @@ namespace Svelto.WeakEvents | |||
_subscribers.UnorderedRemoveAt(i--); | |||
} | |||
void Remove(object thisObject, MethodInfo thisMethod) | |||
void RemoveInternal(object thisObject, MethodInfo thisMethod) | |||
{ | |||
for (int i = 0; i < _subscribers.Count; ++i) | |||
{ | |||
@@ -58,7 +69,7 @@ namespace Svelto.WeakEvents | |||
public static WeakEvent<T1> operator+(WeakEvent<T1> c1, Action<T1> x) | |||
{ | |||
if (c1 == null) c1 = new WeakEvent<T1>(); | |||
c1._subscribers.Add(new WeakActionStruct<T1>(x)); | |||
c1.Add(x); | |||
return c1; | |||
} | |||
@@ -66,11 +77,21 @@ namespace Svelto.WeakEvents | |||
public static WeakEvent<T1> operator-(WeakEvent<T1> c1, Action<T1> x) | |||
{ | |||
DesignByContract.Check.Require(x != null); | |||
c1.Remove(x.Target, x.GetMethodInfoEx()); | |||
c1.Remove(x); | |||
return c1; | |||
} | |||
public void Add(Action<T1> x) | |||
{ | |||
_subscribers.Add(new WeakActionStruct<T1>(x)); | |||
} | |||
public void Remove(Action<T1> x) | |||
{ | |||
RemoveInternal(x.Target, x.GetMethodInfoEx()); | |||
} | |||
public void Invoke(T1 arg1) | |||
{ | |||
args[0] = arg1; | |||
@@ -80,7 +101,7 @@ namespace Svelto.WeakEvents | |||
_subscribers.UnorderedRemoveAt(i--); | |||
} | |||
void Remove(object thisObject, MethodInfo thisMethod) | |||
void RemoveInternal(object thisObject, MethodInfo thisMethod) | |||
{ | |||
for (int i = 0; i < _subscribers.Count; ++i) | |||
{ | |||
@@ -119,11 +140,21 @@ namespace Svelto.WeakEvents | |||
public static WeakEvent<T1, T2> operator-(WeakEvent<T1, T2> c1, Action<T1, T2> x) | |||
{ | |||
DesignByContract.Check.Require(x != null); | |||
c1.Remove(x.Target, x.GetMethodInfoEx()); | |||
c1.Remove(x); | |||
return c1; | |||
} | |||
public void Add(Action<T1, T2> x) | |||
{ | |||
_subscribers.Add(new WeakActionStruct<T1, T2>(x)); | |||
} | |||
public void Remove(Action<T1, T2> x) | |||
{ | |||
RemoveInternal(x.Target, x.GetMethodInfoEx()); | |||
} | |||
public void Invoke(T1 arg1, T2 arg2) | |||
{ | |||
args[0] = arg1; | |||
@@ -134,7 +165,7 @@ namespace Svelto.WeakEvents | |||
_subscribers.UnorderedRemoveAt(i--); | |||
} | |||
void Remove(object thisObject, MethodInfo thisMethod) | |||
void RemoveInternal(object thisObject, MethodInfo thisMethod) | |||
{ | |||
for (int i = 0; i < _subscribers.Count; ++i) | |||
{ | |||