using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace Capnp { /// /// ListDeserializer specialization for unmanaged primitive types (including enum). /// /// List element type public class ListOfPrimitivesDeserializer: ListDeserializer, IReadOnlyList where T: unmanaged { class ListOfULongAsStructView : IReadOnlyList { readonly ListOfPrimitivesDeserializer _lpd; readonly Func _sel; public ListOfULongAsStructView(ListOfPrimitivesDeserializer lpd, Func sel) { _lpd = lpd; _sel = sel; } public U this[int index] { get { var state = _lpd.State; if (index < 0 || index >= _lpd.Count) throw new IndexOutOfRangeException(); state.Offset += index; state.Kind = ObjectKind.Struct; state.StructDataCount = 1; state.StructPtrCount = 0; return _sel(state); } } public int Count => _lpd.Count; IEnumerable Enumerate() { var state = _lpd.State; state.Kind = ObjectKind.Struct; state.StructDataCount = 1; state.StructPtrCount = 0; for (int i = 0; i < Count; i++) { yield return _sel(state); ++state.Offset; } } public IEnumerator GetEnumerator() { return Enumerate().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } readonly ListKind _kind; internal ListOfPrimitivesDeserializer(in DeserializerState state, ListKind kind) : base(state) { _kind = kind; } /// /// One of ListOfBytes, ListOfShorts, ListOfInts, ListOfLongs. /// public override ListKind Kind => _kind; /// /// Retrieves the underlying memory span of this object /// public ReadOnlySpan Span => MemoryMarshal.Cast(State.CurrentSegment.Slice(State.Offset)).Slice(0, Count); /// /// Returns the element at given index. /// /// Element index /// Element value /// is out of range. public T this[int index] => Span[index]; ListOfPrimitivesDeserializer PrimitiveCast() where U: unmanaged { if (Marshal.SizeOf() != Marshal.SizeOf()) throw new NotSupportedException("Source and target types have different sizes, cannot cast"); return new ListOfPrimitivesDeserializer(State, Kind); } /// /// Always throws because this specialization can never represent a List(bool). /// public override IReadOnlyList CastBool() => throw new NotSupportedException("Cannot cast to list of bits"); /// /// Attempts to interpret this instance as List(UInt8). /// /// The desired representation /// Element size is different from 1 byte. public override IReadOnlyList CastByte() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Int8). /// /// The desired representation /// Element size is different from 1 byte. public override IReadOnlyList CastSByte() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(UInt16). /// /// The desired representation /// Element size is different from 2 bytes. public override IReadOnlyList CastUShort() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Int16). /// /// The desired representation /// Element size is different from 2 bytes. public override IReadOnlyList CastShort() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(UInt32). /// /// The desired representation /// Element size is different from 4 bytes. public override IReadOnlyList CastUInt() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Int32). /// /// The desired representation /// Element size is different from 4 bytes. public override IReadOnlyList CastInt() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(UInt64). /// /// The desired representation /// Element size is different from 8 bytes. public override IReadOnlyList CastULong() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Int64). /// /// The desired representation /// Element size is different from 8 bytes. public override IReadOnlyList CastLong() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Float32). /// /// The desired representation /// Element size is different from 4 bytes. public override IReadOnlyList CastFloat() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(Float64). /// /// The desired representation /// Element size is different from 8 bytes. public override IReadOnlyList CastDouble() => PrimitiveCast(); /// /// Attempts to interpret this instance as List(U) whereby U is a struct, applying a selector function to each element. /// /// Selector function /// The desired representation public override IReadOnlyList Cast(Func cons) { switch (Marshal.SizeOf()) { case 1: return PrimitiveCast().LazyListSelect(x => cons(DeserializerState.MakeValueState(x))); case 2: return PrimitiveCast().LazyListSelect(x => cons(DeserializerState.MakeValueState(x))); case 4: return PrimitiveCast().LazyListSelect(x => cons(DeserializerState.MakeValueState(x))); case 8: return new ListOfULongAsStructView(PrimitiveCast(), cons); default: throw new InvalidProgramException("This program path should not be reachable"); } } /// /// Attempts to interpret this instance as Text and returns the string representation. /// /// The decoded string /// Element size is different from 1 byte. public override string CastText() { var utf8Bytes = PrimitiveCast().Span; if (utf8Bytes.Length == 0) return string.Empty; var utf8GytesNoZterm = utf8Bytes.Slice(0, utf8Bytes.Length - 1); return Encoding.UTF8.GetString(utf8GytesNoZterm.ToArray()); } IEnumerable Enumerate() { for (int i = 0; i < Count; i++) yield return this[i]; } /// /// Implements . /// /// public IEnumerator GetEnumerator() { return Enumerate().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }