public NativeSlice <byte> Receive(NetworkPipelineContext ctx, NativeSlice <byte> inboundBuffer, ref bool needsResume, ref bool needsUpdate, ref bool needsSendUpdate)
        {
            needsResume = false;
            // Request a send update to see if a queued packet needs to be resent later or if an ack packet should be sent
            needsSendUpdate = true;

            var context = default(DataStreamReader.Context);
            var header  = default(ReliableUtility.PacketHeader);
            var slice   = default(NativeSlice <byte>);

            unsafe
            {
                ReliableUtility.Context *      reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer.GetUnsafePtr();
                ReliableUtility.SharedContext *shared   = (ReliableUtility.SharedContext *)ctx.internalSharedProcessBuffer.GetUnsafePtr();
                shared->errorCode = 0;
                if (reliable->Resume == ReliableUtility.NullEntry)
                {
                    if (inboundBuffer.Length <= 0)
                    {
                        return(slice);
                    }
                    var reader = new DataStreamReader(inboundBuffer);
                    reader.ReadBytes(ref context, (byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>());

                    if (header.Type == (ushort)ReliableUtility.PacketType.Ack)
                    {
                        ReliableUtility.ReadAckPacket(ctx, header);
                        inboundBuffer = new NativeSlice <byte>();
                        return(inboundBuffer);
                    }

                    var result = ReliableUtility.Read(ctx, header);

                    if (result >= 0)
                    {
                        var nextExpectedSequenceId = (ushort)(reliable->Delivered + 1);
                        if (result == nextExpectedSequenceId)
                        {
                            reliable->Delivered = result;
                            slice = inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>());

                            if (needsResume = SequenceHelpers.GreaterThan16((ushort)shared->ReceivedPackets.Sequence,
                                                                            (ushort)result))
                            {
                                reliable->Resume = (ushort)(result + 1);
                            }
                        }
                        else
                        {
                            ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()));
                            slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume);
                        }
                    }
                }
                else
                {
                    slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume);
                }
            }
            return(slice);
        }
        private static void Receive(ref NetworkPipelineContext ctx, ref InboundRecvBuffer inboundBuffer, ref NetworkPipelineStage.Requests requests)
        {
            // Request a send update to see if a queued packet needs to be resent later or if an ack packet should be sent
            requests = NetworkPipelineStage.Requests.SendUpdate;
            bool needsResume = false;

            var header = default(ReliableUtility.PacketHeader);
            var slice  = default(InboundRecvBuffer);

            ReliableUtility.Context *      reliable = (ReliableUtility.Context *)ctx.internalProcessBuffer;
            ReliableUtility.SharedContext *shared   = (ReliableUtility.SharedContext *)ctx.internalSharedProcessBuffer;
            shared->errorCode = 0;
            if (reliable->Resume == ReliableUtility.NullEntry)
            {
                if (inboundBuffer.bufferLength <= 0)
                {
                    inboundBuffer = slice;
                    return;
                }
                var inboundArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray <byte>(inboundBuffer.buffer, inboundBuffer.bufferLength, Allocator.Invalid);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                var safetyHandle = AtomicSafetyHandle.GetTempMemoryHandle();
                NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref inboundArray, safetyHandle);
#endif
                var reader = new DataStreamReader(inboundArray);
                reader.ReadBytes((byte *)&header, UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>());

                if (header.Type == (ushort)ReliableUtility.PacketType.Ack)
                {
                    ReliableUtility.ReadAckPacket(ctx, header);
                    inboundBuffer = default;
                    return;
                }

                var result = ReliableUtility.Read(ctx, header);

                if (result >= 0)
                {
                    var nextExpectedSequenceId = (ushort)(reliable->Delivered + 1);
                    if (result == nextExpectedSequenceId)
                    {
                        reliable->Delivered = result;
                        slice = inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>());

                        if (needsResume = SequenceHelpers.GreaterThan16((ushort)shared->ReceivedPackets.Sequence,
                                                                        (ushort)result))
                        {
                            reliable->Resume = (ushort)(result + 1);
                        }
                    }
                    else
                    {
                        ReliableUtility.SetPacket(ctx.internalProcessBuffer, result, inboundBuffer.Slice(UnsafeUtility.SizeOf <ReliableUtility.PacketHeader>()));
                        slice = ReliableUtility.ResumeReceive(ctx, reliable->Delivered + 1, ref needsResume);
                    }
                }
            }
            else
            {
                slice = ReliableUtility.ResumeReceive(ctx, reliable->Resume, ref needsResume);
            }
            if (needsResume)
            {
                requests |= NetworkPipelineStage.Requests.Resume;
            }
            inboundBuffer = slice;
        }