public ValueTask <T> DeserializeAsync(CborReader reader, CancellationToken cancellationToken = default)
        {
            var result = new WrappedResult();
            var task   = Deserializer(reader, ref result, cancellationToken);

            if (task.IsCompletedSuccessfully && result.State == MaxSteps) // if everything is synchronous, we should not hit any async path at all
            {
                return(new ValueTask <T>(result.Result));
            }

            return(AwaitDeserializeAsync(task.AsTask(), reader, result, cancellationToken));
        }
        private async ValueTask <T> AwaitDeserializeAsync(Task task, CborReader reader, WrappedResult result, CancellationToken cancellationToken)
        {
            await task.ConfigureAwait(false);

            result.TempTask = task;
            while (result.State < MaxSteps)
            {
                // This is not really nice, as depending on the valuetasksource this allocates the task
                // The idea is, that only if the valuetask really needs awaiting the delegate will return and
                // then the valuetask wraps a task anyway.
                // This is not completely true, as custom valuetasksources might behave differently
                var vTask = Deserializer(reader, ref result, cancellationToken);
                if (vTask.IsCompletedSuccessfully && result.State == MaxSteps)
                {
                    return(result.Result);
                }

                task            = vTask.AsTask();
                result.TempTask = task;
            }

            return(result.Result);
        }