private static unsafe void SetBlocksFromData(
            LocalChunk destination, IndirectBlockPalette palette, ReadOnlySpan <ulong> blockStateData)
        {
            const uint blockLimit = 4096;

            uint bitsPerBlock  = (uint)blockStateData.Length * 64 / blockLimit;
            uint blocksPerLong = 64 / bitsPerBlock;
            uint bitsPerLong   = blocksPerLong * bitsPerBlock;
            uint mask          = ~(uint.MaxValue << (int)bitsPerBlock);

            const int    longBufferLength = 64;
            ulong *      longBuffer       = stackalloc ulong[longBufferLength];
            Span <ulong> longBufferSpan   = new(longBuffer, longBufferLength);

            const int   blockBufferLength = 512;
            uint *      blockBuffer       = stackalloc uint[blockBufferLength];
            Span <uint> blockBufferSpan   = new(blockBuffer, blockBufferLength);

            ReadOnlySpan <ulong> blockData = blockStateData;
            uint blockNumber = 0;
            int  longOffset  = 0;

            while (blockData.Length >= longBufferLength)
            {
                if (BitConverter.IsLittleEndian)
                {
                    ref ulong oSrc = ref MemoryMarshal.GetReference(blockData);
                    for (int i = 0; i < longBufferLength; i += 8)
                    {
                        ref ulong s = ref Unsafe.Add(ref oSrc, i);
                        ulong *   d = longBuffer + i;
                        d[0] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 0));
                        d[1] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 1));
                        d[2] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 2));
                        d[3] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 3));
                        d[4] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 4));
                        d[5] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 5));
                        d[6] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 6));
                        d[7] = BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref s, 7));
                    }
                }
 public BlockEnumerator(LocalChunk chunk) : this()
 {
     _chunk = chunk ?? throw new ArgumentNullException(nameof(chunk));
 }
        public async ValueTask <IChunk> GetOrAddChunk(ChunkColumnManager columnManager, ChunkPosition chunkPosition)
        {
            IChunkColumn column = await ColumnProvider.GetOrAddChunkColumn(columnManager, chunkPosition.Column).Unchain();

            if (column.TryGetChunk(chunkPosition.Y, out IChunk? loadedChunk))
            {
                return(loadedChunk);
            }

            if (column is not LocalChunkColumn localColumn)
            {
                throw new InvalidOperationException();
            }

            Dictionary <int, NbtElement>?chunksToDecode = localColumn._chunksToDecode;

            if (chunksToDecode != null)
            {
                NbtElement chunkElement;
                bool       hasElement = false;

                lock (chunksToDecode)
                {
                    hasElement = chunksToDecode.Remove(chunkPosition.Y, out chunkElement);
                }

                if (hasElement)
                {
                    // TODO: move to a Anvil chunk parser

                    LocalChunk chunk;

                    if (chunkElement.TryGetCompoundElement("BlockStates", out NbtElement blockStatesNbt) &&
                        chunkElement.TryGetCompoundElement("Palette", out NbtElement paletteNbt))
                    {
                        IndirectBlockPalette palette = ParsePalette(columnManager.GlobalBlockPalette, paletteNbt);
                        chunk = new LocalChunk(column, chunkPosition.Y, palette, columnManager.Air);

                        if (palette.Count == 1 &&
                            palette.BlockForId(0) == columnManager.Air)
                        {
                            chunk.FillBlock(columnManager.Air);
                        }
                        else
                        {
                            ReadOnlyMemory <byte> blockStateRawData = blockStatesNbt.GetArrayData(out NbtType blockStateDataType);
                            if (blockStateDataType != NbtType.LongArray)
                            {
                                throw new InvalidDataException();
                            }

                            SetBlocksFromData(chunk, palette, MemoryMarshal.Cast <byte, ulong>(blockStateRawData.Span));
                        }

                        if (chunkElement.TryGetCompoundElement("BlockLight", out NbtElement blockLightNbt))
                        {
                            ReadOnlyMemory <byte> blockLightData = blockLightNbt.GetArrayData(out NbtType blockLightDataType);
                            if (blockLightDataType != NbtType.ByteArray)
                            {
                                throw new InvalidDataException();
                            }

                            chunk.BlockLight = blockLightData.ToArray();
                        }

                        if (chunkElement.TryGetCompoundElement("SkyLight", out NbtElement skyLightNbt))
                        {
                            ReadOnlyMemory <byte> skyLightData = skyLightNbt.GetArrayData(out NbtType skyLightDataType);
                            if (skyLightDataType != NbtType.ByteArray)
                            {
                                throw new InvalidDataException();
                            }

                            chunk.SkyLight = skyLightData.ToArray();
                        }
                    }
                    else
                    {
                        chunk = new LocalChunk(column, chunkPosition.Y, columnManager.GlobalBlockPalette, columnManager.Air);
                        chunk.FillBlock(chunk.AirBlock);

                        chunk.SkyLight = new byte[2048];
                        chunk.SkyLight.AsSpan().Fill(255);
                    }

                    lock (chunksToDecode)
                    {
                        localColumn._chunksToDecodeRefCount--;
                        if (localColumn._chunksToDecodeRefCount == 0)
                        {
                            // TODO: fix
                            //localColumn._encodedColumn?.Dispose();
                            localColumn._encodedColumn = null;
                        }
                    }

                    return(chunk);
                }
            }

            return(await GenerateChunk(column, chunkPosition.Y).Unchain());
        }