using System; using System.Collections.Generic; namespace Capnp { /// /// Base class for interpreting a as List(T). /// public abstract class ListDeserializer { static class GenericCasts { public static Func? CastFunc; public static Func GetCastFunc() => CastFunc ?? throw new NotSupportedException("Requested cast is not supported"); } static ListDeserializer() { GenericCasts>.CastFunc = _ => _.CastBool(); GenericCasts>.CastFunc = _ => _.CastSByte(); GenericCasts>.CastFunc = _ => _.CastByte(); GenericCasts>.CastFunc = _ => _.CastShort(); GenericCasts>.CastFunc = _ => _.CastUShort(); GenericCasts>.CastFunc = _ => _.CastInt(); GenericCasts>.CastFunc = _ => _.CastUInt(); GenericCasts>.CastFunc = _ => _.CastLong(); GenericCasts>.CastFunc = _ => _.CastULong(); GenericCasts>.CastFunc = _ => _.CastFloat(); GenericCasts>.CastFunc = _ => _.CastDouble(); GenericCasts.CastFunc = _ => _.CastText()!; // it *may* return null, but how to express this syntactically correct? } /// /// Underlying deserializer state /// protected readonly DeserializerState State; internal ListDeserializer(in DeserializerState state) { State = state; } internal ListDeserializer() { } T Cast() { return GenericCasts.GetCastFunc()(this); } /// /// This list's element count /// public int Count => State.ListElementCount; /// /// The list's element category /// public abstract ListKind Kind { get; } /// /// Represents this list by applying a selector function to each element's deserializer state. /// This operator is only supported by certain specializations. /// /// Target element type /// Selector function /// The desired representation public abstract IReadOnlyList Cast(Func cons); /// /// Represents this list as a list of lists. /// /// The list of lists representation, each element being a on its own. /// If this kind of list cannot be represented as list of lists (because it is a list of non-pointers) public virtual IReadOnlyList CastList() { throw new NotSupportedException("This kind of list does not contain nested lists"); } /// /// Represents this list as a list of capabilities. /// /// Capability interface /// Capability list representation /// If this kind of list cannot be represented as list of capabilities (because it is a list of non-pointers) /// If does not qualify as capability interface. public virtual IReadOnlyList CastCapList() where T: class { throw new NotSupportedException("This kind of list cannot be represented as list of capabilities"); } object CastND(int n, Func 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); } /// /// Represents this list as n-dimensional list of T, with T being a primitive type. /// /// Element type, must be primitive /// Number of dimensions /// The desired representation as >]]> /// is less than or equal to 0 /// If this list cannot be represented in the desired manner. public object CastND(int n) => CastND(n, ld => ld.Cast>()); /// /// Represents this list as n-dimensional list of T, with T being any type. /// /// Element type /// Number of dimensions /// Selector function which constructs an instance of from a /// The desired representation as >]]> /// is null. /// is less than or equals 0. /// If this list cannot be represented in the desired manner. public object CastND(int n, Func cons) => CastND(n, ld => ld.Cast(cons)); /// /// Represents this list as n-dimensional list of enums. /// /// Enum type /// Number of dimensions /// Cast function which converts ushort value to enum value /// The desired representation as >]]> /// is null. /// is less than or equals 0. /// If this list cannot be represented in the desired manner. public object CastEnumsND(int n, Func cons) => CastND(n, ld => ld.CastEnums(cons)); /// /// Represents this list as n-dimensional List(...List(Void)) /// /// Number of dimensions /// The desired representation as >]]> /// is less than or equals 0 /// If this list cannot be represented in the desired manner. public object CastVoidND(int n) => CastND(n, (ListDeserializer ld) => ld.Count); /// /// Represents this list as "matrix" (jagged array) with primitive element type. /// /// Element type, must be primitive /// The desired representation /// If this list cannot be represented in the desired manner. public IReadOnlyList> Cast2D() { GenericCasts>.GetCastFunc(); // Probe to avoid lazy NotSupportedException return CastList().LazyListSelect(ld => ld.Cast>()); } /// /// Represents this list as List(Data). /// /// The desired representation /// If this list cannot be represented in the desired manner. public IReadOnlyList> CastData() => Cast2D(); /// /// Represents this list as "matrix" (jagged array) with complex element type. /// /// Element type /// Selector function which constructs an instance of from a /// The desired representation /// is null. /// If this list cannot be represented in the desired manner. public IReadOnlyList> Cast2D(Func cons) { return CastList().LazyListSelect(ld => ld.Cast(cons)); } /// /// Represents this list as "matrix" (jagged array) of enum-typed elements. /// /// Enum type /// Cast function which converts ushort value to enum value /// The desired representation /// is null. /// If this list cannot be represented in the desired manner. public IReadOnlyList> CastEnums2D(Func cons) { return CastList().LazyListSelect(ld => ld.CastEnums(cons)); } /// /// Represents this list as 3-dimensional jagged array with primitive element type. /// /// Element type, must be primitive /// The desired representation /// If this list cannot be represented in the desired manner. public IReadOnlyList>> Cast3D() { return CastList().LazyListSelect(ld => ld.Cast2D()); } /// /// Represents this list as 3-dimensional jagged array with complex element type. /// /// Element type /// Selector function which constructs an instance of from a /// The desired representation /// is null. /// If this list cannot be represented in the desired manner. public IReadOnlyList>> Cast3D(Func cons) { return CastList().LazyListSelect(ld => ld.Cast2D(cons)); } /// /// Represents this list as 3-dimensional jagged array of enum-typed elements. /// /// Enum type /// Cast function which converts ushort value to enum value /// The desired representation /// is null. /// If this list cannot be represented in the desired manner. public IReadOnlyList>> CastEnums3D(Func cons) { return CastList().LazyListSelect(ld => ld.CastEnums2D(cons)); } /// /// Represents this list as list of enum-typed elements. /// /// Enum type /// Cast function which converts ushort value to enum value /// The desired representation /// is null. /// If this list cannot be represented in the desired manner. public IReadOnlyList CastEnums(Func cons) { return CastUShort().LazyListSelect(cons); } /// /// Represents this list as List(Void), which boils down to returning the number of elements. /// /// The List(Void) representation which is nothing but the list's element count. public int CastVoid() => Count; /// /// Represents this list as List(List(Void)), which boils down to returning a list of element counts. /// /// A list of integers whereby each number equals the sublist's element count. /// If this list cannot be represented in the desired manner. public IReadOnlyList CastVoid2D() => CastList().LazyListSelect(ld => ld.Count); /// /// Represents this list as List(List(List(Void))). /// /// The List(List(List(Void))) representation which is in turn a 2-dimensional jagged array of element counts. /// If this list cannot be represented in the desired manner. public IReadOnlyList> CastVoid3D() => CastList().LazyListSelect(ld => ld.CastVoid2D()); /// /// Represents this list as List(Text). For representing it as Text, use . /// /// The desired representation /// If this list cannot be represented in the desired manner. public IReadOnlyList CastText2() => CastList().LazyListSelect(ld => ld.CastText()); /// /// Represents this list as Text. For representing it as List(Text), use . /// /// /// 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. /// /// The decoded text /// If this list cannot be represented in the desired manner. public virtual string? CastText() { throw new NotSupportedException("This kind of list does not represent text"); } /// /// Represents this list as List(Bool). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastBool() { return Cast(sd => sd.ReadDataBool(0)); } /// /// Represents this list as List(Int8). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastSByte() { return Cast(sd => sd.ReadDataSByte(0)); } /// /// Represents this list as List(UInt8). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastByte() { return Cast(sd => sd.ReadDataByte(0)); } /// /// Represents this list as List(Int16). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastShort() { return Cast(sd => sd.ReadDataShort(0)); } /// /// Represents this list as List(UInt16). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastUShort() { return Cast(sd => sd.ReadDataUShort(0)); } /// /// Represents this list as List(Int32). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastInt() { return Cast(sd => sd.ReadDataInt(0)); } /// /// Represents this list as List(UInt32). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastUInt() { return Cast(sd => sd.ReadDataUInt(0)); } /// /// Represents this list as List(Int64). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastLong() { return Cast(sd => sd.ReadDataLong(0)); } /// /// Represents this list as List(UInt64). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastULong() { return Cast(sd => sd.ReadDataULong(0)); } /// /// Represents this list as List(Float32). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastFloat() { return Cast(sd => sd.ReadDataFloat(0)); } /// /// Represents this list as List(Float64). /// /// The desired representation /// If this list cannot be represented in the desired manner. public virtual IReadOnlyList CastDouble() { return Cast(sd => sd.ReadDataDouble(0)); } } }