using System; using System.Collections.Generic; namespace Capnp { /// <summary> /// Base class for interpreting a <see cref="DeserializerState"/> as List(T). /// </summary> public abstract class ListDeserializer { static class GenericCasts<T> { public static Func<ListDeserializer, T> CastFunc; } static ListDeserializer() { GenericCasts<IReadOnlyList<bool>>.CastFunc = _ => _.CastBool(); GenericCasts<IReadOnlyList<sbyte>>.CastFunc = _ => _.CastSByte(); GenericCasts<IReadOnlyList<byte>>.CastFunc = _ => _.CastByte(); GenericCasts<IReadOnlyList<short>>.CastFunc = _ => _.CastShort(); GenericCasts<IReadOnlyList<ushort>>.CastFunc = _ => _.CastUShort(); GenericCasts<IReadOnlyList<int>>.CastFunc = _ => _.CastInt(); GenericCasts<IReadOnlyList<uint>>.CastFunc = _ => _.CastUInt(); GenericCasts<IReadOnlyList<long>>.CastFunc = _ => _.CastLong(); GenericCasts<IReadOnlyList<ulong>>.CastFunc = _ => _.CastULong(); GenericCasts<IReadOnlyList<float>>.CastFunc = _ => _.CastFloat(); GenericCasts<IReadOnlyList<double>>.CastFunc = _ => _.CastDouble(); GenericCasts<string>.CastFunc = _ => _.CastText(); } /// <summary> /// Underlying deserializer state /// </summary> protected readonly DeserializerState State; internal ListDeserializer(ref DeserializerState state) { State = state; } internal ListDeserializer() { } T Cast<T>() { var func = GenericCasts<T>.CastFunc; if (func == null) throw new NotSupportedException("Requested cast is not supported"); return func(this); } /// <summary> /// This list's element count /// </summary> public int Count => State.ListElementCount; /// <summary> /// The list's element category /// </summary> public abstract ListKind Kind { get; } /// <summary> /// Represents this list by applying a selector function to each element's deserializer state. /// This operator is only supported by certain specializations. /// </summary> /// <typeparam name="T">Target element type</typeparam> /// <param name="cons">Selector function</param> /// <returns>The desired representation</returns> public abstract IReadOnlyList<T> Cast<T>(Func<DeserializerState, T> cons); /// <summary> /// Represents this list as a list of lists. /// </summary> /// <returns>The list of lists representation, each element being a <see cref="ListDeserializer"/> on its own.</returns> /// <exception cref="NotSupportedException">If this kind of list cannot be represented as list of lists (because it is a list of non-pointers)</exception> public virtual IReadOnlyList<ListDeserializer> CastList() { throw new NotSupportedException("This kind of list does not contain nested lists"); } /// <summary> /// Represents this list as a list of capabilities. /// </summary> /// <typeparam name="T">Capability interface</typeparam> /// <returns>Capability list representation</returns> /// <exception cref="NotSupportedException">If this kind of list cannot be represented as list of capabilities (because it is a list of non-pointers)</exception> /// <exception cref="Rpc.InvalidCapabilityInterfaceException">If <typeparamref name="T"/> does not qualify as capability interface.</exception> public virtual IReadOnlyList<ListOfCapsDeserializer<T>> CastCapList<T>() where T: class { throw new NotSupportedException("This kind of list does not contain nested lists"); } object CastND(int n, Func<ListDeserializer, object> func) { if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n)); for (int i = 1; i < n; i++) { var copy = func; // This copy assignment is intentional. Try to optimize it away and be amazed! func = ld => ld.CastList().LazyListSelect(copy); } return func(this); } /// <summary> /// Represents this list as n-dimensional list of T, with T being a primitive type. /// </summary> /// <typeparam name="T">Element type, must be primitive</typeparam> /// <param name="n">Number of dimensions</param> /// <returns>The desired representation as <![CDATA[IReadOnlyList<...IReadOnlyList<T>>]]></returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="n"/> is less than or equal to 0</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public object CastND<T>(int n) => CastND(n, ld => ld.Cast<IReadOnlyList<T>>()); /// <summary> /// Represents this list as n-dimensional list of T, with T being any type. /// </summary> /// <typeparam name="T">Element type</typeparam> /// <param name="n">Number of dimensions</param> /// <param name="cons">Selector function which constructs an instance of <typeparamref name="T"/> from a <see cref="DeserializerState"/></param> /// <returns>The desired representation as <![CDATA[IReadOnlyList<...IReadOnlyList<T>>]]></returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="n"/> is less than or equals 0.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public object CastND<T>(int n, Func<DeserializerState, T> cons) => CastND(n, ld => ld.Cast(cons)); /// <summary> /// Represents this list as n-dimensional list of enums. /// </summary> /// <typeparam name="T">Enum type</typeparam> /// <param name="n">Number of dimensions</param> /// <param name="cons">Cast function which converts ushort value to enum value</param> /// <returns>The desired representation as <![CDATA[IReadOnlyList<...IReadOnlyList<T>>]]></returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="n"/> is less than or equals 0.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public object CastEnumsND<T>(int n, Func<ushort, T> cons) => CastND(n, ld => ld.CastEnums(cons)); /// <summary> /// Represents this list as n-dimensional List(...List(Void)) /// </summary> /// <param name="n">Number of dimensions</param> /// <returns>The desired representation as <![CDATA[IReadOnlyList<...IReadOnlyList<T>>]]></returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="n"/> is less than or equals 0</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public object CastVoidND(int n) => CastND(n, (ListDeserializer ld) => ld.Count); /// <summary> /// Represents this list as "matrix" (jagged array) with primitive element type. /// </summary> /// <typeparam name="T">Element type, must be primitive</typeparam> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<T>> Cast2D<T>() { return CastList().LazyListSelect(ld => ld.Cast<IReadOnlyList<T>>()); } /// <summary> /// Represents this list as List(Data). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<byte>> CastData() => Cast2D<byte>(); /// <summary> /// Represents this list as "matrix" (jagged array) with complex element type. /// </summary> /// <typeparam name="T">Element type</typeparam> /// <param name="cons">Selector function which constructs an instance of <typeparamref name="T"/> from a <see cref="DeserializerState"/></param> /// <returns>The desired representation</returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<T>> Cast2D<T>(Func<DeserializerState, T> cons) { return CastList().LazyListSelect(ld => ld.Cast(cons)); } /// <summary> /// Represents this list as "matrix" (jagged array) of enum-typed elements. /// </summary> /// <typeparam name="T">Enum type</typeparam> /// <param name="cons">Cast function which converts ushort value to enum value</param> /// <returns>The desired representation</returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<T>> CastEnums2D<T>(Func<ushort, T> cons) { return CastList().LazyListSelect(ld => ld.CastEnums(cons)); } /// <summary> /// Represents this list as 3-dimensional jagged array with primitive element type. /// </summary> /// <typeparam name="T">Element type, must be primitive</typeparam> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<IReadOnlyList<T>>> Cast3D<T>() { return CastList().LazyListSelect(ld => ld.Cast2D<T>()); } /// <summary> /// Represents this list as 3-dimensional jagged array with complex element type. /// </summary> /// <typeparam name="T">Element type</typeparam> /// <param name="cons">Selector function which constructs an instance of <typeparamref name="T"/> from a <see cref="DeserializerState"/></param> /// <returns>The desired representation</returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<IReadOnlyList<T>>> Cast3D<T>(Func<DeserializerState, T> cons) { return CastList().LazyListSelect(ld => ld.Cast2D(cons)); } /// <summary> /// Represents this list as 3-dimensional jagged array of enum-typed elements. /// </summary> /// <typeparam name="T">Enum type</typeparam> /// <param name="cons">Cast function which converts ushort value to enum value</param> /// <returns>The desired representation</returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<IReadOnlyList<T>>> CastEnums3D<T>(Func<ushort, T> cons) { return CastList().LazyListSelect(ld => ld.CastEnums2D(cons)); } /// <summary> /// Represents this list as list of enum-typed elements. /// </summary> /// <typeparam name="T">Enum type</typeparam> /// <param name="cons">Cast function which converts ushort value to enum value</param> /// <returns>The desired representation</returns> /// <exception cref="ArgumentNullException"><paramref name="cons"/> is null.</exception> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<T> CastEnums<T>(Func<ushort, T> cons) { return CastUShort().LazyListSelect(cons); } /// <summary> /// Represents this list as List(Void), which boils down to returning the number of elements. /// </summary> /// <returns>The List(Void) representation which is nothing but the list's element count.</returns> public int CastVoid() => Count; /// <summary> /// Represents this list as List(List(Void)), which boils down to returning a list of element counts. /// </summary> /// <returns>A list of integers whereby each number equals the sublist's element count.</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<int> CastVoid2D() => CastList().LazyListSelect(ld => ld.Count); /// <summary> /// Represents this list as List(List(List(Void))). /// </summary> /// <returns>The List(List(List(Void))) representation which is in turn a 2-dimensional jagged array of element counts.</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<IReadOnlyList<int>> CastVoid3D() => CastList().LazyListSelect(ld => ld.CastVoid2D()); /// <summary> /// Represents this list as List(Text). For representing it as Text, use <seealso cref="CastText"/>. /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public IReadOnlyList<string> CastText2() => CastList().LazyListSelect(ld => ld.CastText()); /// <summary> /// Represents this list as Text. For representing it as List(Text), use <seealso cref="CastText2"/>. /// </summary> /// <remarks> /// Did you notice that the naming pattern is broken here? For every other CastX method, X depicts the element type. /// CastX actually means "represent this list as list of X". Logically, the semantics of CastText should be the semantics /// implemented by CastText2. And this method's name should be "CastChar". This wouldn't be accurate either, since a string /// is semantically more than the list of its characters. Trying to figure out a consistent naming pattern, we'd probably /// end up in less concise method names (do you have a good suggestion?). Considering this and the fact that you probably /// won't use these methods directly (because the code generator will produce nice wrappers for you) it seems acceptable to /// live with the asymmetric and somewhat ugly naming. /// </remarks> /// <returns>The decoded text</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual string CastText() { throw new NotSupportedException("This kind of list does not represent text"); } /// <summary> /// Represents this list as List(Bool). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<bool> CastBool() { return Cast(sd => sd.ReadDataBool(0)); } /// <summary> /// Represents this list as List(Int8). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<sbyte> CastSByte() { return Cast(sd => sd.ReadDataSByte(0)); } /// <summary> /// Represents this list as List(UInt8). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<byte> CastByte() { return Cast(sd => sd.ReadDataByte(0)); } /// <summary> /// Represents this list as List(Int16). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<short> CastShort() { return Cast(sd => sd.ReadDataShort(0)); } /// <summary> /// Represents this list as List(UInt16). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<ushort> CastUShort() { return Cast(sd => sd.ReadDataUShort(0)); } /// <summary> /// Represents this list as List(Int32). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<int> CastInt() { return Cast(sd => sd.ReadDataInt(0)); } /// <summary> /// Represents this list as List(UInt32). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<uint> CastUInt() { return Cast(sd => sd.ReadDataUInt(0)); } /// <summary> /// Represents this list as List(Int64). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<long> CastLong() { return Cast(sd => sd.ReadDataLong(0)); } /// <summary> /// Represents this list as List(UInt64). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<ulong> CastULong() { return Cast(sd => sd.ReadDataULong(0)); } /// <summary> /// Represents this list as List(Float32). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<float> CastFloat() { return Cast(sd => sd.ReadDataFloat(0)); } /// <summary> /// Represents this list as List(Float64). /// </summary> /// <returns>The desired representation</returns> /// <exception cref="NotSupportedException">If this list cannot be represented in the desired manner.</exception> public virtual IReadOnlyList<double> CastDouble() { return Cast(sd => sd.ReadDataDouble(0)); } } }