< Summary

Information
Class: ZeroC.Slice.Codec.SliceDecoderExtensions
Assembly: ZeroC.Slice.Codec
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/ZeroC.Slice.Codec/SliceDecoderExtensions.cs
Tag: 1321_24790053727
Line coverage
87%
Covered lines: 116
Uncovered lines: 16
Coverable lines: 132
Total lines: 275
Line coverage: 87.8%
Branch coverage
84%
Covered branches: 39
Total branches: 46
Branch coverage: 84.7%
Method coverage
100%
Covered methods: 9
Fully covered methods: 2
Total methods: 9
Method coverage: 100%
Full method coverage: 22.2%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CheckEndOfBuffer(...)100%22100%
DecodeDictionary(...)100%4490.47%
DecodeDictionaryWithOptionalValueType(...)83.33%6677.27%
DecodeResult(...)75%4483.33%
DecodeSequence(...)83.33%6690%
DecodeSequence(...)75%4485.71%
DecodeSequence(...)83.33%6685.71%
DecodeSequenceOfOptionals(...)83.33%6686.66%
DecodeSequenceOfOptionals(...)87.5%8886.66%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/ZeroC.Slice.Codec/SliceDecoderExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Runtime.CompilerServices;
 4using System.Runtime.InteropServices;
 5
 6namespace ZeroC.Slice.Codec;
 7
 8/// <summary>Provides extension methods for <see cref="SliceDecoder" /> to decode dictionaries, results, and sequences.
 9/// </summary>
 10public static class SliceDecoderExtensions
 11{
 12    /// <summary>Verifies the Slice decoder has reached the end of its underlying buffer.</summary>
 13    /// <param name="decoder">The Slice decoder.</param>
 14    public static void CheckEndOfBuffer(this ref SliceDecoder decoder)
 734915    {
 734916        if (!decoder.End)
 417        {
 418            throw new InvalidDataException($"There are {decoder.Remaining} bytes remaining in the buffer.");
 19        }
 734520    }
 21
 22    /// <summary>Decodes a dictionary.</summary>
 23    /// <typeparam name="TDictionary">The type of the returned dictionary.</typeparam>
 24    /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
 25    /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
 26    /// <param name="decoder">The Slice decoder.</param>
 27    /// <param name="dictionaryFactory">The factory for creating the dictionary instance.</param>
 28    /// <param name="keyDecodeFunc">The decode function for each key of the dictionary.</param>
 29    /// <param name="valueDecodeFunc">The decode function for each value of the dictionary.</param>
 30    /// <returns>The dictionary decoded by this decoder.</returns>
 31    public static TDictionary DecodeDictionary<TDictionary, TKey, TValue>(
 32        this ref SliceDecoder decoder,
 33        Func<int, TDictionary> dictionaryFactory,
 34        DecodeFunc<TKey> keyDecodeFunc,
 35        DecodeFunc<TValue> valueDecodeFunc)
 36        where TKey : notnull
 37        where TDictionary : IDictionary<TKey, TValue>
 98338    {
 98339        int count = decoder.DecodeSize();
 98140        if (count == 0)
 30941        {
 30942            return dictionaryFactory(0);
 43        }
 44        else
 67245        {
 67246            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TKey>() + Unsafe.SizeOf<TValue>());
 66747            TDictionary dictionary = dictionaryFactory(count);
 991848            for (int i = 0; i < count; ++i)
 429349            {
 429350                TKey key = keyDecodeFunc(ref decoder);
 429351                TValue value = valueDecodeFunc(ref decoder);
 52                try
 429353                {
 429354                    dictionary.Add(key, value);
 429255                }
 156                catch (ArgumentException exception)
 157                {
 158                    throw new InvalidDataException($"Received dictionary with duplicate key '{key}'.", exception);
 59                }
 429260            }
 66661            return dictionary;
 62        }
 97563    }
 64
 65    /// <summary>Decodes a dictionary with an optional value type (T? in Slice).</summary>
 66    /// <typeparam name="TDictionary">The type of the returned dictionary.</typeparam>
 67    /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
 68    /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
 69    /// <param name="decoder">The Slice decoder.</param>
 70    /// <param name="dictionaryFactory">The factory for creating the dictionary instance.</param>
 71    /// <param name="keyDecodeFunc">The decode function for each key of the dictionary.</param>
 72    /// <param name="valueDecodeFunc">The decode function for each non-null value of the dictionary.</param>
 73    /// <returns>The dictionary decoded by this decoder.</returns>
 74    public static TDictionary DecodeDictionaryWithOptionalValueType<TDictionary, TKey, TValue>(
 75        this ref SliceDecoder decoder,
 76        Func<int, TDictionary> dictionaryFactory,
 77        DecodeFunc<TKey> keyDecodeFunc,
 78        DecodeFunc<TValue?> valueDecodeFunc)
 79        where TKey : notnull
 80        where TDictionary : IDictionary<TKey, TValue?>
 1781    {
 1782        int count = decoder.DecodeSize();
 1783        if (count == 0)
 084        {
 085            return dictionaryFactory(0);
 86        }
 87        else
 1788        {
 1789            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TKey>() + Unsafe.SizeOf<TValue?>());
 1690            TDictionary dictionary = dictionaryFactory(count);
 420891            for (int i = 0; i < count; ++i)
 208892            {
 93                // Each entry is encoded like a:
 94                // compact struct Pair
 95                // {
 96                //     key: Key,
 97                //     value: Value?
 98                // }
 208899                bool hasValue = decoder.DecodeBool(); // simplified bit sequence
 2088100                TKey key = keyDecodeFunc(ref decoder);
 2088101                TValue? value = hasValue ? valueDecodeFunc(ref decoder) : default;
 102                try
 2088103                {
 2088104                    dictionary.Add(key, value);
 2088105                }
 0106                catch (ArgumentException exception)
 0107                {
 0108                    throw new InvalidDataException($"Received dictionary with duplicate key '{key}'.", exception);
 109                }
 2088110            }
 16111            return dictionary;
 112        }
 16113    }
 114
 115    /// <summary>Decodes a result.</summary>
 116    /// <typeparam name="TSuccess">The type of the success value.</typeparam>
 117    /// <typeparam name="TFailure">The type of the failure value.</typeparam>
 118    /// <param name="decoder">The Slice decoder.</param>
 119    /// <param name="successDecodeFunc">The decode function for the success type.</param>
 120    /// <param name="failureDecodeFunc">The decode function for the failure type.</param>
 121    /// <returns>The decoded result.</returns>
 122    public static Result<TSuccess, TFailure> DecodeResult<TSuccess, TFailure>(
 123        this ref SliceDecoder decoder,
 124        DecodeFunc<TSuccess> successDecodeFunc,
 125        DecodeFunc<TFailure> failureDecodeFunc) =>
 8126        decoder.DecodeVarInt32() switch
 8127        {
 3128            0 => new Result<TSuccess, TFailure>.Success(successDecodeFunc(ref decoder)),
 5129            1 => new Result<TSuccess, TFailure>.Failure(failureDecodeFunc(ref decoder)),
 0130            int value => throw new InvalidDataException($"Received invalid discriminant value '{value}' for Result.")
 8131        };
 132
 133    /// <summary>Decodes a sequence of fixed-size numeric values.</summary>
 134    /// <typeparam name="T">The sequence element type.</typeparam>
 135    /// <param name="decoder">The Slice decoder.</param>
 136    /// <param name="checkElement">A delegate used to check each element of the array (optional).</param>
 137    /// <returns>An array of T.</returns>
 138    public static T[] DecodeSequence<T>(this ref SliceDecoder decoder, Action<T>? checkElement = null)
 139        where T : struct
 3066140    {
 3066141        int count = decoder.DecodeSize();
 3066142        if (count == 0)
 0143        {
 0144            return Array.Empty<T>();
 145        }
 146        else
 3066147        {
 3066148            int elementSize = Unsafe.SizeOf<T>();
 3066149            decoder.IncreaseCollectionAllocation(count, elementSize);
 3066150            var value = new T[count];
 3066151            Span<byte> destination = MemoryMarshal.Cast<T, byte>(value.AsSpan());
 3066152            decoder.CopyTo(destination);
 153
 3066154            if (checkElement is not null)
 3155            {
 28156                foreach (T e in value)
 10157                {
 10158                    checkElement(e);
 9159                }
 2160            }
 3065161            return value;
 162        }
 3065163    }
 164
 165    /// <summary>Decodes a sequence.</summary>
 166    /// <typeparam name="T">The type of the elements in the array.</typeparam>
 167    /// <param name="decoder">The Slice decoder.</param>
 168    /// <param name="decodeFunc">The decode function for each element of the sequence.</param>
 169    /// <returns>An array of T.</returns>
 170    public static T[] DecodeSequence<T>(this ref SliceDecoder decoder, DecodeFunc<T> decodeFunc) where T : notnull
 32171    {
 32172        int count = decoder.DecodeSize();
 31173        if (count == 0)
 0174        {
 0175            return Array.Empty<T>();
 176        }
 177        else
 31178        {
 31179            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<T>());
 27180            var array = new T[count];
 1500181            for (int i = 0; i < count; ++i)
 723182            {
 723183                array[i] = decodeFunc(ref decoder);
 723184            }
 27185            return array;
 186        }
 27187    }
 188
 189    /// <summary>Decodes a sequence.</summary>
 190    /// <typeparam name="TSequence">The type of the returned sequence.</typeparam>
 191    /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam>
 192    /// <param name="decoder">The Slice decoder.</param>
 193    /// <param name="sequenceFactory">The factory for creating the sequence instance.</param>
 194    /// <param name="decodeFunc">The decode function for each element of the sequence.</param>
 195    /// <returns>A TSequence.</returns>
 196    public static TSequence DecodeSequence<TSequence, TElement>(
 197        this ref SliceDecoder decoder,
 198        Func<int, TSequence> sequenceFactory,
 199        DecodeFunc<TElement> decodeFunc) where TSequence : ICollection<TElement>
 200        where TElement : notnull
 8201    {
 8202        int count = decoder.DecodeSize();
 8203        if (count == 0)
 0204        {
 0205            return sequenceFactory(0);
 206        }
 207        else
 8208        {
 8209            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TElement>());
 8210            TSequence sequence = sequenceFactory(count);
 64211            for (int i = 0; i < count; ++i)
 24212            {
 24213                sequence.Add(decodeFunc(ref decoder));
 24214            }
 8215            return sequence;
 216        }
 8217    }
 218
 219    /// <summary>Decodes a sequence where the element type is an optional Slice type (T?).</summary>
 220    /// <typeparam name="T">The type of the elements in the array.</typeparam>
 221    /// <param name="decoder">The Slice decoder.</param>
 222    /// <param name="decodeFunc">The decode function for each non-null element of the sequence.</param>
 223    /// <returns>An array of T.</returns>
 224    /// <remarks>We return a T? and not a T to avoid ambiguities in the generated code with nullable reference
 225    /// types such as string?.</remarks>
 226    public static T?[] DecodeSequenceOfOptionals<T>(this ref SliceDecoder decoder, DecodeFunc<T> decodeFunc)
 23227    {
 23228        int count = decoder.DecodeSize();
 23229        if (count == 0)
 0230        {
 0231            return Array.Empty<T>();
 232        }
 233        else
 23234        {
 23235            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<T?>());
 19236            BitSequenceReader bitSequenceReader = decoder.GetBitSequenceReader(count);
 18237            var array = new T?[count];
 2488238            for (int i = 0; i < count; ++i)
 1226239            {
 1226240                array[i] = bitSequenceReader.Read() ? decodeFunc(ref decoder) : default;
 1226241            }
 18242            return array;
 243        }
 18244    }
 245
 246    /// <summary>Decodes a sequence where the element type is an optional Slice type (T?).</summary>
 247    /// <typeparam name="TSequence">The type of the returned sequence.</typeparam>
 248    /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam>
 249    /// <param name="decoder">The Slice decoder.</param>
 250    /// <param name="sequenceFactory">The factory for creating the sequence instance.</param>
 251    /// <param name="decodeFunc">The decode function for each non-null element of the sequence.</param>
 252    /// <returns>A TSequence.</returns>
 253    public static TSequence DecodeSequenceOfOptionals<TSequence, TElement>(
 254        this ref SliceDecoder decoder,
 255        Func<int, TSequence> sequenceFactory,
 256        DecodeFunc<TElement> decodeFunc) where TSequence : ICollection<TElement>
 1257    {
 1258        int count = decoder.DecodeSize();
 1259        if (count == 0)
 0260        {
 0261            return sequenceFactory(0);
 262        }
 263        else
 1264        {
 1265            decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TElement>());
 1266            BitSequenceReader bitSequenceReader = decoder.GetBitSequenceReader(count);
 1267            TSequence sequence = sequenceFactory(count);
 8268            for (int i = 0; i < count; ++i)
 3269            {
 3270                sequence.Add(bitSequenceReader.Read() ? decodeFunc(ref decoder) : default!);
 3271            }
 1272            return sequence;
 273        }
 1274    }
 275}