Example #1
0
        /// <summary>
        /// Resizes the buffers backing the tree's nodes and leaves. Will not shrink the buffers below the size needed by the currently resident nodes and leaves.
        /// </summary>
        /// <param name="pool">Pool from which to take and return resources.</param>
        /// <param name="targetLeafSlotCount">The desired number of available leaf slots.</param>
        public void Resize(BufferPool pool, int targetLeafSlotCount)
        {
            //Note that it's not safe to resize below the size of potentially used leaves. If the user wants to go smaller, they'll need to explicitly deal with the leaves somehow first.
            var leafCapacityForTarget = BufferPool.GetCapacityForCount <Leaf>(Math.Max(leafCount, targetLeafSlotCount));
            //Adding incrementally checks the capacity of leaves, and issues a resize if there isn't enough space. But it doesn't check nodes.
            //You could change that, but for now, we simply ensure that the node array has sufficient room to hold everything in the resized leaf array.
            var  nodeCapacityForTarget     = BufferPool.GetCapacityForCount <Node>(Math.Max(nodeCount, leafCapacityForTarget - 1));
            var  metanodeCapacityForTarget = BufferPool.GetCapacityForCount <Metanode>(Math.Max(nodeCount, leafCapacityForTarget - 1));
            bool wasAllocated = Leaves.Allocated;

            Debug.Assert(Leaves.Allocated == Nodes.Allocated);
            if (leafCapacityForTarget != Leaves.Length)
            {
                pool.ResizeToAtLeast(ref Leaves, leafCapacityForTarget, leafCount);
                leaves = Leaves.Memory;
            }
            if (nodeCapacityForTarget != Nodes.Length)
            {
                pool.ResizeToAtLeast(ref Nodes, nodeCapacityForTarget, nodeCount);
                nodes = Nodes.Memory;
            }
            if (metanodeCapacityForTarget != Metanodes.Length)
            {
                pool.ResizeToAtLeast(ref Metanodes, metanodeCapacityForTarget, nodeCount);
                //A node's RefineFlag must be 0, so just clear out the node set.
                //TODO: This won't be necessary if we get rid of refineflags as a concept.
                Metanodes.Clear(nodeCount, Nodes.Length - nodeCount);
                metanodes = Metanodes.Memory;
            }
            if (!wasAllocated)
            {
                InitializeRoot();
            }
        }
Example #2
0
        /// <summary>
        /// Loads a tree from a byte buffer created by the Serialize function.
        /// </summary>
        /// <param name="data">Data to load into the tree.</param>
        /// <param name="pool">Pool to use to create the tree.</param>
        public Tree(Span <byte> data, BufferPool pool)
        {
            if (data.Length <= 4)
            {
                throw new ArgumentException($"Data is only {data.Length} bytes long; that's too small for even a header.");
            }
            leafCount = Unsafe.As <byte, int>(ref data[0]);
            nodeCount = leafCount - 1;
            var       leafByteCount       = leafCount * sizeof(Leaf);
            var       nodeByteCount       = nodeCount * sizeof(Node);
            var       metanodeByteCount   = nodeCount * sizeof(Metanode);
            const int leavesStartIndex    = 4;
            var       nodesStartIndex     = leavesStartIndex + leafByteCount;
            var       metanodesStartIndex = nodesStartIndex + nodeByteCount;

            if (data.Length < leavesStartIndex + leafByteCount + nodeByteCount + metanodeByteCount)
            {
                throw new ArgumentException($"Header suggested there were {leafCount} leaves, but there's not enough room in the data for that.");
            }
            pool.Take(leafCount, out Leaves);
            pool.Take(nodeCount, out Nodes);
            pool.Take(nodeCount, out Metanodes);
            leaves    = Leaves.Memory;
            nodes     = Nodes.Memory;
            metanodes = Metanodes.Memory;
            Unsafe.CopyBlockUnaligned(ref *(byte *)leaves, ref data[leavesStartIndex], (uint)leafByteCount);
            Unsafe.CopyBlockUnaligned(ref *(byte *)nodes, ref data[nodesStartIndex], (uint)nodeByteCount);
            Unsafe.CopyBlockUnaligned(ref *(byte *)metanodes, ref data[metanodesStartIndex], (uint)metanodeByteCount);
        }
