mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 06:41:50 +01:00
233 lines
12 KiB
C#
233 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Capnp
|
|
{
|
|
/// <summary>
|
|
/// This SerializerState specialization provides functionality to build arbitrary Cap'n Proto objects without requiring the schema code generator.
|
|
/// </summary>
|
|
public class DynamicSerializerState : SerializerState
|
|
{
|
|
/// <summary>
|
|
/// Constructs an unbound instance.
|
|
/// </summary>
|
|
public DynamicSerializerState()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs an instance and binds it to the given <see cref="MessageBuilder"/>.
|
|
/// </summary>
|
|
/// <param name="messageBuilder">message builder</param>
|
|
public DynamicSerializerState(MessageBuilder messageBuilder):
|
|
base(messageBuilder)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs an instance, binds it to a dedicated message builder, and initializes the capability table for usage in RPC context.
|
|
/// </summary>
|
|
public static DynamicSerializerState CreateForRpc()
|
|
{
|
|
var mb = MessageBuilder.Create();
|
|
mb.InitCapTable();
|
|
return new DynamicSerializerState(mb);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts any <see cref="DeserializerState"/> to a DynamicSerializerState instance, which involves deep copying the object graph.
|
|
/// </summary>
|
|
/// <param name="state">The deserializer state to convert</param>
|
|
public static explicit operator DynamicSerializerState(DeserializerState state)
|
|
{
|
|
var mb = MessageBuilder.Create();
|
|
if (state.Caps != null)
|
|
{
|
|
mb.InitCapTable();
|
|
}
|
|
var sstate = mb.CreateObject<DynamicSerializerState>();
|
|
Reserializing.DeepCopy(state, sstate);
|
|
|
|
return sstate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Links a sub-item (struct field or list element) of this state to another state. Usually, this operation is not necessary, since objects are constructed top-down.
|
|
/// However, there might be some advanced scenarios where you want to reference the same object twice (also interesting for designing amplification attacks).
|
|
/// The Cap'n Proto serialization intrinsically supports this, since messages are object graphs, not trees.
|
|
/// </summary>
|
|
/// <param name="slot">If this state describes a struct: Index into this struct's pointer table.
|
|
/// If this state describes a list of pointers: List element index.</param>
|
|
/// <param name="target">state to be linked</param>
|
|
/// <param name="allowCopy">Whether to deep copy the target state if it belongs to a different message builder than this state.</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="slot"/> out of range</exception>
|
|
/// <exception cref="InvalidOperationException"><list type="bullet">
|
|
/// <item><description>This state does neither describe a struct, nor a list of pointers</description></item>
|
|
/// <item><description>Another state is already linked to the specified position (sorry, no overwrite allowed)</description></item>
|
|
/// <item><description>This state and <paramref name="target"/> belong to different message builder, and<paramref name="allowCopy"/> is false</description></item></list>
|
|
/// </exception>
|
|
public new void Link(int slot, SerializerState target, bool allowCopy = true) => base.Link(slot, target, allowCopy);
|
|
|
|
/// <summary>
|
|
/// Links a sub-item (struct field or list element) of this state to a capability.
|
|
/// </summary>
|
|
/// <param name="slot">If this state describes a struct: Index into this struct's pointer table.
|
|
/// If this state describes a list of pointers: List element index.</param>
|
|
/// <param name="capabilityIndex">capability index inside the capability table</param>
|
|
/// <exception cref="InvalidOperationException"><list type="bullet">
|
|
/// <item><description>This state does neither describe a struct, nor a list of pointers</description></item>
|
|
/// <item><description>Another state is already linked to the specified position (sorry, no overwrite allowed)</description></item></list>
|
|
/// </exception>
|
|
public new void LinkToCapability(int slot, uint? capabilityIndex) => base.LinkToCapability(slot, capabilityIndex);
|
|
|
|
/// <summary>
|
|
/// Determines the underlying object to be a struct.
|
|
/// </summary>
|
|
/// <param name="dataCount">Desired size of the struct's data section, in words</param>
|
|
/// <param name="ptrCount">Desired size of the struct's pointer section, in words</param>
|
|
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
|
public new void SetStruct(ushort dataCount, ushort ptrCount) => base.SetStruct(dataCount, ptrCount);
|
|
|
|
/// <summary>
|
|
/// Determines the underyling object to be a capability.
|
|
/// </summary>
|
|
/// <param name="capabilityIndex">Capability table index, or null to encode a null pointer</param>
|
|
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
|
public new void SetCapability(uint? capabilityIndex) => base.SetCapability(capabilityIndex);
|
|
|
|
/// <summary>
|
|
/// Determines the underlying object to be a list of (primitive) values.
|
|
/// </summary>
|
|
/// <param name="bitsPerElement">Element size in bits, must be 0 (void), 1 (bool), 8, 16, 32, or 64</param>
|
|
/// <param name="totalCount">Desired element count</param>
|
|
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="bitsPerElement"/> outside allowed range,
|
|
/// <paramref name="totalCount"/> negative or exceeding 2^29-1</exception>
|
|
public new void SetListOfValues(byte bitsPerElement, int totalCount) => base.SetListOfValues(bitsPerElement, totalCount);
|
|
|
|
/// <summary>
|
|
/// Determines the underlying object to be a list of pointers.
|
|
/// </summary>
|
|
/// <param name="totalCount">Desired element count</param>
|
|
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="totalCount"/> negative or exceeding 2^29-1</exception>
|
|
public new void SetListOfPointers(int totalCount) => base.SetListOfPointers(totalCount);
|
|
|
|
/// <summary>
|
|
/// Determines the underlying object to be a list of structs (fixed-width compound list).
|
|
/// </summary>
|
|
/// <param name="totalCount">Desired element count</param>
|
|
/// <param name="dataCount">Desired size of each struct's data section, in words</param>
|
|
/// <param name="ptrCount">Desired size of each struct's pointer section, in words</param>
|
|
/// <exception cref="InvalidOperationException">The object type was already set to something different</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="totalCount"/> negative, or total word count would exceed 2^29-1</exception>
|
|
public new void SetListOfStructs(int totalCount, ushort dataCount, ushort ptrCount) => base.SetListOfStructs(totalCount, dataCount, ptrCount);
|
|
|
|
/// <summary>
|
|
/// Constructs the underlying object from the given representation.
|
|
/// </summary>
|
|
/// <param name="obj">Object representation. Must be one of the following:
|
|
/// <list type="bullet">
|
|
/// <item><description>An instance implementing <see cref="ICapnpSerializable"/></description></item>
|
|
/// <item><description>null</description>, <see cref="String"/></item>
|
|
/// <item><description><c>IReadOnlyList<byte></c>, <c>IReadOnlyList<sbyte></c>, <c>IReadOnlyList<ushort></c>, <c>IReadOnlyList<short></c></description></item>
|
|
/// <item><description><c>IReadOnlyList<int></c>, <c>IReadOnlyList<uint></c>, <c>IReadOnlyList<long></c>, <c>IReadOnlyList<ulong></c></description></item>
|
|
/// <item><description><c>IReadOnlyList<float></c>, <c>IReadOnlyList<double></c>, <c>IReadOnlyList<bool></c>, <c>IReadOnlyList<string></c></description></item>
|
|
/// <item><description>Another <see cref="DeserializerState"/> or <see cref="SerializerState"/></description></item>
|
|
/// <item><description>Low-level capability object (<see cref="Rpc.ConsumedCapability"/>)</description></item>
|
|
/// <item><description>Proxy object (<see cref="Rpc.Proxy"/>)</description></item>
|
|
/// <item><description>Skeleton object (<see cref="Rpc.Skeleton"/>)</description></item>
|
|
/// <item><description>Capability interface implementation</description></item>
|
|
/// <item><description><c>IReadOnlyList<object></c>, whereby each list item is one of the things listed here.</description></item>
|
|
/// </list>
|
|
/// </param>
|
|
public void SetObject(object? obj)
|
|
{
|
|
void RewrapAndInheritBack<T>(Action<T> init) where T : SerializerState, new()
|
|
{
|
|
var r = Rewrap<T>();
|
|
init(r);
|
|
InheritFrom(r);
|
|
}
|
|
|
|
switch (obj)
|
|
{
|
|
case ICapnpSerializable serializable:
|
|
serializable.Serialize(this);
|
|
break;
|
|
|
|
case string s:
|
|
WriteText(s);
|
|
break;
|
|
|
|
case IReadOnlyList<byte> bytes:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<byte>>(_ => _.Init(bytes));
|
|
break;
|
|
|
|
case IReadOnlyList<sbyte> sbytes:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<sbyte>>(_ => _.Init(sbytes));
|
|
break;
|
|
|
|
case IReadOnlyList<ushort> ushorts:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<ushort>>(_ => _.Init(ushorts));
|
|
break;
|
|
|
|
case IReadOnlyList<short> shorts:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<short>>(_ => _.Init(shorts));
|
|
break;
|
|
|
|
case IReadOnlyList<uint> uints:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<uint>>(_ => _.Init(uints));
|
|
break;
|
|
|
|
case IReadOnlyList<int> ints:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<int>>(_ => _.Init(ints));
|
|
break;
|
|
|
|
case IReadOnlyList<ulong> ulongs:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<ulong>>(_ => _.Init(ulongs));
|
|
break;
|
|
|
|
case IReadOnlyList<long> longs:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<long>>(_ => _.Init(longs));
|
|
break;
|
|
|
|
case IReadOnlyList<float> floats:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<float>>(_ => _.Init(floats));
|
|
break;
|
|
|
|
case IReadOnlyList<double> doubles:
|
|
RewrapAndInheritBack<ListOfPrimitivesSerializer<double>>(_ => _.Init(doubles));
|
|
break;
|
|
|
|
case IReadOnlyList<bool> bools:
|
|
RewrapAndInheritBack<ListOfBitsSerializer>(_ => _.Init(bools));
|
|
break;
|
|
|
|
case IReadOnlyList<string> strings:
|
|
RewrapAndInheritBack<ListOfTextSerializer>(_ => _.Init(strings));
|
|
break;
|
|
|
|
case IReadOnlyList<object> objects:
|
|
RewrapAndInheritBack<ListOfPointersSerializer<DynamicSerializerState>>(_ => _.Init(objects, (s, o) => s.SetObject(o)));
|
|
break;
|
|
|
|
case DeserializerState ds:
|
|
Reserializing.DeepCopy(ds, this);
|
|
break;
|
|
|
|
case SerializerState s:
|
|
Reserializing.DeepCopy(s, this);
|
|
break;
|
|
|
|
case null:
|
|
break;
|
|
|
|
default:
|
|
SetCapability(ProvideCapability(obj));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} |