internal static byte[] CalculateNextChecksum(SpannerDataReader reader, byte[] currentChecksum, bool readResult)
        {
            int size = currentChecksum.Length;

            size += Protobuf.WellKnownTypes.Value.ForBool(readResult).CalculateSize();
            if (readResult)
            {
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    size += reader.GetJsonValue(i).CalculateSize();
                }
            }
            using var ms = new MemoryStream(size);
            ms.Write(currentChecksum, 0, currentChecksum.Length);
            using var cos = new CodedOutputStream(ms);
            Protobuf.WellKnownTypes.Value.ForBool(readResult).WriteTo(cos);
            if (readResult)
            {
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    reader.GetJsonValue(i).WriteTo(cos);
                }
            }
            // Flush the protobuf stream so everything is written to the memory stream.
            cos.Flush();
            // Then reset the memory stream to the start so the hash is calculated over all the bytes in the buffer.
            ms.Position = 0;
            SHA256 checksum = SHA256.Create();

            return(checksum.ComputeHash(ms));
        }
 internal SpannerDataReaderWithChecksum(
     SpannerRetriableTransaction transaction,
     SpannerDataReader spannerDataReader,
     SpannerCommand command)
 {
     Transaction        = transaction;
     _spannerDataReader = spannerDataReader;
     _spannerCommand    = (SpannerCommand)command.Clone();
 }
        async Task IRetriableStatement.RetryAsync(SpannerRetriableTransaction transaction, CancellationToken cancellationToken, int timeoutSeconds)
        {
            _spannerCommand.Transaction = transaction.SpannerTransaction;
            var reader = await _spannerCommand.ExecuteReaderAsync(cancellationToken);

            int  counter = 0;
            bool read    = true;

            byte[]           newChecksum  = new byte[0];
            SpannerException newException = null;

            while (read && counter < _numberOfReadCalls)
            {
                try
                {
                    read = await reader.ReadAsync(cancellationToken);

                    newChecksum = CalculateNextChecksum(reader, newChecksum, read);
                    counter++;
                }
                catch (SpannerException e) when(e.ErrorCode == ErrorCode.Aborted)
                {
                    // Propagate Aborted errors to trigger a new retry.
                    throw;
                }
                catch (SpannerException e)
                {
                    newException = e;
                    counter++;
                    break;
                }
            }
            if (counter == _numberOfReadCalls &&
                newChecksum.SequenceEqual(_currentChecksum) &&
                SpannerRetriableTransaction.SpannerExceptionsEqualForRetry(newException, _firstException))
            {
                // Checksum is ok, we only need to replace the delegate result set if it's still open.
                if (IsClosed)
                {
                    reader.Close();
                }
                else
                {
                    _spannerDataReader = reader;
                }
            }
            else
            {
                // The results are not equal, there is an actual concurrent modification, so we cannot
                // continue the transaction.
                throw new SpannerAbortedDueToConcurrentModificationException();
            }
        }