Example #3
0
        /// <summary>
        /// Resizes the buffers backing the tree's nodes and leaves. Will not shrink the buffers below the size needed by the currently resident nodes and leaves.
        /// </summary>
        /// <param name="targetLeafSlotCount">The desired number of available leaf slots.</param>
        public void Resize(int targetLeafSlotCount)
        {
            //Note that it's not safe to resize below the size of potentially used leaves. If the user wants to go smaller, they'll need to explicitly deal with the leaves somehow first.
            var leafCapacityForTarget = BufferPool <Leaf> .GetLowestContainingElementCount(Math.Max(leafCount, targetLeafSlotCount));

            //Adding incrementally checks the capacity of leaves, and issues a resize if there isn't enough space. But it doesn't check nodes.
            //You could change that, but for now, we simply ensure that the node array has sufficient room to hold everything in the resized leaf array.
            var nodeCapacityForTarget = BufferPool <Node> .GetLowestContainingElementCount(Math.Max(nodeCount, leafCapacityForTarget - 1));

            bool wasAllocated = Leaves.Allocated;

            Debug.Assert(Leaves.Allocated == Nodes.Allocated);
            if (leafCapacityForTarget != Leaves.Length)
            {
                Pool.SpecializeFor <Leaf>().Resize(ref Leaves, leafCapacityForTarget, leafCount);
                leaves = (Leaf *)Leaves.Memory;
            }
            if (nodeCapacityForTarget != Nodes.Length)
            {
                Pool.SpecializeFor <Node>().Resize(ref Nodes, nodeCapacityForTarget, nodeCount);
                //A node's RefineFlag must be 0, so just clear out the node set.
                //TODO: You could avoid the bulk of this by either a) getting rid of refine flags as a concept or b) using a separate array for the node metadata (a good idea anyway).
                Nodes.Clear(nodeCount, Nodes.Length - nodeCount);
                nodes = (Node *)Nodes.Memory;
            }
            if (!wasAllocated)
            {
                InitializeRoot();
            }
        }
        unsafe void OptimizeBFS(Node *optimizedNodes, Leaf *optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var nodesToVisit = new Queue <NodeToVisit>();

            nodesToVisit.Enqueue(new NodeToVisit {
                NodeIndex = 0, OptimizedIndex = 0, OptimizedParentIndex = -1
            });
            optimizedNodeCount = 1;

            while (nodesToVisit.Count > 0)
            {
                var nodeToVisit        = nodesToVisit.Dequeue();
                var node               = nodes + nodeToVisit.NodeIndex;
                var optimizedNodeIndex = nodeToVisit.OptimizedIndex;
                var optimizedNode      = optimizedNodes + optimizedNodeIndex;
                *   optimizedNode      = *node;
                optimizedNode->Parent = nodeToVisit.OptimizedParentIndex;
                var optimizedChildren = &optimizedNode->ChildA;
                var nodeChildren      = &node->ChildA;

                for (int i = 0; i < node->ChildCount; ++i)
                {
                    if (nodeChildren[i] >= 0)
                    {
                        optimizedChildren[i] = optimizedNodeCount++;
                        nodesToVisit.Enqueue(new NodeToVisit {
                            NodeIndex = nodeChildren[i], OptimizedIndex = optimizedChildren[i], OptimizedParentIndex = optimizedNodeIndex
                        });
                    }
                    else
                    {
                        var leafIndex          = Encode(nodeChildren[i]);
                        var optimizedLeafIndex = optimizedLeafCount++;
                        var optimizedLeaf      = optimizedLeaves + optimizedLeafIndex;
                        optimizedLeaf->Id         = leaves[leafIndex].Id;
                        optimizedLeaf->NodeIndex  = optimizedNodeIndex;
                        optimizedLeaf->ChildIndex = i;
                        optimizedChildren[i]      = Encode(optimizedLeafIndex);
                    }
                }
            }
        }
        unsafe void OptimizeGroupDFS(int optimizedNodeIndex, int optimizedParentNodeIndex, int nodeIndex, Node *optimizedNodes, Leaf *optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var node = nodes + nodeIndex;

            var optimizedNode = optimizedNodes + optimizedNodeIndex;

            *optimizedNode = *node;
            optimizedNode->Parent = optimizedParentNodeIndex;
            var optimizedChildren = &optimizedNode->ChildA;
            var nodeChildren      = &node->ChildA;

            for (int i = 0; i < node->ChildCount; ++i)
            {
                if (nodeChildren[i] >= 0)
                {
                    optimizedChildren[i] = optimizedNodeCount++;
                }
                else
                {
                    var leafIndex          = Encode(nodeChildren[i]);
                    var optimizedLeafIndex = optimizedLeafCount++;
                    var optimizedLeaf      = optimizedLeaves + optimizedLeafIndex;
                    optimizedLeaf->Id         = leaves[leafIndex].Id;
                    optimizedLeaf->NodeIndex  = optimizedNodeIndex;
                    optimizedLeaf->ChildIndex = i;
                    optimizedChildren[i]      = Encode(optimizedLeafIndex);
                }
            }
            for (int i = 0; i < node->ChildCount; ++i)
            {
                if (nodeChildren[i] >= 0)
                {
                    OptimizeGroupDFS(optimizedChildren[i], optimizedNodeIndex, nodeChildren[i], optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                }
            }
        }
 unsafe void OptimizeGroupDFS(Node *optimizedNodes, Leaf *optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
 {
     optimizedNodeCount = 1;
     OptimizeGroupDFS(0, -1, 0, optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
 }
        unsafe int OptimizeDFS(int optimizedParentIndex, int nodeIndex, Node *optimizedNodes, Leaf *optimizedLeaves, ref int optimizedNodeCount, ref int optimizedLeafCount)
        {
            var node = nodes + nodeIndex;
            var optimizedNodeIndex = optimizedNodeCount++;
            var optimizedNode      = optimizedNodes + optimizedNodeIndex;

            *optimizedNode = *node;
            optimizedNode->Parent = optimizedParentIndex;
            var children = &optimizedNode->ChildA;

            for (int i = 0; i < node->ChildCount; ++i)
            {
                if (children[i] >= 0)
                {
                    children[i] = OptimizeDFS(optimizedNodeIndex, children[i], optimizedNodes, optimizedLeaves, ref optimizedNodeCount, ref optimizedLeafCount);
                }
                else
                {
                    var leafIndex          = Encode(children[i]);
                    var optimizedLeafIndex = optimizedLeafCount++;
                    var optimizedLeaf      = optimizedLeaves + optimizedLeafIndex;
                    optimizedLeaf->Id         = leaves[leafIndex].Id;
                    optimizedLeaf->NodeIndex  = optimizedNodeIndex;
                    optimizedLeaf->ChildIndex = i;
                    children[i] = Encode(optimizedLeafIndex);
                }
            }
            return(optimizedNodeIndex);
        }