public override unsafe void Free(void *pointer)
        {
            if (pointer == null)
            {
                throw new ArgumentException("pointer is null");
            }

            if (!IsOwner(pointer))
            {
                throw new ArgumentException("StackAllocator don't owns the given memory block.");
            }

            if (_buffer == null)
            {
                throw new InvalidOperationException("StackAllocator have been disposed");
            }

            byte *block = (byte *)pointer;
            StackAllocatorHeader *header = (StackAllocatorHeader *)(block - sizeof(StackAllocatorHeader));

            if (header != _prevOffset)
            {
                throw new InvalidOperationException($"Invalid memory block position.\n" +
                                                    $"Expected memory address: {ToHex(_prevOffset)} but {ToHex(header)} was get.\n" +
                                                    $"Any memory allocated within the StackAllocator must be free in a LIFO (Last-In First-Out) order.");
            }

            _offset     -= header->size;
            _prevOffset -= header->offset;
        }
        public override unsafe void *Allocate(int elementCount, int elementSize = 1, bool initMemory = true)
        {
            if (_buffer == null)
            {
                throw new InvalidOperationException("StackAllocator have been disposed");
            }

            if (elementCount <= 0)
            {
                throw new ArgumentException(elementCount.ToString(), nameof(elementCount));
            }

            if (elementSize <= 0)
            {
                throw new ArgumentException(elementSize.ToString(), nameof(elementSize));
            }

            int   blockSize  = elementCount * elementSize;
            int   size       = blockSize + sizeof(StackAllocatorHeader);
            byte *end        = _buffer + _length;
            byte *nextOffset = _offset + size;

            if (nextOffset > end)
            {
                ThrowOutOfMemory(blockSize);
            }

            StackAllocatorHeader *header = (StackAllocatorHeader *)_offset;

            header->size   = blockSize;
            header->offset = _prevOffset == null ? 0 : (int)(_prevOffset - _buffer);

            byte *block = _offset + sizeof(StackAllocatorHeader);

            if (initMemory)
            {
                Unsafe.InitBlockUnaligned(block, 0, (uint)blockSize);
            }

            _prevOffset = _offset;
            _offset    += size;
            return(block);
        }
        public override unsafe void *Reallocate(void *pointer, int elementCount, int elementSize = 1, bool initMemory = true)
        {
            if (_buffer == null)
            {
                throw new InvalidOperationException("StackAllocator have been disposed");
            }

            if (!IsOwner(pointer))
            {
                throw new ArgumentException("StackAllocator don't owns the given memory block.");
            }

            if (elementCount <= 0)
            {
                throw new ArgumentException(elementCount.ToString(), nameof(elementCount));
            }

            if (elementSize <= 0)
            {
                throw new ArgumentException(elementSize.ToString(), nameof(elementSize));
            }

            byte *block     = (byte *)pointer;
            int   blockSize = elementCount * elementSize;

            StackAllocatorHeader *header = (StackAllocatorHeader *)(block - sizeof(StackAllocatorHeader));

            if (header->size < blockSize)
            {
                return(pointer);
            }

            if (header == _prevOffset)
            {
                int   size       = blockSize + sizeof(StackAllocatorHeader);
                byte *end        = _buffer + _length;
                byte *nextOffset = _offset + size;

                if (nextOffset > end)
                {
                    ThrowOutOfMemory(blockSize);
                }

                int requiredSize = blockSize - header->size;

                if (initMemory)
                {
                    Unsafe.InitBlockUnaligned(_offset, 0, (uint)requiredSize);
                }

                header->size = size;
                _offset      = nextOffset;

                return(block);
            }
            else
            {
                throw new InvalidOperationException("Only the last allocated memory block can be reallocated.\n" +
                                                    $"Last memory block was {ToHex(_prevOffset)} but {ToHex(header)} was get.");
            }
        }