private ushort GetHead(int arenaIndex) { return(_memory.Read <ushort>(_headsPtr + (arenaIndex * sizeof(ushort)))); }
/// <summary> /// Find the chunk (with start index) that contains or is before the given index /// </summary> protected void FindNearestChunk(uint targetIndex, out bool found, out long chunkPtr, out uint chunkIndex) { unchecked { // 1. Calculate desired chunk index uint targetChunkIdx = (uint)(targetIndex / ElemsPerChunk); uint endChunkIdx = (uint)((_elementCount - 1) / ElemsPerChunk); // 2. Optimise for start- and end- of chain (small lists & very likely for Push & Pop) if (targetChunkIdx == 0) { // start of chain found = true; chunkPtr = _baseChunkTable; chunkIndex = targetChunkIdx; return; } if (_elementCount == 0 || targetChunkIdx == endChunkIdx) { // lands in a chunk found = true; chunkPtr = _endChunkPtr; chunkIndex = targetChunkIdx; return; } if (targetIndex >= _elementCount) { // lands outside a chunk -- off the end found = false; chunkPtr = _endChunkPtr; chunkIndex = targetChunkIdx; return; } // All the simple optimal paths failed. Make sure the skip list is good... MaybeRebuildSkipTable(); // 3. Use the skip table to find a chunk near the target // By ensuring the skip table is fresh, we can calculate the correct location uint startChunkIdx = 0; var chunkHeadPtr = _baseChunkTable; if (_skipEntries > 1) { // guess search bounds var guess = (targetChunkIdx * _skipEntries) / endChunkIdx; var upper = guess + 2; var lower = guess - 2; if (upper > _skipEntries) { upper = _skipEntries; } if (lower < 0) { lower = 0; } // binary search for the best chunk while (lower < upper) { var mid = ((upper - lower) / 2) + lower; if (mid == lower) { break; } var midChunkIdx = _mem.Read <uint>(_skipTable + (SKIP_ELEM_SIZE * mid)); if (midChunkIdx == targetChunkIdx) { break; } if (midChunkIdx < targetChunkIdx) { lower = mid; } else { upper = mid; } } var baseAddr = _skipTable + (SKIP_ELEM_SIZE * lower); // pointer to skip table entry startChunkIdx = _mem.Read <uint>(baseAddr); chunkHeadPtr = _mem.Read <long>(baseAddr + INDEX_SIZE); } var walk = targetChunkIdx - startChunkIdx; if (walk > 5 && _skipEntries < SKIP_TABLE_SIZE_LIMIT) { _skipTableDirty = true; // if we are walking too far, try builing a better table } // 4. Walk the chain until we find the chunk we want for (; startChunkIdx < targetChunkIdx; startChunkIdx++) { chunkHeadPtr = _mem.Read <long>(chunkHeadPtr); } found = true; chunkPtr = chunkHeadPtr; chunkIndex = targetChunkIdx; } }
public Result <long> AddChild(long parent, TElement element) { if (parent < 0) { return(Result.Fail <long>()); } var head = _mem.Read <TreeNodeHead>(parent); if (head.FirstChildPtr >= 0) // There is a sibling chain. Switch function { return(AddSibling(head.FirstChildPtr, element)); } // This is the first child of this parent var res = AllocateAndWriteNode(parent, element); if (!res.Success) { return(Result.Fail <long>()); } var newChildPtr = res.Value; // Set ourself as the parent's first child head.FirstChildPtr = newChildPtr; _mem.Write(parent, head); return(Result.Ok(newChildPtr)); }