zhouqijie

Job的链表兼容

CRE:NativeContainer目前只支持数组、字符串、哈希表等。
CRE:对于链表的支持,可以使用自定义的静态链表,并且静态链表的内存布局对CPU的缓存一致性也比较友好。

一个静态链表示例



public class NativeSLL<T> : IEnumerable<T>, IEnumerable
{
    //节点
    public struct Node
    {
        public int next;
        public int prev;
        public T value;
    }
    public NativeArray<Node> nodes;

    //指针
    private int firstPtr;
    private int lastPtr;
    private int emptyFirstPtr;
    private int emptyLastPtr;


    //标记
    private int count;

    //数据
    private int datalength;
    private T defaultValue;

    // ---------------------------- Public Interface -------------------------------

    public NativeSLL(int initLength, T defaultValue = default)
    {
        this.datalength = initLength;
        this.defaultValue = defaultValue;

        //this.count = -1;
        //this.firstPtr = -1;
        //this.lastPtr = -1;
        //this.emptyFirstPtr = -1;
        //this.emptyLastPtr = -1;
        //this.nodes = default;
        Clear();
    }


    public int Count => this.count;


    public int AddLast(T newItem)
    {
        return AddLastInternal(newItem);
    }

    public void Remove(T item)
    {
        if (object.Equals(item, this.defaultValue)) return;


        for (int i = 0; i < nodes.Length; ++i)
        {
            if (object.Equals(nodes[i].value, item))
            {
                RemoveAtInternal(i);
                return;
            }
        }
    }


    public void RemoveAt(int indexInNodes)
    {
        RemoveAtInternal(indexInNodes);
    }

    public void Clear()
    {
        if (nodes.IsCreated) nodes.Dispose();
        nodes = new NativeArray<Node>(this.datalength, Allocator.Persistent);

        this.firstPtr = 0;
        this.lastPtr = 0;
        this.emptyFirstPtr = 0;
        this.emptyLastPtr = this.datalength - 1;

        for (int i = 0; i < nodes.Length; ++i)
        {
            int nodePrev = (i - 1) > -1 ? (i - 1) : -1;
            int nodeNext = (i + 1) < (nodes.Length) ? (i + 1) : -1;
            nodes[i] = new Node() { value = this.defaultValue, next = nodeNext, prev = nodePrev };
        }

        this.count = 0;
    }

    public void SetValueAt(int indexInNodes, T newvalue)
    {
        var node = this.nodes[indexInNodes];

        node.value = newvalue;

        this.nodes[indexInNodes] = node;
    }

