| | | 1 | | // Copyright (c) ZeroC, Inc. |
| | | 2 | | |
| | | 3 | | using System.Runtime.CompilerServices; |
| | | 4 | | using System.Runtime.InteropServices; |
| | | 5 | | |
| | | 6 | | namespace IceRpc.Ice.Codec; |
| | | 7 | | |
| | | 8 | | /// <summary>Provides extension methods for <see cref="IceDecoder" /> to decode sequences or dictionaries.</summary> |
| | | 9 | | public static class IceDecoderExtensions |
| | | 10 | | { |
| | | 11 | | /// <summary>Verifies the Ice decoder has reached the end of its underlying buffer.</summary> |
| | | 12 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 13 | | public static void CheckEndOfBuffer(this ref IceDecoder decoder) |
| | 4672 | 14 | | { |
| | 4672 | 15 | | if (!decoder.End) |
| | 1 | 16 | | { |
| | 1 | 17 | | throw new InvalidDataException($"There are {decoder.Remaining} bytes remaining in the buffer."); |
| | | 18 | | } |
| | 4671 | 19 | | } |
| | | 20 | | |
| | | 21 | | /// <summary>Decodes a dictionary.</summary> |
| | | 22 | | /// <typeparam name="TDictionary">The type of the returned dictionary.</typeparam> |
| | | 23 | | /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> |
| | | 24 | | /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> |
| | | 25 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 26 | | /// <param name="dictionaryFactory">The factory for creating the dictionary instance.</param> |
| | | 27 | | /// <param name="keyDecodeFunc">The decode function for each key of the dictionary.</param> |
| | | 28 | | /// <param name="valueDecodeFunc">The decode function for each value of the dictionary.</param> |
| | | 29 | | /// <returns>The dictionary decoded by this decoder.</returns> |
| | | 30 | | public static TDictionary DecodeDictionary<TDictionary, TKey, TValue>( |
| | | 31 | | this ref IceDecoder decoder, |
| | | 32 | | Func<int, TDictionary> dictionaryFactory, |
| | | 33 | | DecodeFunc<TKey> keyDecodeFunc, |
| | | 34 | | DecodeFunc<TValue> valueDecodeFunc) |
| | | 35 | | where TKey : notnull |
| | | 36 | | where TDictionary : IDictionary<TKey, TValue> |
| | 34 | 37 | | { |
| | 34 | 38 | | int count = decoder.DecodeSize(); |
| | 34 | 39 | | if (count == 0) |
| | 0 | 40 | | { |
| | 0 | 41 | | return dictionaryFactory(0); |
| | | 42 | | } |
| | | 43 | | else |
| | 34 | 44 | | { |
| | 34 | 45 | | decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TKey>() + Unsafe.SizeOf<TValue>()); |
| | 30 | 46 | | TDictionary dictionary = dictionaryFactory(count); |
| | 2548 | 47 | | for (int i = 0; i < count; ++i) |
| | 1245 | 48 | | { |
| | 1245 | 49 | | TKey key = keyDecodeFunc(ref decoder); |
| | 1245 | 50 | | TValue value = valueDecodeFunc(ref decoder); |
| | | 51 | | try |
| | 1245 | 52 | | { |
| | 1245 | 53 | | dictionary.Add(key, value); |
| | 1244 | 54 | | } |
| | 1 | 55 | | catch (ArgumentException exception) |
| | 1 | 56 | | { |
| | 1 | 57 | | throw new InvalidDataException($"Received dictionary with duplicate key '{key}'.", exception); |
| | | 58 | | } |
| | 1244 | 59 | | } |
| | 29 | 60 | | return dictionary; |
| | | 61 | | } |
| | 29 | 62 | | } |
| | | 63 | | |
| | | 64 | | /// <summary>Decodes a sequence of fixed-size numeric values.</summary> |
| | | 65 | | /// <typeparam name="T">The sequence element type.</typeparam> |
| | | 66 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 67 | | /// <param name="checkElement">A delegate used to check each element of the array (optional).</param> |
| | | 68 | | /// <returns>An array of T.</returns> |
| | | 69 | | public static T[] DecodeSequence<T>(this ref IceDecoder decoder, Action<T>? checkElement = null) |
| | | 70 | | where T : struct |
| | 21 | 71 | | { |
| | 21 | 72 | | int count = decoder.DecodeSize(); |
| | 21 | 73 | | if (count == 0) |
| | 0 | 74 | | { |
| | 0 | 75 | | return []; |
| | | 76 | | } |
| | | 77 | | else |
| | 21 | 78 | | { |
| | 21 | 79 | | int elementSize = Unsafe.SizeOf<T>(); |
| | 21 | 80 | | decoder.IncreaseCollectionAllocation(count, elementSize); |
| | 21 | 81 | | var value = new T[count]; |
| | 21 | 82 | | Span<byte> destination = MemoryMarshal.Cast<T, byte>(value.AsSpan()); |
| | 21 | 83 | | decoder.CopyTo(destination); |
| | | 84 | | |
| | 21 | 85 | | if (checkElement is not null) |
| | 3 | 86 | | { |
| | 36 | 87 | | foreach (T e in value) |
| | 14 | 88 | | { |
| | 14 | 89 | | checkElement(e); |
| | 13 | 90 | | } |
| | 2 | 91 | | } |
| | 20 | 92 | | return value; |
| | | 93 | | } |
| | 20 | 94 | | } |
| | | 95 | | |
| | | 96 | | /// <summary>Decodes a sequence.</summary> |
| | | 97 | | /// <typeparam name="T">The type of the elements in the array.</typeparam> |
| | | 98 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 99 | | /// <param name="decodeFunc">The decode function for each element of the sequence.</param> |
| | | 100 | | /// <returns>An array of T.</returns> |
| | | 101 | | public static T[] DecodeSequence<T>(this ref IceDecoder decoder, DecodeFunc<T> decodeFunc) |
| | 1485 | 102 | | { |
| | 1485 | 103 | | int count = decoder.DecodeSize(); |
| | 1485 | 104 | | if (count == 0) |
| | 1448 | 105 | | { |
| | 1448 | 106 | | return []; |
| | | 107 | | } |
| | | 108 | | else |
| | 37 | 109 | | { |
| | 37 | 110 | | decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<T>()); |
| | 33 | 111 | | var array = new T[count]; |
| | 1526 | 112 | | for (int i = 0; i < count; ++i) |
| | 730 | 113 | | { |
| | 730 | 114 | | array[i] = decodeFunc(ref decoder); |
| | 730 | 115 | | } |
| | 33 | 116 | | return array; |
| | | 117 | | } |
| | 1481 | 118 | | } |
| | | 119 | | |
| | | 120 | | /// <summary>Decodes an Ice sequence mapped to a custom collection.</summary> |
| | | 121 | | /// <typeparam name="TCollection">The type of the returned collection.</typeparam> |
| | | 122 | | /// <typeparam name="TElement">The type of the elements in the collection.</typeparam> |
| | | 123 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 124 | | /// <param name="collectionFactory">A delegate used to create the collection with the specified capacity.</param> |
| | | 125 | | /// <param name="addElement">A delegate used to add each decoded element to the collection.</param> |
| | | 126 | | /// <param name="decodeFunc">The decode function for each element of the collection.</param> |
| | | 127 | | /// <returns>The decoded collection.</returns> |
| | | 128 | | public static TCollection DecodeCollection<TCollection, TElement>( |
| | | 129 | | this ref IceDecoder decoder, |
| | | 130 | | Func<int, TCollection> collectionFactory, |
| | | 131 | | Action<TCollection, TElement> addElement, |
| | | 132 | | DecodeFunc<TElement> decodeFunc) |
| | 26 | 133 | | { |
| | 26 | 134 | | int count = decoder.DecodeSize(); |
| | 26 | 135 | | if (count == 0) |
| | 0 | 136 | | { |
| | 0 | 137 | | return collectionFactory(0); |
| | | 138 | | } |
| | | 139 | | else |
| | 26 | 140 | | { |
| | 26 | 141 | | decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<TElement>()); |
| | 26 | 142 | | TCollection collection = collectionFactory(count); |
| | 168 | 143 | | for (int i = 0; i < count; ++i) |
| | 58 | 144 | | { |
| | 58 | 145 | | addElement(collection, decodeFunc(ref decoder)); |
| | 58 | 146 | | } |
| | 26 | 147 | | return collection; |
| | | 148 | | } |
| | 26 | 149 | | } |
| | | 150 | | |
| | | 151 | | /// <summary>Decodes an Ice sequence mapped to a <see cref="LinkedList{T}" />.</summary> |
| | | 152 | | /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam> |
| | | 153 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 154 | | /// <param name="decodeFunc">The decode function for each element of the sequence.</param> |
| | | 155 | | /// <returns>A <see cref="LinkedList{T}" />.</returns> |
| | | 156 | | public static LinkedList<TElement> DecodeLinkedList<TElement>( |
| | | 157 | | this ref IceDecoder decoder, |
| | | 158 | | DecodeFunc<TElement> decodeFunc) => |
| | 4 | 159 | | decoder.DecodeCollection<LinkedList<TElement>, TElement>( |
| | 4 | 160 | | collectionFactory: _ => new LinkedList<TElement>(), |
| | 7 | 161 | | (list, element) => list.AddLast(element), |
| | 4 | 162 | | decodeFunc); |
| | | 163 | | |
| | | 164 | | /// <summary>Decodes an Ice sequence mapped to a <see cref="List{T}" />.</summary> |
| | | 165 | | /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam> |
| | | 166 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 167 | | /// <param name="decodeFunc">The decode function for each element of the sequence.</param> |
| | | 168 | | /// <returns>A <see cref="List{T}" />.</returns> |
| | | 169 | | public static List<TElement> DecodeList<TElement>( |
| | | 170 | | this ref IceDecoder decoder, |
| | | 171 | | DecodeFunc<TElement> decodeFunc) => |
| | 4 | 172 | | decoder.DecodeCollection<List<TElement>, TElement>( |
| | 4 | 173 | | collectionFactory: count => new List<TElement>(count), |
| | 7 | 174 | | (list, element) => list.Add(element), |
| | 4 | 175 | | decodeFunc); |
| | | 176 | | |
| | | 177 | | /// <summary>Decodes an Ice sequence mapped to a <see cref="Queue{T}" />.</summary> |
| | | 178 | | /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam> |
| | | 179 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 180 | | /// <param name="decodeFunc">The decode function for each element of the sequence.</param> |
| | | 181 | | /// <returns>A <see cref="Queue{T}" />.</returns> |
| | | 182 | | public static Queue<TElement> DecodeQueue<TElement>(this ref IceDecoder decoder, DecodeFunc<TElement> decodeFunc) => |
| | 4 | 183 | | decoder.DecodeCollection<Queue<TElement>, TElement>( |
| | 4 | 184 | | collectionFactory: count => new Queue<TElement>(count), |
| | 7 | 185 | | (queue, element) => queue.Enqueue(element), |
| | 4 | 186 | | decodeFunc); |
| | | 187 | | |
| | | 188 | | /// <summary>Decodes an Ice sequence mapped to a <see cref="Stack{T}" />.</summary> |
| | | 189 | | /// <typeparam name="TElement">The type of the elements in the sequence.</typeparam> |
| | | 190 | | /// <param name="decoder">The Ice decoder.</param> |
| | | 191 | | /// <param name="decodeFunc">The decode function for each element of the sequence.</param> |
| | | 192 | | /// <returns>A <see cref="Stack{T}" />.</returns> |
| | | 193 | | public static Stack<TElement> DecodeStack<TElement>(this ref IceDecoder decoder, DecodeFunc<TElement> decodeFunc) |
| | 6 | 194 | | { |
| | 6 | 195 | | var array = decoder.DecodeSequence(decodeFunc); |
| | 6 | 196 | | Array.Reverse(array); |
| | 6 | 197 | | return new Stack<TElement>(array); |
| | 6 | 198 | | } |
| | | 199 | | } |