    public IEnumerator<T> GetEnumerator()
    {
        int current = firstPtr;
        while (current != emptyFirstPtr && current != -1)
        {
            if (object.Equals(nodes[current].value, defaultValue))
            {
                Debug.LogAssertion("遍历到空元素:打印链表:");
                Log();
            }

            yield return nodes[current].value;

            if (current == lastPtr) break;

            current = nodes[current].next;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        int current = firstPtr;
        while (current != emptyFirstPtr && current != -1)
        {
            if (object.Equals(nodes[current].value, defaultValue))
            {
                Debug.LogAssertion("遍历到空元素:打印链表:");
                Log();
            }

            yield return nodes[current].value;

            if (current == lastPtr) break;

            current = nodes[current].next;
        }
    }


    public void Dispose()
    {
        if(nodes.IsCreated)
            nodes.Dispose();
    }



    // ---------------------------- Private -------------------------------
    private int AddLastInternal(T newItem)
    {
        if (object.Equals(newItem, defaultValue))
        {
            Debug.LogAssertion("正在往表中添加默认值!");
        }

        //不足则扩充
        if (emptyFirstPtr == emptyLastPtr)
        {
            Extend();
        }
        //获取空位
        int target = emptyFirstPtr;

        //首空位指针更新
        int newEmptyFirst = this.nodes[emptyFirstPtr].next;
        if (newEmptyFirst < 0 || newEmptyFirst > this.nodes.Length - 1)
        {
            Debug.LogAssertion("空节点列表指针越界。打印:");
            Log();
        }
        emptyFirstPtr = newEmptyFirst;

        //旧的末数据节点更新
        nodes[lastPtr] = new Node() { value = nodes[lastPtr].value, prev = nodes[lastPtr].prev, next = target };

        //新的末数据节点设置
        nodes[target] = new Node() { value = newItem, prev = lastPtr, next = -1 };

        //末数据位指针更新
        lastPtr = target;

        count += 1;



        return target;
    }
    private void RemoveAtInternal(int idx)
    {
        if (idx < 0 || idx > (this.nodes.Length - 1)) return;//越界
        if (object.Equals(this.nodes[idx].value, this.defaultValue)) return;//空闲节点不能删除
        if (firstPtr == lastPtr && lastPtr == emptyFirstPtr) return; //空表  



        if (idx == firstPtr && idx == lastPtr)//唯一节点  
        {
            FreeNodeInternal(idx);
        }
        else if (idx == firstPtr)//首节点
        {
            int thisNext = nodes[idx].next;
            nodes[thisNext] = new Node() { value = nodes[thisNext].value, prev = -1, next = nodes[thisNext].next };
            firstPtr = thisNext;

            FreeNodeInternal(idx);
        }
        else if (idx == lastPtr)//末节点
        {
            int thisPrev = nodes[idx].prev;
            nodes[thisPrev] = new Node() { value = nodes[thisPrev].value, prev = nodes[thisPrev].prev, next = -1 };
            lastPtr = thisPrev;

            FreeNodeInternal(idx);
        }
        else//中间节点
        {
            int thisPrev = nodes[idx].prev;
            int thisNext = nodes[idx].next;
            if (thisPrev > -1)
            {
                nodes[thisPrev] = new Node() { value = nodes[thisPrev].value, prev = nodes[thisPrev].prev, next = nodes[idx].next };
            }
            if (thisNext > -1)
            {
                nodes[thisNext] = new Node() { value = nodes[thisNext].value, prev = nodes[idx].prev, next = nodes[thisNext].next };
            }

            FreeNodeInternal(idx);
        }

        count -= 1;
    }
    private void FreeNodeInternal(int idx)
    {
        if (idx < 0 || idx > this.nodes.Length - 1)
        {
            Debug.LogAssertion("Free Idx越界");
            Log();
        }

        int nextEmpty = nodes[emptyFirstPtr].next;
        if (emptyFirstPtr == emptyLastPtr)
            nextEmpty = emptyLastPtr;

        nodes[idx] = new Node() { value = defaultValue, prev = -1, next = emptyFirstPtr };
        nodes[emptyFirstPtr] = new Node() { value = defaultValue, prev = idx, next = nextEmpty };
        emptyFirstPtr = idx;
    }

    private void Extend()
    {
        int oldLength = this.datalength;

        //创建新的数组并拷贝数据 
        this.datalength *= 2;
        var newDataNodes = new NativeArray<Node>(this.datalength, Allocator.Temp);
        for (int i = 0; i < newDataNodes.Length; ++i)
        {
            if (i < this.nodes.Length)
            {
                newDataNodes[i] = this.nodes[i];
            }
            else
            {
                int nodePrev = (i - 1) > -1 ? (i - 1) : -1;
                int nodeNext = (i + 1) < (newDataNodes.Length) ? (i + 1) : -1;
                newDataNodes[i] = new Node() { value = this.defaultValue, next = nodeNext, prev = nodePrev };
            }
        }
        //指向新数组
        if (this.nodes.IsCreated) this.nodes.Dispose();
        this.nodes = new NativeArray<Node>(this.datalength, Allocator.Persistent);
        this.nodes.CopyFrom(newDataNodes);
        newDataNodes.Dispose();


        //首尾空节点指针设置  
        this.nodes[emptyLastPtr] = new Node() { value = this.defaultValue, prev = this.nodes[emptyLastPtr].prev, next = (oldLength - 1 + 1) };
        this.nodes[(oldLength - 1 + 1)] = new Node() { value = this.defaultValue, prev = emptyLastPtr, next = this.nodes[(oldLength - 1 + 1)].next };
        emptyLastPtr = this.nodes.Length - 1;

    }


    public void Log()
    {
        System.Text.StringBuilder strb = new System.Text.StringBuilder();

        strb.Append("************ StaticLinkedList(size:" + this.nodes.Length + ") *************");

        for (int i = 0; i < this.nodes.Length; ++i)
        {
            strb.Append("\n");

            if (i == firstPtr && i == lastPtr)
            {
                strb.Append("<color=#FF0000>first/last  -></color>\t");
            }
            else if (i == firstPtr)
            {
                strb.Append("<color=#FF0000>first       -></color>\t");
            }
            else if (i == lastPtr)
            {
                strb.Append("<color=#FF0000>last        -></color>\t");
            }
            else
            {
                strb.Append("<color=#FF0000>--------------</color>\t");
            }


            strb.Append("<color=#AAAA00>【");
            strb.Append(i);
            strb.Append("】</color>");

            strb.Append("(prev:" + this.nodes[i].prev + ")");

            strb.Append("<b>:" + this.nodes[i].value.ToString() + "</b>");

            strb.Append("(next:" + this.nodes[i].next + ")");


            if (i == emptyFirstPtr && i == emptyLastPtr)
            {
                strb.Append("<color=#00FF00>  ->  empty first/last</color>");
            }
            if (i == emptyFirstPtr)
            {
                strb.Append("<color=#00FF00>  ->  empty first</color>");
            }
            else if (i == emptyLastPtr)
            {
                strb.Append("<color=#00FF00>  ->   empty last</color>");
            }
        }

        Debug.Log(strb.ToString());
    }
}