From 9b3065d4ca6004c7b91d13bc3ea66ce7212b2e00 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:49 +0200 Subject: [PATCH 1/7] First test with the livekit ffi uniffi integration --- AGENTS.md | 6 + README.md | 8 + .../ffi-macos-arm64/liblivekit_ffi.dylib | 4 +- Runtime/Scripts/UniFFI.meta | 8 + Runtime/Scripts/UniFFI/livekit_ffi.cs | 1554 +++++++++++++++++ Runtime/Scripts/UniFFI/livekit_ffi.cs.meta | 11 + Samples~/Meet/Assets/Runtime/MeetManager.cs | 5 +- Samples~/Meet/Assets/Scenes/MeetApp.unity | 2 +- Scripts~/build_ffi_locally.sh | 71 +- Scripts~/downgrade_uniffi_bindings.py | 254 +++ Scripts~/uniffi.toml | 7 + client-sdk-rust~ | 2 +- 12 files changed, 1918 insertions(+), 14 deletions(-) create mode 100644 Runtime/Scripts/UniFFI.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_ffi.cs create mode 100644 Runtime/Scripts/UniFFI/livekit_ffi.cs.meta create mode 100755 Scripts~/downgrade_uniffi_bindings.py create mode 100644 Scripts~/uniffi.toml diff --git a/AGENTS.md b/AGENTS.md index 1acfc5e9..b2ca4763 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,10 +28,13 @@ Scripts~/build_ffi_locally.sh [build_type] - **Android**: requires `cargo-ndk` and Android NDK - **iOS**: builds static lib (`liblivekit_ffi.a`) - After macOS builds, Unity must be restarted to load the new dylib +- macOS builds also regenerate the UniFFI C# bindings in `Runtime/Scripts/UniFFI/` with `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of `livekit-ffi`), configured by `Scripts~/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types), and post-process them with `Scripts~/downgrade_uniffi_bindings.py` for C# 9. Runs from inside `client-sdk-rust~` because bindgen needs `cargo metadata`. ### Other scripts in `Scripts~/` - `download_libs.py` — downloads the prebuilt FFI binaries for all platforms; the release tag is pinned in `version.ini` - `generate_proto.sh` — regenerates `Runtime/Scripts/Proto/` from the protobuf definitions in `client-sdk-rust~/livekit-ffi/protocol` (requires `protoc`) +- `uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with the build script +- `downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present); idempotent, called by `build_ffi_locally.sh macos` - `build_docs.sh`, `prepare_release.py`, `unity_test_results_utils.py` — docs generation, release preparation, CI test-result parsing ### Run tests @@ -60,6 +63,7 @@ The SDK wraps a Rust native library (`liblivekit_ffi`) via P/Invoke. The communi - `TokenSource/` — token generation/fetching helpers and MonoBehaviour component - `UniTask/` — optional UniTask integration (own asmdef: `livekit.unity.Runtime.UniTask.asmdef`) - `Internal/`, `Proto/` — FFI plumbing and generated protobuf code +- `UniFFI/` — generated C# bindings for the UniFFI surface of `liblivekit_ffi` (namespace `LiveKit.Uniffi`; uniffi-bindgen-cs output, post-processed for C# 9) Key internal files: - `Internal/FFI/FFIClient.cs` — singleton managing request/response lifecycle with Rust via protobuf @@ -70,6 +74,8 @@ Key internal files: ### Proto/Generated Code `Runtime/Scripts/Proto/` contains auto-generated C# from protobuf definitions in the Rust SDK. Do not edit these files manually; regenerate with `Scripts~/generate_proto.sh`. +`Runtime/Scripts/UniFFI/` contains auto-generated C# from `uniffi-bindgen-cs`. Do not edit these files manually; regenerate with `Scripts~/build_ffi_locally.sh macos`, which also runs the C# 9 post-processing. + ### Native Plugins 10 platform/arch combinations in `Runtime/Plugins/ffi-{platform}-{arch}/`. These are large binary files tracked with Git LFS. The `.meta` files configure Unity platform targeting. diff --git a/README.md b/README.md index 284dee4c..d90be256 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,14 @@ So a build command is for example: `./Scripts~/build_ffi_locally.sh macos release` +### Generating UniFFI C# bindings + +macOS builds also regenerate the C# bindings for the UniFFI part of `liblivekit_ffi` into `Runtime/Scripts/UniFFI`. This requires [uniffi-bindgen-cs](https://github.com/NordSecurity/uniffi-bindgen-cs) at the version pinned in `Scripts~/build_ffi_locally.sh`, which must match the uniffi version used by `livekit-ffi`: + +`cargo install uniffi-bindgen-cs --git https://github.com/NordSecurity/uniffi-bindgen-cs --tag v0.11.0+v0.31.0` + +The generator is configured by `Scripts~/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types), which the build script passes with `--config`. uniffi-bindgen-cs emits C# 10+ syntax, so the build script post-processes the output with `Scripts~/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler. Run that script on `Runtime/Scripts/UniFFI` yourself if you generated the bindings by hand. + ### VSCode setup Look at the Unity-SDK.code-workspace setup for VSCode. This will use the Meet Sample as the Unity project and the Unity SDK package as two roots in a multi-root workspace and the Meet.sln as the `dotnet.defaultSolution`, enabling Rust and C# IDE support. diff --git a/Runtime/Plugins/ffi-macos-arm64/liblivekit_ffi.dylib b/Runtime/Plugins/ffi-macos-arm64/liblivekit_ffi.dylib index ec3fd1be..0258d4eb 100644 --- a/Runtime/Plugins/ffi-macos-arm64/liblivekit_ffi.dylib +++ b/Runtime/Plugins/ffi-macos-arm64/liblivekit_ffi.dylib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9072a445c981f3954b1857df41b2ac1ac8c7432626dd4158acb8c074b683d020 -size 17566144 +oid sha256:8717ab547f64e349c8bf2f9095d97ee1f5b2418e5c546f432e9f5cb632d7bdc4 +size 18269840 diff --git a/Runtime/Scripts/UniFFI.meta b/Runtime/Scripts/UniFFI.meta new file mode 100644 index 00000000..df67b8bf --- /dev/null +++ b/Runtime/Scripts/UniFFI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4900a47861ece41189ea630c2b5b522d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_ffi.cs b/Runtime/Scripts/UniFFI/livekit_ffi.cs new file mode 100644 index 00000000..734a023d --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_ffi.cs @@ -0,0 +1,1554 @@ +// +// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// Post-processed for C# 9 by Scripts~/downgrade_uniffi_bindings.py +// + +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LiveKit.Uniffi +{ + // This is a helper for safely working with byte buffers returned from the Rust code. + // A rust-owned buffer is represented by its capacity, its current length, and a + // pointer to the underlying data. + + [StructLayout(LayoutKind.Sequential)] + internal struct RustBuffer + { + public ulong capacity; + public ulong len; + public IntPtr data; + + public static RustBuffer Alloc(int size) + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + var buffer = _UniFFILib.ffi_livekit_ffi_rustbuffer_alloc( + Convert.ToUInt64(size), + ref status + ); + if (buffer.data == IntPtr.Zero) + { + throw new AllocationException( + $"RustBuffer.Alloc() returned null data pointer (size={size})" + ); + } + return buffer; + } + ); + } + + public static void Free(RustBuffer buffer) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_ffi_rustbuffer_free(buffer, ref status); + } + ); + } + + public static BigEndianStream MemoryStream(IntPtr data, long length) + { + unsafe + { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) + ); + } + } + + public BigEndianStream AsWriteableStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream( + (byte*)data.ToPointer(), + Convert.ToInt64(capacity), + Convert.ToInt64(capacity), + FileAccess.Write + ) + ); + } + } + } + + // This is a helper for safely passing byte references into the rust code. + // It's not actually used at the moment, because there aren't many things that you + // can take a direct pointer to managed memory, and if we're going to copy something + // then we might as well copy it into a `RustBuffer`. But it's here for API + // completeness. + + [StructLayout(LayoutKind.Sequential)] + internal struct ForeignBytes + { + public int length; + public IntPtr data; + } + + // The FfiConverter interface handles converter types to and from the FFI + // + // All implementing objects should be public to support external types. When a + // type is external we need to import it's FfiConverter. + internal abstract class FfiConverter + { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) + { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try + { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToUInt64(stream.Position); + return rbuf; + } + catch + { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) + { + var stream = rbuf.AsStream(); + try + { + var item = Read(stream); + if (stream.HasRemaining()) + { + throw new InternalException( + "junk remaining in buffer after lifting, something is very wrong!!" + ); + } + return item; + } + finally + { + RustBuffer.Free(rbuf); + } + } + } + + // FfiConverter that uses `RustBuffer` as the FfiType + internal abstract class FfiConverterRustBuffer : FfiConverter + { + public override CsType Lift(RustBuffer value) + { + return LiftFromRustBuffer(value); + } + + public override RustBuffer Lower(CsType value) + { + return LowerIntoRustBuffer(value); + } + } + + // A handful of classes and functions to support the generated data structures. + // This would be a good candidate for isolating in its own ffi-support lib. + // Error runtime. + [StructLayout(LayoutKind.Sequential)] + struct UniffiRustCallStatus + { + public sbyte code; + public RustBuffer error_buf; + + public bool IsSuccess() + { + return code == 0; + } + + public bool IsError() + { + return code == 1; + } + + public bool IsPanic() + { + return code == 2; + } + } + + // Base class for all uniffi exceptions + public class UniffiException : System.Exception + { + public UniffiException() + : base() { } + + public UniffiException(string message) + : base(message) { } + } + + public class UndeclaredErrorException : UniffiException + { + public UndeclaredErrorException(string message) + : base(message) { } + } + + public class PanicException : UniffiException + { + public PanicException(string message) + : base(message) { } + } + + public class AllocationException : UniffiException + { + public AllocationException(string message) + : base(message) { } + } + + public class InternalException : UniffiException + { + public InternalException(string message) + : base(message) { } + } + + public class InvalidEnumException : InternalException + { + public InvalidEnumException(string message) + : base(message) { } + } + + public class UniffiContractVersionException : UniffiException + { + public UniffiContractVersionException(string message) + : base(message) { } + } + + public class UniffiContractChecksumException : UniffiException + { + public UniffiContractChecksumException(string message) + : base(message) { } + } + + // Each top-level error class has a companion object that can lift the error from the call status's rust buffer + interface CallStatusErrorHandler + where E : System.Exception + { + E Lift(RustBuffer error_buf); + } + + // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + class NullCallStatusErrorHandler : CallStatusErrorHandler + { + public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); + + public UniffiException Lift(RustBuffer error_buf) + { + RustBuffer.Free(error_buf); + return new UndeclaredErrorException( + "library has returned an error not declared in UNIFFI interface file" + ); + } + } + + // Helpers for calling Rust + // In practice we usually need to be synchronized to call this safely, so it doesn't + // synchronize itself + class _UniffiHelpers + { + public delegate void RustCallAction(ref UniffiRustCallStatus status); + public delegate U RustCallFunc(ref UniffiRustCallStatus status); + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static U RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallFunc callback + ) + where E : System.Exception + { + var status = new UniffiRustCallStatus(); + var return_value = callback(ref status); + if (status.IsSuccess()) + { + return return_value; + } + else if (status.IsError()) + { + throw errorHandler.Lift(status.error_buf); + } + else if (status.IsPanic()) + { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) + { + throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); + } + else + { + throw new PanicException("Rust panic"); + } + } + else + { + throw new InternalException($"Unknown rust call status: {status.code}"); + } + } + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static void RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallAction callback + ) + where E : System.Exception + { + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + + // Call a rust function that returns a plain value + public static U RustCall(RustCallFunc callback) + { + return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); + } + + // Call a rust function that returns a plain value + public static void RustCall(RustCallAction callback) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + } + + static class FFIObjectUtil + { + public static void DisposeAll(params Object?[] list) + { + Dispose(list); + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(Object? obj) + { + if (obj == null) + { + return; + } + + if (obj is IDisposable disposable) + { + disposable.Dispose(); + return; + } + + var objType = obj.GetType(); + var typeCode = Type.GetTypeCode(objType); + if (typeCode != TypeCode.Object) + { + return; + } + + var genericArguments = objType.GetGenericArguments(); + if (genericArguments.Length == 0 && !objType.IsArray) + { + return; + } + + if (obj is System.Collections.IDictionary objDictionary) + { + //This extra code tests to not call "Dispose" for a Dictionary() + //for all values as "double" and alike doesn't support interface "IDisposable" + var valuesType = objType.GetGenericArguments()[1]; + var elementValuesTypeCode = Type.GetTypeCode(valuesType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in objDictionary.Values) + { + Dispose(value); + } + } + else if (obj is System.Collections.IEnumerable listValues) + { + //This extra code tests to not call "Dispose" for a List() + //for all keys as "int" and alike doesn't support interface "IDisposable" + var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; + var elementValuesTypeCode = Type.GetTypeCode(elementType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in listValues) + { + Dispose(value); + } + } + } + } + + // Big endian streams are not yet available in dotnet :'( + // https://github.com/dotnet/runtime/issues/26904 + + class StreamUnderflowException : System.Exception + { + public StreamUnderflowException() { } + } + + static class BigEndianStreamExtensions + { + public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static void WriteInt64(this Stream stream, long value) + { + int bytesToWrite = 8; +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static uint ReadUint32(this Stream stream, int bytesToRead = 4) + { + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + uint result = 0; + uint digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static ulong ReadUInt64(this Stream stream) + { + int bytesToRead = 8; + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + ulong result = 0; + ulong digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static void CheckRemaining(this Stream stream, int length) + { + if (stream.Length - stream.Position < length) + { + throw new StreamUnderflowException(); + } + } + + public static void ForEach(this T[] items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + } + + class BigEndianStream + { + Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public Stream InnerStream => stream; + + public bool HasRemaining() + { + return (stream.Length - Position) > 0; + } + + public long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] buffer) + { +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteUInt(uint value) => stream.WriteInt32((int)value); + + public void WriteInt(int value) => stream.WriteInt32(value); + + public void WriteULong(ulong value) => stream.WriteInt64((long)value); + + public void WriteLong(long value) => stream.WriteInt64(value); + + public void WriteFloat(float value) + { + unsafe + { + WriteInt(*((int*)&value)); + } + } + + public void WriteDouble(double value) => + stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); + + public byte[] ReadBytes(int length) + { + stream.CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); + + public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); + + public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); + + public ulong ReadULong() => stream.ReadUInt64(); + + public sbyte ReadSByte() => (sbyte)ReadByte(); + + public short ReadShort() => (short)ReadUShort(); + + public int ReadInt() => (int)ReadUInt(); + + public float ReadFloat() + { + unsafe + { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() => (long)ReadULong(); + + public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + + // This is an implementation detail that will be called internally by the public API. +#if NET8_0_OR_GREATER + static partial class _UniFFILib + { +#else + static class _UniFFILib + { +#endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceFree(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureDroppedCallbackStruct + { + public ulong @handle; + public IntPtr @free; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU8 + { + public byte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI8 + { + public sbyte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU16 + { + public ushort @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI16 + { + public short @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU32 + { + public uint @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI32 + { + public int @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU64 + { + public ulong @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI64 + { + public long @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF32 + { + public float @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF64 + { + public double @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultRustBuffer + { + public RustBuffer @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteRustBuffer( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultRustBuffer @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultVoid + { + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteVoid( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultVoid @result + ); + + static _UniFFILib() + { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + } + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_ffi_fn_func_build_version(ref UniffiRustCallStatus _uniffi_out_err); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_ffi_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_ffi_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rustbuffer_free(RustBuffer @buf, ref UniffiRustCallStatus _uniffi_out_err); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_ffi_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_u8(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_ffi_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_i8(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_ffi_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_u16(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_ffi_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_i16(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_ffi_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_u32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_ffi_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_i32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_ffi_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_u64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_ffi_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_i64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_ffi_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_f32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_ffi_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_f64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_ffi_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_ffi_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_ffi_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_ffi_checksum_func_build_version(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_ffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_ffi_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = _UniFFILib.ffi_livekit_ffi_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"LiveKit.Uniffi: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() + { + { + var checksum = _UniFFILib.uniffi_livekit_ffi_checksum_func_build_version(); + if (checksum != 47150) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_ffi_checksum_func_build_version` checksum `47150`, library returned `{checksum}`" + ); + } + } + } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } +#pragma warning restore 8625 + public static class LivekitFfiMethods + { + /// + /// Returns the version specified in the crate's Cargo.toml. + /// + /// This is the first function exposed over UniFFI from `livekit-ffi`. It carries + /// no server state and touches no handle store, so downstream SDKs can call it to + /// prove the UniFFI bindgen toolchain is wired up end-to-end. + /// + public static string BuildVersion() + { + return FfiConverterString.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_ffi_fn_func_build_version(ref _status) + ) + ); + } + } +} diff --git a/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta b/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta new file mode 100644 index 00000000..b4f67d49 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b19de9f1c658c40cb89437a04ce766ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Runtime/MeetManager.cs b/Samples~/Meet/Assets/Runtime/MeetManager.cs index 1dc27c71..3b69b189 100644 --- a/Samples~/Meet/Assets/Runtime/MeetManager.cs +++ b/Samples~/Meet/Assets/Runtime/MeetManager.cs @@ -5,6 +5,7 @@ using LiveKit; using LiveKit.Proto; using RoomOptions = LiveKit.RoomOptions; +using LiveKit.Uniffi; /// /// Manages a LiveKit room connection with local/remote audio and video tracks. @@ -200,9 +201,7 @@ private void OnToggleMicrophone() private void OnPublishData() { Debug.Log($"Published Data"); - var bytes = System.Text.Encoding.Default.GetBytes("hello from unity!"); - _room.LocalParticipant.PublishData(bytes); - _room.LocalParticipant.SendText("Hello from Unity, Max", "Chat"); + Debug.Log(LivekitFfiMethods.BuildVersion()); } #endregion diff --git a/Samples~/Meet/Assets/Scenes/MeetApp.unity b/Samples~/Meet/Assets/Scenes/MeetApp.unity index 91fc1e07..f06aaca4 100644 --- a/Samples~/Meet/Assets/Scenes/MeetApp.unity +++ b/Samples~/Meet/Assets/Scenes/MeetApp.unity @@ -919,7 +919,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a498c208deeab40c39b4ba609d7d222c, type: 3} m_Name: m_EditorClassIdentifier: - _config: {fileID: 11400000, guid: 1a1b8efb5101449b280f574f853b7459, type: 2} + _config: {fileID: 11400000, guid: aae6c4b1158ca4c929c5d84962c95f91, type: 2} --- !u!1 &1651282853 GameObject: m_ObjectHideFlags: 0 diff --git a/Scripts~/build_ffi_locally.sh b/Scripts~/build_ffi_locally.sh index d4588e67..4da0c16e 100755 --- a/Scripts~/build_ffi_locally.sh +++ b/Scripts~/build_ffi_locally.sh @@ -5,6 +5,12 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" MANIFEST="$ROOT/client-sdk-rust~/Cargo.toml" BASE_DST="$ROOT/Runtime/Plugins" BASE_TARGET="$ROOT/client-sdk-rust~/target" +UNIFFI_OUT_DIR="$ROOT/Runtime/Scripts/UniFFI" +UNIFFI_CONFIG="$SCRIPT_DIR/uniffi.toml" +# uniffi-bindgen-cs release. The "+vX.Y.Z" suffix is the uniffi version it targets +# and must match the uniffi version used by livekit-ffi in client-sdk-rust~. +UNIFFI_BINDGEN_CS_REPO="https://github.com/NordSecurity/uniffi-bindgen-cs" +UNIFFI_BINDGEN_CS_TAG="v0.11.0+v0.31.0" RED='\033[0;31m' YELLOW='\033[0;33m' @@ -30,6 +36,9 @@ usage() { echo "Build types (optional, defaults to 'debug'):" echo " release Optimized release build" echo " debug Debug build" + echo "" + echo "macOS builds also regenerate the UniFFI C# bindings in Runtime/Scripts/UniFFI" + echo "(requires uniffi-bindgen-cs) and post-process them for C# 9." exit 1 } @@ -130,17 +139,26 @@ if [ "$PLATFORM" = "ios" ] && [ "$BUILD_TYPE" = "release" ]; then xcrun ranlib "$SRC" fi +# Copy a built artifact into the package by writing a temp file next to the +# destination and renaming it into place, instead of overwriting in place. +# macOS caches code-signature state per inode: overwriting a signed dylib that a +# running Unity editor still has mapped makes every later load of that path die +# with SIGKILL "Code Signature Invalid" until the file gets a new inode. +install_file() { + local src="$1" dst="$2" tmp="$2.tmp" + if ! cp -f "$src" "$tmp" || ! mv -f "$tmp" "$dst"; then + rm -f "$tmp" + return 1 + fi +} + # Copy the built lib echo "" echo "Copying to $DST..." -cp -f "$SRC" "$DST" +install_file "$SRC" "$DST" if [ $? -eq 0 ]; then echo -e "${GREEN}Copied $(basename "$DST") successfully.${RESET}" - if [ "$PLATFORM" = "macos" ]; then - echo "" - echo -e "${YELLOW}WARNING: QUIT UNITY TO LOAD NEW LIB${RESET}" - fi else echo -e "${RED}Failed to copy $(basename "$DST"). Check that the source file exists and the destination directory is writable.${RESET}" exit 1 @@ -150,7 +168,7 @@ fi if [ "$PLATFORM" = "android" ]; then echo "" echo "Copying to $JAR_DST..." - cp -f "$JAR_SRC" "$JAR_DST" + install_file "$JAR_SRC" "$JAR_DST" if [ $? -eq 0 ]; then echo -e "${GREEN}Copied $(basename "$JAR_DST") successfully.${RESET}" @@ -158,4 +176,43 @@ if [ "$PLATFORM" = "android" ]; then echo -e "${RED}Failed to copy $(basename "$JAR_DST"). Check that the source file exists and the destination directory is writable.${RESET}" exit 1 fi -fi \ No newline at end of file +fi + +# For macOS, regenerate the UniFFI C# bindings from the freshly built dylib and +# post-process them for C# 9 (Unity). uniffi-bindgen-cs resolves the crate config +# via `cargo metadata`, so it has to run from inside the Rust workspace. +if [ "$PLATFORM" = "macos" ]; then + echo "" + if ! command -v uniffi-bindgen-cs > /dev/null 2>&1; then + echo -e "${YELLOW}uniffi-bindgen-cs not found, skipping C# binding generation. Install it with:${RESET}" + echo " cargo install uniffi-bindgen-cs --git $UNIFFI_BINDGEN_CS_REPO --tag $UNIFFI_BINDGEN_CS_TAG" + else + INSTALLED_TAG="v$(uniffi-bindgen-cs --version | awk '{print $2}')" + if [ "$INSTALLED_TAG" != "$UNIFFI_BINDGEN_CS_TAG" ]; then + echo -e "${YELLOW}WARNING: uniffi-bindgen-cs $INSTALLED_TAG is installed, expected $UNIFFI_BINDGEN_CS_TAG (must match the uniffi version of livekit-ffi).${RESET}" + fi + + echo "Generating C# bindings into $UNIFFI_OUT_DIR..." + mkdir -p "$UNIFFI_OUT_DIR" + pushd "$ROOT/client-sdk-rust~" > /dev/null + uniffi-bindgen-cs --library "$SRC" --config "$UNIFFI_CONFIG" --out-dir "$UNIFFI_OUT_DIR" + BINDGEN_STATUS=$? + popd > /dev/null + + if [ $BINDGEN_STATUS -ne 0 ]; then + echo -e "${RED}uniffi-bindgen-cs failed.${RESET}" + exit 1 + fi + + # uniffi-bindgen-cs exits 0 without writing anything if the lib carries no UniFFI metadata + if [ -z "$(find "$UNIFFI_OUT_DIR" -maxdepth 1 -name '*.cs' -newer "$SRC")" ]; then + echo -e "${YELLOW}WARNING: No bindings were written. $(basename "$SRC") contains no UniFFI metadata; is client-sdk-rust~ on a commit where livekit-ffi exports UniFFI?${RESET}" + else + python3 "$SCRIPT_DIR/downgrade_uniffi_bindings.py" "$UNIFFI_OUT_DIR" || exit 1 + echo -e "${GREEN}Generated C# bindings successfully.${RESET}" + fi + fi + + echo "" + echo -e "${YELLOW}WARNING: QUIT UNITY TO LOAD NEW LIB${RESET}" +fi diff --git a/Scripts~/downgrade_uniffi_bindings.py b/Scripts~/downgrade_uniffi_bindings.py new file mode 100755 index 00000000..118a9b07 --- /dev/null +++ b/Scripts~/downgrade_uniffi_bindings.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +"""Post-process uniffi-bindgen-cs output so it compiles with C# 9 (Unity). + +uniffi-bindgen-cs emits C# 10+ syntax, but Unity pins the compiler to C# 9. +This script rewrites the generated .cs files in place: + + * file-scoped namespace (`namespace Foo;`, C# 10) becomes a block-scoped + namespace with the body indented by four spaces + * `return [];` in sequence converters (collection expression, C# 12) becomes + `return System.Array.Empty();` + * method groups stored in locals (`var readFn = FfiConverterX.INSTANCE.Read;`, + inferred delegate type, C# 10) are removed and their calls inlined + * when the bindings use records or `init` accessors (C# 9 syntax whose + `System.Runtime.CompilerServices.IsExternalInit` marker type is missing + from .NET Standard 2.1), an `IsExternalInit.cs` polyfill is written next to + the bindings; it is removed again when no longer needed + +Custom types that alias an array, nullable or tuple type (`using Foo = byte[];`, +C# 12) cannot be expressed in C# 9 and are reported as errors; change the +custom type mapping in uniffi.toml instead. + +Usage: + downgrade_uniffi_bindings.py [...] + +Directories are scanned for *.cs files (non-recursive). The script is +idempotent: files that are already downgraded are left untouched. +""" + +import re +import sys +from pathlib import Path + +INDENT = " " +MARKER = "// Post-processed for C# 9 by Scripts~/downgrade_uniffi_bindings.py" +POLYFILL_NAME = "IsExternalInit.cs" +POLYFILL = f"""// +// Written by Scripts~/downgrade_uniffi_bindings.py. Do not edit. +// The generated UniFFI bindings use C# 9 records / init accessors, which +// require this marker type. .NET Standard 2.1 (Unity) does not ship it. +// + +#if !NET5_0_OR_GREATER +namespace System.Runtime.CompilerServices +{{ +{INDENT}internal static class IsExternalInit {{ }} +}} +#endif +""" + +FILE_SCOPED_NAMESPACE = re.compile(r"^\s*namespace\s+([A-Za-z_][\w.]*)\s*;\s*$") +# `public override T[] Read(BigEndianStream stream) {` in SequenceTemplate.cs +ARRAY_READ_SIGNATURE = re.compile(r"^\s*public\s+override\s+(?P.+?)\s*\[\]\s+Read\s*\(\s*BigEndianStream\b") +EMPTY_COLLECTION_RETURN = re.compile(r"^(?P\s*)return\s*\[\s*\]\s*;\s*$") +# `using Alias = Target;` — C# 9 only allows (qualified, possibly generic) type names as Target +TYPE_ALIAS = re.compile(r"^\s*using\s+[A-Za-z_@]\w*\s*=\s*(?P[^;]+);\s*$") +VALID_ALIAS_TARGET = re.compile(r"^[A-Za-z_@][\w.]*(\s*<.*>)?\s*$") +# `var readFn = FfiConverterX.INSTANCE.Read;` in SequenceTemplate.cs / MapTemplate.cs +METHOD_GROUP_LOCAL = re.compile( + r"^\s*var\s+(?P[A-Za-z_]\w*)\s*=\s*(?P[A-Za-z_][\w.]*\.(?:Read|Write|AllocationSize))\s*;\s*$" +) +STRING_LITERAL = re.compile(r'\$?@?"(?:[^"\\]|\\.)*"') +RECORD_DECLARATION = re.compile( + r"^\s*(?:(?:public|internal|private|protected|sealed|abstract|partial|static|new)\s+)*record\s+(?:struct\s+|class\s+)?[A-Za-z_@]" +) +INIT_ACCESSOR = re.compile(r"\binit\s*[;{]") + + +def block_scope_namespace(lines): + """Wrap everything after a file-scoped namespace declaration into a + block-scoped namespace. Returns (lines, changed).""" + matches = [i for i, line in enumerate(lines) if FILE_SCOPED_NAMESPACE.match(line)] + if not matches: + return lines, False + if len(matches) > 1: + raise ValueError(f"expected a single file-scoped namespace, found {len(matches)}") + + index = matches[0] + name = FILE_SCOPED_NAMESPACE.match(lines[index]).group(1) + + body = [] + for line in lines[index + 1 :]: + stripped = line.strip() + if not stripped: + body.append("") + elif stripped.startswith("#"): + # Keep preprocessor directives at column 0, as CSharpier does. + body.append(line) + else: + body.append(INDENT + line) + + # Drop blank lines directly inside the braces. + while body and not body[0]: + body.pop(0) + while body and not body[-1]: + body.pop() + + return lines[:index] + [f"namespace {name}", "{"] + body + ["}"], True + + +def replace_empty_collection_returns(lines): + """`return [];` -> `return System.Array.Empty();`, taking T from the + enclosing `T[] Read(BigEndianStream)` signature. Returns (lines, changed).""" + result = [] + element = None + changed = False + for number, line in enumerate(lines, start=1): + signature = ARRAY_READ_SIGNATURE.match(line) + if signature: + element = signature.group("element").strip() + elif "override" in line and "(" in line: + element = None # a different method started + + empty_return = EMPTY_COLLECTION_RETURN.match(line) + if empty_return: + if element is None: + raise ValueError(f"line {number}: `return [];` outside of a sequence Read() method") + line = f"{empty_return.group('indent')}return System.Array.Empty<{element}>();" + changed = True + result.append(line) + return result, changed + + +def inline_method_groups(lines): + """Drop `var fn = Converter.INSTANCE.Method;` locals and call the method + directly wherever `fn(...)` is used in the enclosing block. Returns + (lines, changed).""" + lines = list(lines) + result = [] + changed = False + for i, line in enumerate(lines): + local = METHOD_GROUP_LOCAL.match(line) + if not local: + result.append(line) + continue + + name, target = local.group("name"), local.group("target") + call = re.compile(rf"\b{re.escape(name)}\s*\(") + bare = re.compile(rf"\b{re.escape(name)}\b") + depth = 0 + for j in range(i + 1, len(lines)): + without_strings = STRING_LITERAL.sub('""', lines[j]) + depth += without_strings.count("{") - without_strings.count("}") + if depth < 0: + break # enclosing block closed + lines[j] = call.sub(f"{target}(", lines[j]) + if bare.search(lines[j]): + raise ValueError( + f"line {j + 1}: `{name}` is used as a delegate value; cannot inline method group `{target}`" + ) + changed = True # declaration line is dropped + return result, changed + + +def check_type_aliases(lines): + for number, line in enumerate(lines, start=1): + alias = TYPE_ALIAS.match(line) + if alias and not VALID_ALIAS_TARGET.match(alias.group("target")): + raise ValueError( + f"line {number}: `{line.strip()}` needs C# 12 (alias any type); " + "map this custom type to a named type in uniffi.toml" + ) + + +def add_marker(lines): + """Record the post-processing in the header, if present.""" + if MARKER in lines: + return lines + for i, line in enumerate(lines[:10]): + if line.strip() == "// ": + return lines[:i] + [MARKER] + lines[i:] + return lines + + +def needs_is_external_init(lines): + return any(RECORD_DECLARATION.match(line) or INIT_ACCESSOR.search(line) for line in lines) + + +def process_file(path): + """Returns (changed, needs_polyfill).""" + raw = path.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw[3:].decode("utf-8") if bom else raw.decode("utf-8") + newline = "\r\n" if "\r\n" in text else "\n" + lines = text.replace("\r\n", "\n").split("\n") + if lines and lines[-1] == "": + lines.pop() # trailing newline is re-added on write + + check_type_aliases(lines) + lines, namespace_changed = block_scope_namespace(lines) + lines, returns_changed = replace_empty_collection_returns(lines) + lines, method_groups_changed = inline_method_groups(lines) + changed = namespace_changed or returns_changed or method_groups_changed + + if changed: + lines = add_marker(lines) + out = newline.join(lines) + newline + path.write_bytes((b"\xef\xbb\xbf" if bom else b"") + out.encode("utf-8")) + + return changed, needs_is_external_init(lines) + + +def sync_polyfill(directory, needed): + """Create or remove IsExternalInit.cs (and its Unity .meta) in `directory`.""" + polyfill = directory / POLYFILL_NAME + if needed: + if not polyfill.exists() or polyfill.read_text(encoding="utf-8") != POLYFILL: + polyfill.write_text(POLYFILL, encoding="utf-8") + print(f" wrote {polyfill}") + elif polyfill.exists() and "downgrade_uniffi_bindings.py" in polyfill.read_text(encoding="utf-8"): + polyfill.unlink() + print(f" removed {polyfill}") + meta = polyfill.with_name(POLYFILL_NAME + ".meta") + if meta.exists(): + meta.unlink() + print(f" removed {meta}") + + +def main(argv): + if len(argv) < 2: + print(__doc__.strip()) + return 2 + + status = 0 + for arg in argv[1:]: + target = Path(arg) + if target.is_dir(): + directory = target + files = sorted(p for p in target.glob("*.cs") if p.name != POLYFILL_NAME) + elif target.is_file(): + directory = target.parent + files = [target] + else: + print(f"error: {target} does not exist", file=sys.stderr) + status = 1 + continue + + polyfill_needed = False + for path in files: + try: + changed, needs_polyfill = process_file(path) + except (ValueError, UnicodeDecodeError) as exc: + print(f"error: {path}: {exc}", file=sys.stderr) + status = 1 + continue + polyfill_needed |= needs_polyfill + print(f" {'downgraded' if changed else 'unchanged '} {path}") + + sync_polyfill(directory, polyfill_needed) + + return status + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/Scripts~/uniffi.toml b/Scripts~/uniffi.toml new file mode 100644 index 00000000..a905383e --- /dev/null +++ b/Scripts~/uniffi.toml @@ -0,0 +1,7 @@ +# uniffi-bindgen-cs configuration for the C# bindings in Runtime/Scripts/UniFFI. +# Passed via --config by build_ffi_locally.sh; keeps the Unity-specific choices out of +# the Rust crate. Options: https://github.com/NordSecurity/uniffi-bindgen-cs/blob/main/docs/CONFIGURATION.md +[bindings.csharp] +namespace = "LiveKit.Uniffi" +# Generated API types (methods class, records, enums, errors, objects) are internal by default. +access_modifier = "public" diff --git a/client-sdk-rust~ b/client-sdk-rust~ index 267ebb84..4dfca31a 160000 --- a/client-sdk-rust~ +++ b/client-sdk-rust~ @@ -1 +1 @@ -Subproject commit 267ebb84a0c2e1b3f68f5af8a27f0f7d92b6a7ff +Subproject commit 4dfca31ae81939e40e8d15fe4dca0922ed5e687a From cec976ccf82a57af059c7b55be8f887fc04bcb66 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:42:05 +0200 Subject: [PATCH 2/7] Moved uniffi stuff into subfolder --- AGENTS.md | 6 +++--- README.md | 2 +- Runtime/Scripts/UniFFI/livekit_ffi.cs | 2 +- Scripts~/build_ffi_locally.sh | 4 ++-- Scripts~/{ => uniffi}/downgrade_uniffi_bindings.py | 4 ++-- Scripts~/{ => uniffi}/uniffi.toml | 0 6 files changed, 9 insertions(+), 9 deletions(-) rename Scripts~/{ => uniffi}/downgrade_uniffi_bindings.py (98%) rename Scripts~/{ => uniffi}/uniffi.toml (100%) diff --git a/AGENTS.md b/AGENTS.md index b2ca4763..32fd844f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,13 +28,13 @@ Scripts~/build_ffi_locally.sh [build_type] - **Android**: requires `cargo-ndk` and Android NDK - **iOS**: builds static lib (`liblivekit_ffi.a`) - After macOS builds, Unity must be restarted to load the new dylib -- macOS builds also regenerate the UniFFI C# bindings in `Runtime/Scripts/UniFFI/` with `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of `livekit-ffi`), configured by `Scripts~/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types), and post-process them with `Scripts~/downgrade_uniffi_bindings.py` for C# 9. Runs from inside `client-sdk-rust~` because bindgen needs `cargo metadata`. +- macOS builds also regenerate the UniFFI C# bindings in `Runtime/Scripts/UniFFI/` with `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of `livekit-ffi`), configured by `Scripts~/uniffi/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types), and post-process them with `Scripts~/uniffi/downgrade_uniffi_bindings.py` for C# 9. Runs from inside `client-sdk-rust~` because bindgen needs `cargo metadata`. ### Other scripts in `Scripts~/` - `download_libs.py` — downloads the prebuilt FFI binaries for all platforms; the release tag is pinned in `version.ini` - `generate_proto.sh` — regenerates `Runtime/Scripts/Proto/` from the protobuf definitions in `client-sdk-rust~/livekit-ffi/protocol` (requires `protoc`) -- `uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with the build script -- `downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present); idempotent, called by `build_ffi_locally.sh macos` +- `uniffi/uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with the build script +- `uniffi/downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present); idempotent, called by `build_ffi_locally.sh macos` - `build_docs.sh`, `prepare_release.py`, `unity_test_results_utils.py` — docs generation, release preparation, CI test-result parsing ### Run tests diff --git a/README.md b/README.md index d90be256..a7cc9ab1 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ macOS builds also regenerate the C# bindings for the UniFFI part of `liblivekit_ `cargo install uniffi-bindgen-cs --git https://github.com/NordSecurity/uniffi-bindgen-cs --tag v0.11.0+v0.31.0` -The generator is configured by `Scripts~/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types), which the build script passes with `--config`. uniffi-bindgen-cs emits C# 10+ syntax, so the build script post-processes the output with `Scripts~/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler. Run that script on `Runtime/Scripts/UniFFI` yourself if you generated the bindings by hand. +The generator is configured by `Scripts~/uniffi/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types), which the build script passes with `--config`. uniffi-bindgen-cs emits C# 10+ syntax, so the build script post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler. Run that script on `Runtime/Scripts/UniFFI` yourself if you generated the bindings by hand. ### VSCode setup diff --git a/Runtime/Scripts/UniFFI/livekit_ffi.cs b/Runtime/Scripts/UniFFI/livekit_ffi.cs index 734a023d..d14f0e6a 100644 --- a/Runtime/Scripts/UniFFI/livekit_ffi.cs +++ b/Runtime/Scripts/UniFFI/livekit_ffi.cs @@ -1,7 +1,7 @@ // // This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 // See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. -// Post-processed for C# 9 by Scripts~/downgrade_uniffi_bindings.py +// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py // #nullable enable diff --git a/Scripts~/build_ffi_locally.sh b/Scripts~/build_ffi_locally.sh index 4da0c16e..6a87b518 100755 --- a/Scripts~/build_ffi_locally.sh +++ b/Scripts~/build_ffi_locally.sh @@ -6,7 +6,7 @@ MANIFEST="$ROOT/client-sdk-rust~/Cargo.toml" BASE_DST="$ROOT/Runtime/Plugins" BASE_TARGET="$ROOT/client-sdk-rust~/target" UNIFFI_OUT_DIR="$ROOT/Runtime/Scripts/UniFFI" -UNIFFI_CONFIG="$SCRIPT_DIR/uniffi.toml" +UNIFFI_CONFIG="$SCRIPT_DIR/uniffi/uniffi.toml" # uniffi-bindgen-cs release. The "+vX.Y.Z" suffix is the uniffi version it targets # and must match the uniffi version used by livekit-ffi in client-sdk-rust~. UNIFFI_BINDGEN_CS_REPO="https://github.com/NordSecurity/uniffi-bindgen-cs" @@ -208,7 +208,7 @@ if [ "$PLATFORM" = "macos" ]; then if [ -z "$(find "$UNIFFI_OUT_DIR" -maxdepth 1 -name '*.cs' -newer "$SRC")" ]; then echo -e "${YELLOW}WARNING: No bindings were written. $(basename "$SRC") contains no UniFFI metadata; is client-sdk-rust~ on a commit where livekit-ffi exports UniFFI?${RESET}" else - python3 "$SCRIPT_DIR/downgrade_uniffi_bindings.py" "$UNIFFI_OUT_DIR" || exit 1 + python3 "$SCRIPT_DIR/uniffi/downgrade_uniffi_bindings.py" "$UNIFFI_OUT_DIR" || exit 1 echo -e "${GREEN}Generated C# bindings successfully.${RESET}" fi fi diff --git a/Scripts~/downgrade_uniffi_bindings.py b/Scripts~/uniffi/downgrade_uniffi_bindings.py similarity index 98% rename from Scripts~/downgrade_uniffi_bindings.py rename to Scripts~/uniffi/downgrade_uniffi_bindings.py index 118a9b07..c6e64d4d 100755 --- a/Scripts~/downgrade_uniffi_bindings.py +++ b/Scripts~/uniffi/downgrade_uniffi_bindings.py @@ -31,10 +31,10 @@ from pathlib import Path INDENT = " " -MARKER = "// Post-processed for C# 9 by Scripts~/downgrade_uniffi_bindings.py" +MARKER = "// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py" POLYFILL_NAME = "IsExternalInit.cs" POLYFILL = f"""// -// Written by Scripts~/downgrade_uniffi_bindings.py. Do not edit. +// Written by Scripts~/uniffi/downgrade_uniffi_bindings.py. Do not edit. // The generated UniFFI bindings use C# 9 records / init accessors, which // require this marker type. .NET Standard 2.1 (Unity) does not ship it. // diff --git a/Scripts~/uniffi.toml b/Scripts~/uniffi/uniffi.toml similarity index 100% rename from Scripts~/uniffi.toml rename to Scripts~/uniffi/uniffi.toml From 8b7da5111a99a219690c39cbddb7b890f0f186cc Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Wed, 9 Sep 2026 17:45:07 +0200 Subject: [PATCH 3/7] Untested Uniffi crate scripts and compiled macos lib --- .gitattributes | 1 + AGENTS.md | 30 +- README.md | 12 +- .../ffi-macos-arm64/liblivekit_uniffi.dylib | 3 + Runtime/Scripts/UniFFI/livekit_ffi.cs | 1554 -------- Runtime/Scripts/UniFFI/livekit_ffi.cs.meta | 11 - Runtime/Scripts/UniFFI/livekit_uniffi.cs | 3460 +++++++++++++++++ Samples~/Meet/Assets/Runtime/MeetManager.cs | 2 +- Scripts~/build_ffi_locally.sh | 44 +- Scripts~/build_uniffi_locally.sh | 125 + Scripts~/generate_uniffi_bindings.sh | 106 + Scripts~/uniffi/downgrade_uniffi_bindings.py | 14 +- client-sdk-rust~ | 2 +- 13 files changed, 3745 insertions(+), 1619 deletions(-) create mode 100755 Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib delete mode 100644 Runtime/Scripts/UniFFI/livekit_ffi.cs delete mode 100644 Runtime/Scripts/UniFFI/livekit_ffi.cs.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_uniffi.cs create mode 100755 Scripts~/build_uniffi_locally.sh create mode 100755 Scripts~/generate_uniffi_bindings.sh diff --git a/.gitattributes b/.gitattributes index 42445c37..c23823f4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,6 +14,7 @@ Runtime/Plugins/ffi-linux-x86_64/liblivekit_ffi.so filter=lfs diff=lfs merge=lfs Runtime/Plugins/ffi-android-arm64/liblivekit_ffi.so filter=lfs diff=lfs merge=lfs -text Runtime/Plugins/ffi-macos-x86_64/liblivekit_ffi.dylib filter=lfs diff=lfs merge=lfs -text Runtime/Plugins/ffi-macos-arm64/liblivekit_ffi.dylib filter=lfs diff=lfs merge=lfs -text +Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib filter=lfs diff=lfs merge=lfs -text Runtime/Plugins/ffi-android-arm64/libwebrtc.jar filter=lfs diff=lfs merge=lfs -text Runtime/Plugins/ffi-android-armv7/libwebrtc.jar filter=lfs diff=lfs merge=lfs -text Runtime/Plugins/ffi-android-x86_64/libwebrtc.jar filter=lfs diff=lfs merge=lfs -text diff --git a/AGENTS.md b/AGENTS.md index 32fd844f..589a81dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,13 +28,31 @@ Scripts~/build_ffi_locally.sh [build_type] - **Android**: requires `cargo-ndk` and Android NDK - **iOS**: builds static lib (`liblivekit_ffi.a`) - After macOS builds, Unity must be restarted to load the new dylib -- macOS builds also regenerate the UniFFI C# bindings in `Runtime/Scripts/UniFFI/` with `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of `livekit-ffi`), configured by `Scripts~/uniffi/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types), and post-process them with `Scripts~/uniffi/downgrade_uniffi_bindings.py` for C# 9. Runs from inside `client-sdk-rust~` because bindgen needs `cargo metadata`. +- macOS builds also run `Scripts~/generate_uniffi_bindings.sh` on the new dylib (see below); if it carries no UniFFI metadata the step warns and leaves the existing bindings untouched + +### Build the UniFFI library locally from Rust source +```bash +# Builds the experimental livekit-uniffi crate (client-sdk-rust~/livekit-uniffi), macOS only +Scripts~/build_uniffi_locally.sh macos [build_type] +# Build types: debug (default), release +``` +- Plain-cargo equivalent of `cargo make build` in the crate's `Makefile.toml`, with a selectable build type +- Installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64/`, then regenerates the UniFFI C# bindings from it with `Scripts~/generate_uniffi_bindings.sh` +- After the build, Unity must be restarted to load the new dylib + +### Generate the UniFFI C# bindings +```bash +# Defaults to the installed Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib +Scripts~/generate_uniffi_bindings.sh [library] +``` +- Runs `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of the Rust crate) from inside `client-sdk-rust~` because bindgen needs `cargo metadata`; configured by `Scripts~/uniffi/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types) +- Post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` for C# 9, then replaces the `.cs` files in `Runtime/Scripts/UniFFI/` and removes `.meta` files of bindings that no longer exist. The folder always reflects the last library it was generated from; a library without UniFFI metadata leaves it untouched (with a warning) ### Other scripts in `Scripts~/` - `download_libs.py` — downloads the prebuilt FFI binaries for all platforms; the release tag is pinned in `version.ini` - `generate_proto.sh` — regenerates `Runtime/Scripts/Proto/` from the protobuf definitions in `client-sdk-rust~/livekit-ffi/protocol` (requires `protoc`) -- `uniffi/uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with the build script -- `uniffi/downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present); idempotent, called by `build_ffi_locally.sh macos` +- `uniffi/uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with `generate_uniffi_bindings.sh` +- `uniffi/downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present unless `--no-polyfill`); idempotent, called by `generate_uniffi_bindings.sh` with `--no-polyfill` because the Runtime assembly already ships `Internal/IsExternalInit.cs` - `build_docs.sh`, `prepare_release.py`, `unity_test_results_utils.py` — docs generation, release preparation, CI test-result parsing ### Run tests @@ -63,7 +81,7 @@ The SDK wraps a Rust native library (`liblivekit_ffi`) via P/Invoke. The communi - `TokenSource/` — token generation/fetching helpers and MonoBehaviour component - `UniTask/` — optional UniTask integration (own asmdef: `livekit.unity.Runtime.UniTask.asmdef`) - `Internal/`, `Proto/` — FFI plumbing and generated protobuf code -- `UniFFI/` — generated C# bindings for the UniFFI surface of `liblivekit_ffi` (namespace `LiveKit.Uniffi`; uniffi-bindgen-cs output, post-processed for C# 9) +- `UniFFI/` — generated C# bindings for the UniFFI surface of the native library, currently `liblivekit_uniffi` from the experimental `livekit-uniffi` crate (namespace `LiveKit.Uniffi`; uniffi-bindgen-cs output, post-processed for C# 9) Key internal files: - `Internal/FFI/FFIClient.cs` — singleton managing request/response lifecycle with Rust via protobuf @@ -74,10 +92,10 @@ Key internal files: ### Proto/Generated Code `Runtime/Scripts/Proto/` contains auto-generated C# from protobuf definitions in the Rust SDK. Do not edit these files manually; regenerate with `Scripts~/generate_proto.sh`. -`Runtime/Scripts/UniFFI/` contains auto-generated C# from `uniffi-bindgen-cs`. Do not edit these files manually; regenerate with `Scripts~/build_ffi_locally.sh macos`, which also runs the C# 9 post-processing. +`Runtime/Scripts/UniFFI/` contains auto-generated C# from `uniffi-bindgen-cs`. Do not edit these files manually; regenerate with `Scripts~/generate_uniffi_bindings.sh` (also run by `Scripts~/build_uniffi_locally.sh macos`), which includes the C# 9 post-processing. ### Native Plugins -10 platform/arch combinations in `Runtime/Plugins/ffi-{platform}-{arch}/`. These are large binary files tracked with Git LFS. The `.meta` files configure Unity platform targeting. +10 platform/arch combinations in `Runtime/Plugins/ffi-{platform}-{arch}/`. These are large binary files tracked with Git LFS. The `.meta` files configure Unity platform targeting. `ffi-macos-arm64/` additionally holds `liblivekit_uniffi.dylib`, the locally built experimental UniFFI library. ### Samples - `Samples~/Common` — shared components used by the other samples diff --git a/README.md b/README.md index a7cc9ab1..49761e2f 100644 --- a/README.md +++ b/README.md @@ -83,13 +83,21 @@ So a build command is for example: `./Scripts~/build_ffi_locally.sh macos release` +### Building the UniFFI library locally + +The experimental [livekit-uniffi](https://github.com/livekit/rust-sdks/tree/main/livekit-uniffi) crate is built with a second script, currently for macOS only: + +`./Scripts~/build_uniffi_locally.sh macos [debug|release]` + +It installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64` and then regenerates the C# bindings as described below. + ### Generating UniFFI C# bindings -macOS builds also regenerate the C# bindings for the UniFFI part of `liblivekit_ffi` into `Runtime/Scripts/UniFFI`. This requires [uniffi-bindgen-cs](https://github.com/NordSecurity/uniffi-bindgen-cs) at the version pinned in `Scripts~/build_ffi_locally.sh`, which must match the uniffi version used by `livekit-ffi`: +`./Scripts~/generate_uniffi_bindings.sh [library]` regenerates the C# bindings in `Runtime/Scripts/UniFFI` from a native library with UniFFI metadata; without an argument it uses the installed `liblivekit_uniffi.dylib`. Both build scripts call it after a macOS build. It requires [uniffi-bindgen-cs](https://github.com/NordSecurity/uniffi-bindgen-cs) at the version pinned in the script, which must match the uniffi version used by the Rust crate: `cargo install uniffi-bindgen-cs --git https://github.com/NordSecurity/uniffi-bindgen-cs --tag v0.11.0+v0.31.0` -The generator is configured by `Scripts~/uniffi/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types), which the build script passes with `--config`. uniffi-bindgen-cs emits C# 10+ syntax, so the build script post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler. Run that script on `Runtime/Scripts/UniFFI` yourself if you generated the bindings by hand. +The generator is configured by `Scripts~/uniffi/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types). uniffi-bindgen-cs emits C# 10+ syntax, so the script post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler before replacing the files in `Runtime/Scripts/UniFFI`. ### VSCode setup diff --git a/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib new file mode 100755 index 00000000..58144111 --- /dev/null +++ b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d7ad0a8501c760db077813cdffb5927e064489e3738ba5c675adc28fa886e40 +size 4199592 diff --git a/Runtime/Scripts/UniFFI/livekit_ffi.cs b/Runtime/Scripts/UniFFI/livekit_ffi.cs deleted file mode 100644 index d14f0e6a..00000000 --- a/Runtime/Scripts/UniFFI/livekit_ffi.cs +++ /dev/null @@ -1,1554 +0,0 @@ -// -// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 -// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. -// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py -// - -#nullable enable - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace LiveKit.Uniffi -{ - // This is a helper for safely working with byte buffers returned from the Rust code. - // A rust-owned buffer is represented by its capacity, its current length, and a - // pointer to the underlying data. - - [StructLayout(LayoutKind.Sequential)] - internal struct RustBuffer - { - public ulong capacity; - public ulong len; - public IntPtr data; - - public static RustBuffer Alloc(int size) - { - return _UniffiHelpers.RustCall( - (ref UniffiRustCallStatus status) => - { - var buffer = _UniFFILib.ffi_livekit_ffi_rustbuffer_alloc( - Convert.ToUInt64(size), - ref status - ); - if (buffer.data == IntPtr.Zero) - { - throw new AllocationException( - $"RustBuffer.Alloc() returned null data pointer (size={size})" - ); - } - return buffer; - } - ); - } - - public static void Free(RustBuffer buffer) - { - _UniffiHelpers.RustCall( - (ref UniffiRustCallStatus status) => - { - _UniFFILib.ffi_livekit_ffi_rustbuffer_free(buffer, ref status); - } - ); - } - - public static BigEndianStream MemoryStream(IntPtr data, long length) - { - unsafe - { - return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); - } - } - - public BigEndianStream AsStream() - { - unsafe - { - return new BigEndianStream( - new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) - ); - } - } - - public BigEndianStream AsWriteableStream() - { - unsafe - { - return new BigEndianStream( - new UnmanagedMemoryStream( - (byte*)data.ToPointer(), - Convert.ToInt64(capacity), - Convert.ToInt64(capacity), - FileAccess.Write - ) - ); - } - } - } - - // This is a helper for safely passing byte references into the rust code. - // It's not actually used at the moment, because there aren't many things that you - // can take a direct pointer to managed memory, and if we're going to copy something - // then we might as well copy it into a `RustBuffer`. But it's here for API - // completeness. - - [StructLayout(LayoutKind.Sequential)] - internal struct ForeignBytes - { - public int length; - public IntPtr data; - } - - // The FfiConverter interface handles converter types to and from the FFI - // - // All implementing objects should be public to support external types. When a - // type is external we need to import it's FfiConverter. - internal abstract class FfiConverter - { - // Convert an FFI type to a C# type - public abstract CsType Lift(FfiType value); - - // Convert C# type to an FFI type - public abstract FfiType Lower(CsType value); - - // Read a C# type from a `ByteBuffer` - public abstract CsType Read(BigEndianStream stream); - - // Calculate bytes to allocate when creating a `RustBuffer` - // - // This must return at least as many bytes as the write() function will - // write. It can return more bytes than needed, for example when writing - // Strings we can't know the exact bytes needed until we the UTF-8 - // encoding, so we pessimistically allocate the largest size possible (3 - // bytes per codepoint). Allocating extra bytes is not really a big deal - // because the `RustBuffer` is short-lived. - public abstract int AllocationSize(CsType value); - - // Write a C# type to a `ByteBuffer` - public abstract void Write(CsType value, BigEndianStream stream); - - // Lower a value into a `RustBuffer` - // - // This method lowers a value into a `RustBuffer` rather than the normal - // FfiType. It's used by the callback interface code. Callback interface - // returns are always serialized into a `RustBuffer` regardless of their - // normal FFI type. - public RustBuffer LowerIntoRustBuffer(CsType value) - { - var rbuf = RustBuffer.Alloc(AllocationSize(value)); - try - { - var stream = rbuf.AsWriteableStream(); - Write(value, stream); - rbuf.len = Convert.ToUInt64(stream.Position); - return rbuf; - } - catch - { - RustBuffer.Free(rbuf); - throw; - } - } - - // Lift a value from a `RustBuffer`. - // - // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. - // It's currently only used by the `FfiConverterRustBuffer` class below. - protected CsType LiftFromRustBuffer(RustBuffer rbuf) - { - var stream = rbuf.AsStream(); - try - { - var item = Read(stream); - if (stream.HasRemaining()) - { - throw new InternalException( - "junk remaining in buffer after lifting, something is very wrong!!" - ); - } - return item; - } - finally - { - RustBuffer.Free(rbuf); - } - } - } - - // FfiConverter that uses `RustBuffer` as the FfiType - internal abstract class FfiConverterRustBuffer : FfiConverter - { - public override CsType Lift(RustBuffer value) - { - return LiftFromRustBuffer(value); - } - - public override RustBuffer Lower(CsType value) - { - return LowerIntoRustBuffer(value); - } - } - - // A handful of classes and functions to support the generated data structures. - // This would be a good candidate for isolating in its own ffi-support lib. - // Error runtime. - [StructLayout(LayoutKind.Sequential)] - struct UniffiRustCallStatus - { - public sbyte code; - public RustBuffer error_buf; - - public bool IsSuccess() - { - return code == 0; - } - - public bool IsError() - { - return code == 1; - } - - public bool IsPanic() - { - return code == 2; - } - } - - // Base class for all uniffi exceptions - public class UniffiException : System.Exception - { - public UniffiException() - : base() { } - - public UniffiException(string message) - : base(message) { } - } - - public class UndeclaredErrorException : UniffiException - { - public UndeclaredErrorException(string message) - : base(message) { } - } - - public class PanicException : UniffiException - { - public PanicException(string message) - : base(message) { } - } - - public class AllocationException : UniffiException - { - public AllocationException(string message) - : base(message) { } - } - - public class InternalException : UniffiException - { - public InternalException(string message) - : base(message) { } - } - - public class InvalidEnumException : InternalException - { - public InvalidEnumException(string message) - : base(message) { } - } - - public class UniffiContractVersionException : UniffiException - { - public UniffiContractVersionException(string message) - : base(message) { } - } - - public class UniffiContractChecksumException : UniffiException - { - public UniffiContractChecksumException(string message) - : base(message) { } - } - - // Each top-level error class has a companion object that can lift the error from the call status's rust buffer - interface CallStatusErrorHandler - where E : System.Exception - { - E Lift(RustBuffer error_buf); - } - - // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR - class NullCallStatusErrorHandler : CallStatusErrorHandler - { - public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); - - public UniffiException Lift(RustBuffer error_buf) - { - RustBuffer.Free(error_buf); - return new UndeclaredErrorException( - "library has returned an error not declared in UNIFFI interface file" - ); - } - } - - // Helpers for calling Rust - // In practice we usually need to be synchronized to call this safely, so it doesn't - // synchronize itself - class _UniffiHelpers - { - public delegate void RustCallAction(ref UniffiRustCallStatus status); - public delegate U RustCallFunc(ref UniffiRustCallStatus status); - - // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err - public static U RustCallWithError( - CallStatusErrorHandler errorHandler, - RustCallFunc callback - ) - where E : System.Exception - { - var status = new UniffiRustCallStatus(); - var return_value = callback(ref status); - if (status.IsSuccess()) - { - return return_value; - } - else if (status.IsError()) - { - throw errorHandler.Lift(status.error_buf); - } - else if (status.IsPanic()) - { - // when the rust code sees a panic, it tries to construct a rustbuffer - // with the message. but if that code panics, then it just sends back - // an empty buffer. - if (status.error_buf.len > 0) - { - throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); - } - else - { - throw new PanicException("Rust panic"); - } - } - else - { - throw new InternalException($"Unknown rust call status: {status.code}"); - } - } - - // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err - public static void RustCallWithError( - CallStatusErrorHandler errorHandler, - RustCallAction callback - ) - where E : System.Exception - { - _UniffiHelpers.RustCallWithError( - errorHandler, - (ref UniffiRustCallStatus status) => - { - callback(ref status); - return 0; - } - ); - } - - // Call a rust function that returns a plain value - public static U RustCall(RustCallFunc callback) - { - return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); - } - - // Call a rust function that returns a plain value - public static void RustCall(RustCallAction callback) - { - _UniffiHelpers.RustCall( - (ref UniffiRustCallStatus status) => - { - callback(ref status); - return 0; - } - ); - } - } - - static class FFIObjectUtil - { - public static void DisposeAll(params Object?[] list) - { - Dispose(list); - } - - // Dispose is implemented by recursive type inspection at runtime. This is because - // generating correct Dispose calls for recursive complex types, e.g. List> - // is quite cumbersome. - private static void Dispose(Object? obj) - { - if (obj == null) - { - return; - } - - if (obj is IDisposable disposable) - { - disposable.Dispose(); - return; - } - - var objType = obj.GetType(); - var typeCode = Type.GetTypeCode(objType); - if (typeCode != TypeCode.Object) - { - return; - } - - var genericArguments = objType.GetGenericArguments(); - if (genericArguments.Length == 0 && !objType.IsArray) - { - return; - } - - if (obj is System.Collections.IDictionary objDictionary) - { - //This extra code tests to not call "Dispose" for a Dictionary() - //for all values as "double" and alike doesn't support interface "IDisposable" - var valuesType = objType.GetGenericArguments()[1]; - var elementValuesTypeCode = Type.GetTypeCode(valuesType); - if (elementValuesTypeCode != TypeCode.Object) - { - return; - } - foreach (var value in objDictionary.Values) - { - Dispose(value); - } - } - else if (obj is System.Collections.IEnumerable listValues) - { - //This extra code tests to not call "Dispose" for a List() - //for all keys as "int" and alike doesn't support interface "IDisposable" - var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; - var elementValuesTypeCode = Type.GetTypeCode(elementType); - if (elementValuesTypeCode != TypeCode.Object) - { - return; - } - foreach (var value in listValues) - { - Dispose(value); - } - } - } - } - - // Big endian streams are not yet available in dotnet :'( - // https://github.com/dotnet/runtime/issues/26904 - - class StreamUnderflowException : System.Exception - { - public StreamUnderflowException() { } - } - - static class BigEndianStreamExtensions - { - public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) - { -#if NET8_0_OR_GREATER - Span buffer = stackalloc byte[bytesToWrite]; -#else - byte[] buffer = new byte[bytesToWrite]; -#endif - var posByte = bytesToWrite; - while (posByte != 0) - { - posByte--; - buffer[posByte] = (byte)(value); - value >>= 8; - } - -#if NET8_0_OR_GREATER - stream.Write(buffer); -#else - stream.Write(buffer, 0, buffer.Length); -#endif - } - - public static void WriteInt64(this Stream stream, long value) - { - int bytesToWrite = 8; -#if NET8_0_OR_GREATER - Span buffer = stackalloc byte[bytesToWrite]; -#else - byte[] buffer = new byte[bytesToWrite]; -#endif - var posByte = bytesToWrite; - while (posByte != 0) - { - posByte--; - buffer[posByte] = (byte)(value); - value >>= 8; - } - -#if NET8_0_OR_GREATER - stream.Write(buffer); -#else - stream.Write(buffer, 0, buffer.Length); -#endif - } - - public static uint ReadUint32(this Stream stream, int bytesToRead = 4) - { - CheckRemaining(stream, bytesToRead); -#if NET8_0_OR_GREATER - Span buffer = stackalloc byte[bytesToRead]; - stream.Read(buffer); -#else - byte[] buffer = new byte[bytesToRead]; - stream.Read(buffer, 0, bytesToRead); -#endif - uint result = 0; - uint digitMultiplier = 1; - int posByte = bytesToRead; - while (posByte != 0) - { - posByte--; - result |= buffer[posByte] * digitMultiplier; - digitMultiplier <<= 8; - } - - return result; - } - - public static ulong ReadUInt64(this Stream stream) - { - int bytesToRead = 8; - CheckRemaining(stream, bytesToRead); -#if NET8_0_OR_GREATER - Span buffer = stackalloc byte[bytesToRead]; - stream.Read(buffer); -#else - byte[] buffer = new byte[bytesToRead]; - stream.Read(buffer, 0, bytesToRead); -#endif - ulong result = 0; - ulong digitMultiplier = 1; - int posByte = bytesToRead; - while (posByte != 0) - { - posByte--; - result |= buffer[posByte] * digitMultiplier; - digitMultiplier <<= 8; - } - - return result; - } - - public static void CheckRemaining(this Stream stream, int length) - { - if (stream.Length - stream.Position < length) - { - throw new StreamUnderflowException(); - } - } - - public static void ForEach(this T[] items, Action action) - { - foreach (var item in items) - { - action(item); - } - } - } - - class BigEndianStream - { - Stream stream; - - public BigEndianStream(Stream stream) - { - this.stream = stream; - } - - public Stream InnerStream => stream; - - public bool HasRemaining() - { - return (stream.Length - Position) > 0; - } - - public long Position - { - get => stream.Position; - set => stream.Position = value; - } - - public void WriteBytes(byte[] buffer) - { -#if NET8_0_OR_GREATER - stream.Write(buffer); -#else - stream.Write(buffer, 0, buffer.Length); -#endif - } - - public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); - - public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); - - public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); - - public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); - - public void WriteUInt(uint value) => stream.WriteInt32((int)value); - - public void WriteInt(int value) => stream.WriteInt32(value); - - public void WriteULong(ulong value) => stream.WriteInt64((long)value); - - public void WriteLong(long value) => stream.WriteInt64(value); - - public void WriteFloat(float value) - { - unsafe - { - WriteInt(*((int*)&value)); - } - } - - public void WriteDouble(double value) => - stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); - - public byte[] ReadBytes(int length) - { - stream.CheckRemaining(length); - byte[] result = new byte[length]; - stream.Read(result, 0, length); - return result; - } - - public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); - - public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); - - public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); - - public ulong ReadULong() => stream.ReadUInt64(); - - public sbyte ReadSByte() => (sbyte)ReadByte(); - - public short ReadShort() => (short)ReadUShort(); - - public int ReadInt() => (int)ReadUInt(); - - public float ReadFloat() - { - unsafe - { - int value = ReadInt(); - return *((float*)&value); - } - } - - public long ReadLong() => (long)ReadULong(); - - public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); - } - - // Contains loading, initialization code, - // and the FFI Function declarations in a com.sun.jna.Library. - - // This is an implementation detail that will be called internally by the public API. -#if NET8_0_OR_GREATER - static partial class _UniFFILib - { -#else - static class _UniFFILib - { -#endif - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiCallbackInterfaceFree(ulong @handle); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureDroppedCallbackStruct - { - public ulong @handle; - public IntPtr @free; - } - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultU8 - { - public byte @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteU8( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultU8 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultI8 - { - public sbyte @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteI8( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultI8 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultU16 - { - public ushort @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteU16( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultU16 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultI16 - { - public short @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteI16( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultI16 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultU32 - { - public uint @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteU32( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultU32 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultI32 - { - public int @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteI32( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultI32 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultU64 - { - public ulong @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteU64( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultU64 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultI64 - { - public long @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteI64( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultI64 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultF32 - { - public float @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteF32( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultF32 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultF64 - { - public double @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteF64( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultF64 @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultRustBuffer - { - public RustBuffer @returnValue; - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteRustBuffer( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultRustBuffer @result - ); - - [StructLayout(LayoutKind.Sequential)] - public struct UniffiForeignFutureResultVoid - { - public UniffiRustCallStatus @callStatus; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UniffiForeignFutureCompleteVoid( - ulong @callbackData, - _UniFFILib.UniffiForeignFutureResultVoid @result - ); - - static _UniFFILib() - { - _UniFFILib.uniffiCheckContractApiVersion(); - _UniFFILib.uniffiCheckApiChecksums(); - } - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer uniffi_livekit_ffi_fn_func_build_version(ref UniffiRustCallStatus _uniffi_out_err); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer ffi_livekit_ffi_rustbuffer_alloc( - ulong @size, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer ffi_livekit_ffi_rustbuffer_from_bytes( - ForeignBytes @bytes, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rustbuffer_free(RustBuffer @buf, ref UniffiRustCallStatus _uniffi_out_err); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer ffi_livekit_ffi_rustbuffer_reserve( - RustBuffer @buf, - ulong @additional, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_u8(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_u8(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_u8(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - byte ffi_livekit_ffi_rust_future_complete_u8( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_i8(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_i8(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_i8(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - sbyte ffi_livekit_ffi_rust_future_complete_i8( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_u16(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_u16(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_u16(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - ushort ffi_livekit_ffi_rust_future_complete_u16( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_i16(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_i16(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_i16(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - short ffi_livekit_ffi_rust_future_complete_i16( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_u32(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_u32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_u32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - uint ffi_livekit_ffi_rust_future_complete_u32( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_i32(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_i32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_i32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - int ffi_livekit_ffi_rust_future_complete_i32( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_u64(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_u64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_u64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - ulong ffi_livekit_ffi_rust_future_complete_u64( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_i64(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_i64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_i64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - long ffi_livekit_ffi_rust_future_complete_i64( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_f32(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_f32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_f32(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - float ffi_livekit_ffi_rust_future_complete_f32( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_f64(ulong @handle, IntPtr @callback, ulong @callbackData); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_f64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_f64(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - double ffi_livekit_ffi_rust_future_complete_f64( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_rust_buffer( - ulong @handle, - IntPtr @callback, - ulong @callbackData - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_rust_buffer(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_rust_buffer(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer ffi_livekit_ffi_rust_future_complete_rust_buffer( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_poll_void( - ulong @handle, - IntPtr @callback, - ulong @callbackData - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_cancel_void(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_free_void(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_ffi_rust_future_complete_void( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err - ); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - ushort uniffi_livekit_ffi_checksum_func_build_version(); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_ffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_ffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - uint ffi_livekit_ffi_uniffi_contract_version(); - - static void uniffiCheckContractApiVersion() - { - var scaffolding_contract_version = _UniFFILib.ffi_livekit_ffi_uniffi_contract_version(); - if (30 != scaffolding_contract_version) - { - throw new UniffiContractVersionException( - $"LiveKit.Uniffi: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" - ); - } - } - - static void uniffiCheckApiChecksums() - { - { - var checksum = _UniFFILib.uniffi_livekit_ffi_checksum_func_build_version(); - if (checksum != 47150) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_ffi_checksum_func_build_version` checksum `47150`, library returned `{checksum}`" - ); - } - } - } - } - - // Public interface members begin here. - -#pragma warning disable 8625 - - class FfiConverterString : FfiConverter - { - public static FfiConverterString INSTANCE = new FfiConverterString(); - - // Note: we don't inherit from FfiConverterRustBuffer, because we use a - // special encoding when lowering/lifting. We can use `RustBuffer.len` to - // store our length and avoid writing it out to the buffer. - public override string Lift(RustBuffer value) - { - try - { - var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); - return System.Text.Encoding.UTF8.GetString(bytes); - } - finally - { - RustBuffer.Free(value); - } - } - - public override string Read(BigEndianStream stream) - { - var length = stream.ReadInt(); - var bytes = stream.ReadBytes(length); - return System.Text.Encoding.UTF8.GetString(bytes); - } - - public override RustBuffer Lower(string value) - { - var bytes = System.Text.Encoding.UTF8.GetBytes(value); - var rbuf = RustBuffer.Alloc(bytes.Length); - rbuf.AsWriteableStream().WriteBytes(bytes); - return rbuf; - } - - // TODO(CS) - // We aren't sure exactly how many bytes our string will be once it's UTF-8 - // encoded. Allocate 3 bytes per unicode codepoint which will always be - // enough. - public override int AllocationSize(string value) - { - const int sizeForLength = 4; - var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); - return sizeForLength + sizeForString; - } - - public override void Write(string value, BigEndianStream stream) - { - var bytes = System.Text.Encoding.UTF8.GetBytes(value); - stream.WriteInt(bytes.Length); - stream.WriteBytes(bytes); - } - } -#pragma warning restore 8625 - public static class LivekitFfiMethods - { - /// - /// Returns the version specified in the crate's Cargo.toml. - /// - /// This is the first function exposed over UniFFI from `livekit-ffi`. It carries - /// no server state and touches no handle store, so downstream SDKs can call it to - /// prove the UniFFI bindgen toolchain is wired up end-to-end. - /// - public static string BuildVersion() - { - return FfiConverterString.INSTANCE.Lift( - _UniffiHelpers.RustCall( - (ref UniffiRustCallStatus _status) => - _UniFFILib.uniffi_livekit_ffi_fn_func_build_version(ref _status) - ) - ); - } - } -} diff --git a/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta b/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta deleted file mode 100644 index b4f67d49..00000000 --- a/Runtime/Scripts/UniFFI/livekit_ffi.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b19de9f1c658c40cb89437a04ce766ce -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_uniffi.cs b/Runtime/Scripts/UniFFI/livekit_uniffi.cs new file mode 100644 index 00000000..aab88772 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_uniffi.cs @@ -0,0 +1,3460 @@ +// +// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py +// + +#nullable enable + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace LiveKit.Uniffi +{ + // This is a helper for safely working with byte buffers returned from the Rust code. + // A rust-owned buffer is represented by its capacity, its current length, and a + // pointer to the underlying data. + + [StructLayout(LayoutKind.Sequential)] + internal struct RustBuffer + { + public ulong capacity; + public ulong len; + public IntPtr data; + + public static RustBuffer Alloc(int size) + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + var buffer = _UniFFILib.ffi_livekit_uniffi_rustbuffer_alloc( + Convert.ToUInt64(size), + ref status + ); + if (buffer.data == IntPtr.Zero) + { + throw new AllocationException( + $"RustBuffer.Alloc() returned null data pointer (size={size})" + ); + } + return buffer; + } + ); + } + + public static void Free(RustBuffer buffer) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rustbuffer_free(buffer, ref status); + } + ); + } + + public static BigEndianStream MemoryStream(IntPtr data, long length) + { + unsafe + { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) + ); + } + } + + public BigEndianStream AsWriteableStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream( + (byte*)data.ToPointer(), + Convert.ToInt64(capacity), + Convert.ToInt64(capacity), + FileAccess.Write + ) + ); + } + } + } + + // This is a helper for safely passing byte references into the rust code. + // It's not actually used at the moment, because there aren't many things that you + // can take a direct pointer to managed memory, and if we're going to copy something + // then we might as well copy it into a `RustBuffer`. But it's here for API + // completeness. + + [StructLayout(LayoutKind.Sequential)] + internal struct ForeignBytes + { + public int length; + public IntPtr data; + } + + // The FfiConverter interface handles converter types to and from the FFI + // + // All implementing objects should be public to support external types. When a + // type is external we need to import it's FfiConverter. + internal abstract class FfiConverter + { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) + { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try + { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToUInt64(stream.Position); + return rbuf; + } + catch + { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) + { + var stream = rbuf.AsStream(); + try + { + var item = Read(stream); + if (stream.HasRemaining()) + { + throw new InternalException( + "junk remaining in buffer after lifting, something is very wrong!!" + ); + } + return item; + } + finally + { + RustBuffer.Free(rbuf); + } + } + } + + // FfiConverter that uses `RustBuffer` as the FfiType + internal abstract class FfiConverterRustBuffer : FfiConverter + { + public override CsType Lift(RustBuffer value) + { + return LiftFromRustBuffer(value); + } + + public override RustBuffer Lower(CsType value) + { + return LowerIntoRustBuffer(value); + } + } + + // A handful of classes and functions to support the generated data structures. + // This would be a good candidate for isolating in its own ffi-support lib. + // Error runtime. + [StructLayout(LayoutKind.Sequential)] + struct UniffiRustCallStatus + { + public sbyte code; + public RustBuffer error_buf; + + public bool IsSuccess() + { + return code == 0; + } + + public bool IsError() + { + return code == 1; + } + + public bool IsPanic() + { + return code == 2; + } + } + + // Base class for all uniffi exceptions + public class UniffiException : System.Exception + { + public UniffiException() + : base() { } + + public UniffiException(string message) + : base(message) { } + } + + public class UndeclaredErrorException : UniffiException + { + public UndeclaredErrorException(string message) + : base(message) { } + } + + public class PanicException : UniffiException + { + public PanicException(string message) + : base(message) { } + } + + public class AllocationException : UniffiException + { + public AllocationException(string message) + : base(message) { } + } + + public class InternalException : UniffiException + { + public InternalException(string message) + : base(message) { } + } + + public class InvalidEnumException : InternalException + { + public InvalidEnumException(string message) + : base(message) { } + } + + public class UniffiContractVersionException : UniffiException + { + public UniffiContractVersionException(string message) + : base(message) { } + } + + public class UniffiContractChecksumException : UniffiException + { + public UniffiContractChecksumException(string message) + : base(message) { } + } + + // Each top-level error class has a companion object that can lift the error from the call status's rust buffer + interface CallStatusErrorHandler + where E : System.Exception + { + E Lift(RustBuffer error_buf); + } + + // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + class NullCallStatusErrorHandler : CallStatusErrorHandler + { + public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); + + public UniffiException Lift(RustBuffer error_buf) + { + RustBuffer.Free(error_buf); + return new UndeclaredErrorException( + "library has returned an error not declared in UNIFFI interface file" + ); + } + } + + // Helpers for calling Rust + // In practice we usually need to be synchronized to call this safely, so it doesn't + // synchronize itself + class _UniffiHelpers + { + public delegate void RustCallAction(ref UniffiRustCallStatus status); + public delegate U RustCallFunc(ref UniffiRustCallStatus status); + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static U RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallFunc callback + ) + where E : System.Exception + { + var status = new UniffiRustCallStatus(); + var return_value = callback(ref status); + if (status.IsSuccess()) + { + return return_value; + } + else if (status.IsError()) + { + throw errorHandler.Lift(status.error_buf); + } + else if (status.IsPanic()) + { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) + { + throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); + } + else + { + throw new PanicException("Rust panic"); + } + } + else + { + throw new InternalException($"Unknown rust call status: {status.code}"); + } + } + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static void RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallAction callback + ) + where E : System.Exception + { + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + + // Call a rust function that returns a plain value + public static U RustCall(RustCallFunc callback) + { + return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); + } + + // Call a rust function that returns a plain value + public static void RustCall(RustCallAction callback) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + } + + static class FFIObjectUtil + { + public static void DisposeAll(params Object?[] list) + { + Dispose(list); + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(Object? obj) + { + if (obj == null) + { + return; + } + + if (obj is IDisposable disposable) + { + disposable.Dispose(); + return; + } + + var objType = obj.GetType(); + var typeCode = Type.GetTypeCode(objType); + if (typeCode != TypeCode.Object) + { + return; + } + + var genericArguments = objType.GetGenericArguments(); + if (genericArguments.Length == 0 && !objType.IsArray) + { + return; + } + + if (obj is System.Collections.IDictionary objDictionary) + { + //This extra code tests to not call "Dispose" for a Dictionary() + //for all values as "double" and alike doesn't support interface "IDisposable" + var valuesType = objType.GetGenericArguments()[1]; + var elementValuesTypeCode = Type.GetTypeCode(valuesType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in objDictionary.Values) + { + Dispose(value); + } + } + else if (obj is System.Collections.IEnumerable listValues) + { + //This extra code tests to not call "Dispose" for a List() + //for all keys as "int" and alike doesn't support interface "IDisposable" + var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; + var elementValuesTypeCode = Type.GetTypeCode(elementType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in listValues) + { + Dispose(value); + } + } + } + } + + // Big endian streams are not yet available in dotnet :'( + // https://github.com/dotnet/runtime/issues/26904 + + class StreamUnderflowException : System.Exception + { + public StreamUnderflowException() { } + } + + static class BigEndianStreamExtensions + { + public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static void WriteInt64(this Stream stream, long value) + { + int bytesToWrite = 8; +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static uint ReadUint32(this Stream stream, int bytesToRead = 4) + { + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + uint result = 0; + uint digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static ulong ReadUInt64(this Stream stream) + { + int bytesToRead = 8; + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + ulong result = 0; + ulong digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static void CheckRemaining(this Stream stream, int length) + { + if (stream.Length - stream.Position < length) + { + throw new StreamUnderflowException(); + } + } + + public static void ForEach(this T[] items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + } + + class BigEndianStream + { + Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public Stream InnerStream => stream; + + public bool HasRemaining() + { + return (stream.Length - Position) > 0; + } + + public long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] buffer) + { +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteUInt(uint value) => stream.WriteInt32((int)value); + + public void WriteInt(int value) => stream.WriteInt32(value); + + public void WriteULong(ulong value) => stream.WriteInt64((long)value); + + public void WriteLong(long value) => stream.WriteInt64(value); + + public void WriteFloat(float value) + { + unsafe + { + WriteInt(*((int*)&value)); + } + } + + public void WriteDouble(double value) => + stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); + + public byte[] ReadBytes(int length) + { + stream.CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); + + public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); + + public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); + + public ulong ReadULong() => stream.ReadUInt64(); + + public sbyte ReadSByte() => (sbyte)ReadByte(); + + public short ReadShort() => (short)ReadUShort(); + + public int ReadInt() => (int)ReadUInt(); + + public float ReadFloat() + { + unsafe + { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() => (long)ReadULong(); + + public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + + // This is an implementation detail that will be called internally by the public API. +#if NET8_0_OR_GREATER + static partial class _UniFFILib + { +#else + static class _UniFFILib + { +#endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceFree(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureDroppedCallbackStruct + { + public ulong @handle; + public IntPtr @free; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU8 + { + public byte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI8 + { + public sbyte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU16 + { + public ushort @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI16 + { + public short @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU32 + { + public uint @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI32 + { + public int @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU64 + { + public ulong @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI64 + { + public long @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF32 + { + public float @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF64 + { + public double @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultRustBuffer + { + public RustBuffer @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteRustBuffer( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultRustBuffer @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultVoid + { + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteVoid( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultVoid @result + ); + + static _UniFFILib() + { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + } + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_claims_from_unverified( + RustBuffer @token, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_generate( + RustBuffer @options, + RustBuffer @credentials, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_verify( + RustBuffer @token, + RustBuffer @credentials, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_build_version( + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_func_log_forward_bootstrap( + RustBuffer @level, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_func_log_forward_receive(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rustbuffer_free( + RustBuffer @buf, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_uniffi_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_uniffi_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_uniffi_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_uniffi_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_uniffi_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_uniffi_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_uniffi_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_uniffi_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_f32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_uniffi_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_f64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_uniffi_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_generate(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_verify(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_build_version(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_log_forward_receive(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_uniffi_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = _UniFFILib.ffi_livekit_uniffi_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"LiveKit.Uniffi: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() + { + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); + if (checksum != 50037) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified` checksum `50037`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_generate(); + if (checksum != 61939) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_generate` checksum `61939`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_verify(); + if (checksum != 22102) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_verify` checksum `22102`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_build_version(); + if (checksum != 21480) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_build_version` checksum `21480`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); + if (checksum != 25458) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap` checksum `25458`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_receive(); + if (checksum != 58503) + { + throw new UniffiContractChecksumException( + $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_receive` checksum `58503`, library returned `{checksum}`" + ); + } + } + } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterUInt32 : FfiConverter + { + public static FfiConverterUInt32 INSTANCE = new FfiConverterUInt32(); + + public override uint Lift(uint value) + { + return value; + } + + public override uint Read(BigEndianStream stream) + { + return stream.ReadUInt(); + } + + public override uint Lower(uint value) + { + return value; + } + + public override int AllocationSize(uint value) + { + return 4; + } + + public override void Write(uint value, BigEndianStream stream) + { + stream.WriteUInt(value); + } + } + + class FfiConverterInt32 : FfiConverter + { + public static FfiConverterInt32 INSTANCE = new FfiConverterInt32(); + + public override int Lift(int value) + { + return value; + } + + public override int Read(BigEndianStream stream) + { + return stream.ReadInt(); + } + + public override int Lower(int value) + { + return value; + } + + public override int AllocationSize(int value) + { + return 4; + } + + public override void Write(int value, BigEndianStream stream) + { + stream.WriteInt(value); + } + } + + class FfiConverterUInt64 : FfiConverter + { + public static FfiConverterUInt64 INSTANCE = new FfiConverterUInt64(); + + public override ulong Lift(ulong value) + { + return value; + } + + public override ulong Read(BigEndianStream stream) + { + return stream.ReadULong(); + } + + public override ulong Lower(ulong value) + { + return value; + } + + public override int AllocationSize(ulong value) + { + return 8; + } + + public override void Write(ulong value, BigEndianStream stream) + { + stream.WriteULong(value); + } + } + + class FfiConverterBoolean : FfiConverter + { + public static FfiConverterBoolean INSTANCE = new FfiConverterBoolean(); + + public override bool Lift(sbyte value) + { + return value != 0; + } + + public override bool Read(BigEndianStream stream) + { + return Lift(stream.ReadSByte()); + } + + public override sbyte Lower(bool value) + { + return value ? (sbyte)1 : (sbyte)0; + } + + public override int AllocationSize(bool value) + { + return (sbyte)1; + } + + public override void Write(bool value, BigEndianStream stream) + { + stream.WriteSByte(Lower(value)); + } + } + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } + + class FfiConverterDuration : FfiConverterRustBuffer + { + public static FfiConverterDuration INSTANCE = new FfiConverterDuration(); + + // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs + private const uint NanosecondsPerTick = 100; + + public override TimeSpan Read(BigEndianStream stream) + { + var seconds = stream.ReadULong(); + var nanoseconds = stream.ReadUInt(); + var ticks = seconds * TimeSpan.TicksPerSecond; + ticks += nanoseconds / NanosecondsPerTick; + return new TimeSpan(Convert.ToInt64(ticks)); + } + + public override int AllocationSize(TimeSpan value) + { + // 8 bytes for seconds, 4 bytes for nanoseconds + return 12; + } + + public override void Write(TimeSpan value, BigEndianStream stream) + { + stream.WriteULong(Convert.ToUInt64(value.Ticks / TimeSpan.TicksPerSecond)); + stream.WriteUInt( + Convert.ToUInt32(value.Ticks % TimeSpan.TicksPerSecond * NanosecondsPerTick) + ); + } + } + + /// + /// API credentials for access token generation and verification. + /// + public record ApiCredentials(string Key, string Secret) { } + + class FfiConverterTypeApiCredentials : FfiConverterRustBuffer + { + public static FfiConverterTypeApiCredentials INSTANCE = new FfiConverterTypeApiCredentials(); + + public override ApiCredentials Read(BigEndianStream stream) + { + return new ApiCredentials( + Key: FfiConverterString.INSTANCE.Read(stream), + Secret: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(ApiCredentials value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Key) + + FfiConverterString.INSTANCE.AllocationSize(value.Secret); + } + + public override void Write(ApiCredentials value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Key, stream); + FfiConverterString.INSTANCE.Write(value.Secret, stream); + } + } + + /// + /// Claims decoded from a valid access token. + /// + public record Claims( + ulong Exp, + string Iss, + ulong Nbf, + string Sub, + string Name, + VideoGrants Video, + SipGrants Sip, + string Sha256, + string Metadata, + Dictionary Attributes, + RoomConfiguration? RoomConfiguration + ) { } + + class FfiConverterTypeClaims : FfiConverterRustBuffer + { + public static FfiConverterTypeClaims INSTANCE = new FfiConverterTypeClaims(); + + public override Claims Read(BigEndianStream stream) + { + return new Claims( + Exp: FfiConverterUInt64.INSTANCE.Read(stream), + Iss: FfiConverterString.INSTANCE.Read(stream), + Nbf: FfiConverterUInt64.INSTANCE.Read(stream), + Sub: FfiConverterString.INSTANCE.Read(stream), + Name: FfiConverterString.INSTANCE.Read(stream), + Video: FfiConverterTypeVideoGrants.INSTANCE.Read(stream), + Sip: FfiConverterTypeSIPGrants.INSTANCE.Read(stream), + Sha256: FfiConverterString.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(Claims value) + { + return 0 + + FfiConverterUInt64.INSTANCE.AllocationSize(value.Exp) + + FfiConverterString.INSTANCE.AllocationSize(value.Iss) + + FfiConverterUInt64.INSTANCE.AllocationSize(value.Nbf) + + FfiConverterString.INSTANCE.AllocationSize(value.Sub) + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterTypeVideoGrants.INSTANCE.AllocationSize(value.Video) + + FfiConverterTypeSIPGrants.INSTANCE.AllocationSize(value.Sip) + + FfiConverterString.INSTANCE.AllocationSize(value.Sha256) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( + value.RoomConfiguration + ); + } + + public override void Write(Claims value, BigEndianStream stream) + { + FfiConverterUInt64.INSTANCE.Write(value.Exp, stream); + FfiConverterString.INSTANCE.Write(value.Iss, stream); + FfiConverterUInt64.INSTANCE.Write(value.Nbf, stream); + FfiConverterString.INSTANCE.Write(value.Sub, stream); + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterTypeVideoGrants.INSTANCE.Write(value.Video, stream); + FfiConverterTypeSIPGrants.INSTANCE.Write(value.Sip, stream); + FfiConverterString.INSTANCE.Write(value.Sha256, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + } + } + + public record LogForwardEntry( + LogForwardLevel Level, + string Target, + string? File, + uint? Line, + string Message + ) { } + + class FfiConverterTypeLogForwardEntry : FfiConverterRustBuffer + { + public static FfiConverterTypeLogForwardEntry INSTANCE = new FfiConverterTypeLogForwardEntry(); + + public override LogForwardEntry Read(BigEndianStream stream) + { + return new LogForwardEntry( + Level: FfiConverterTypeLogForwardLevel.INSTANCE.Read(stream), + Target: FfiConverterString.INSTANCE.Read(stream), + File: FfiConverterOptionalString.INSTANCE.Read(stream), + Line: FfiConverterOptionalUInt32.INSTANCE.Read(stream), + Message: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(LogForwardEntry value) + { + return 0 + + FfiConverterTypeLogForwardLevel.INSTANCE.AllocationSize(value.Level) + + FfiConverterString.INSTANCE.AllocationSize(value.Target) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.File) + + FfiConverterOptionalUInt32.INSTANCE.AllocationSize(value.Line) + + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(LogForwardEntry value, BigEndianStream stream) + { + FfiConverterTypeLogForwardLevel.INSTANCE.Write(value.Level, stream); + FfiConverterString.INSTANCE.Write(value.Target, stream); + FfiConverterOptionalString.INSTANCE.Write(value.File, stream); + FfiConverterOptionalUInt32.INSTANCE.Write(value.Line, stream); + FfiConverterString.INSTANCE.Write(value.Message, stream); + } + } + + /// + /// Agent dispatch configuration + /// + /// Defines which agents should be dispatched to a room. + /// + public record RoomAgentDispatch( + string AgentName, + string Metadata, + int RestartPolicy, + string Deployment, + Dictionary Attributes + ) { } + + class FfiConverterTypeRoomAgentDispatch : FfiConverterRustBuffer + { + public static FfiConverterTypeRoomAgentDispatch INSTANCE = + new FfiConverterTypeRoomAgentDispatch(); + + public override RoomAgentDispatch Read(BigEndianStream stream) + { + return new RoomAgentDispatch( + AgentName: FfiConverterString.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + RestartPolicy: FfiConverterInt32.INSTANCE.Read(stream), + Deployment: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(RoomAgentDispatch value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.AgentName) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterInt32.INSTANCE.AllocationSize(value.RestartPolicy) + + FfiConverterString.INSTANCE.AllocationSize(value.Deployment) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes); + } + + public override void Write(RoomAgentDispatch value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.AgentName, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterInt32.INSTANCE.Write(value.RestartPolicy, stream); + FfiConverterString.INSTANCE.Write(value.Deployment, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + } + } + + /// + /// Room configuration + /// + /// Configuration options for a room. + /// + public record RoomConfiguration( + string Name, + uint EmptyTimeout, + uint DepartureTimeout, + uint MaxParticipants, + string Metadata, + uint MinPlayoutDelay, + uint MaxPlayoutDelay, + bool SyncStreams, + RoomAgentDispatch[] Agents + ) { } + + class FfiConverterTypeRoomConfiguration : FfiConverterRustBuffer + { + public static FfiConverterTypeRoomConfiguration INSTANCE = + new FfiConverterTypeRoomConfiguration(); + + public override RoomConfiguration Read(BigEndianStream stream) + { + return new RoomConfiguration( + Name: FfiConverterString.INSTANCE.Read(stream), + EmptyTimeout: FfiConverterUInt32.INSTANCE.Read(stream), + DepartureTimeout: FfiConverterUInt32.INSTANCE.Read(stream), + MaxParticipants: FfiConverterUInt32.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + MinPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), + MaxPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), + SyncStreams: FfiConverterBoolean.INSTANCE.Read(stream), + Agents: FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(RoomConfiguration value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.EmptyTimeout) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.DepartureTimeout) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxParticipants) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MinPlayoutDelay) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxPlayoutDelay) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.SyncStreams) + + FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.AllocationSize(value.Agents); + } + + public override void Write(RoomConfiguration value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterUInt32.INSTANCE.Write(value.EmptyTimeout, stream); + FfiConverterUInt32.INSTANCE.Write(value.DepartureTimeout, stream); + FfiConverterUInt32.INSTANCE.Write(value.MaxParticipants, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterUInt32.INSTANCE.Write(value.MinPlayoutDelay, stream); + FfiConverterUInt32.INSTANCE.Write(value.MaxPlayoutDelay, stream); + FfiConverterBoolean.INSTANCE.Write(value.SyncStreams, stream); + FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Write(value.Agents, stream); + } + } + + /// + /// SIP grants + /// + /// Maps to the JWT's `sip` field. + /// + public record SipGrants(bool Admin, bool Call) { } + + class FfiConverterTypeSIPGrants : FfiConverterRustBuffer + { + public static FfiConverterTypeSIPGrants INSTANCE = new FfiConverterTypeSIPGrants(); + + public override SipGrants Read(BigEndianStream stream) + { + return new SipGrants( + Admin: FfiConverterBoolean.INSTANCE.Read(stream), + Call: FfiConverterBoolean.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(SipGrants value) + { + return 0 + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Admin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Call); + } + + public override void Write(SipGrants value, BigEndianStream stream) + { + FfiConverterBoolean.INSTANCE.Write(value.Admin, stream); + FfiConverterBoolean.INSTANCE.Write(value.Call, stream); + } + } + + /// + /// Options used for generating an access token. + /// + /// Any fields left empty will use the token generator's defaults. + /// + public record TokenOptions( + TimeSpan? Ttl = null, + VideoGrants? VideoGrants = null, + SipGrants? SipGrants = null, + string? Identity = null, + string? Name = null, + string? Metadata = null, + Dictionary? Attributes = null, + string? Sha256 = null, + RoomConfiguration? RoomConfiguration = null + ) { } + + class FfiConverterTypeTokenOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeTokenOptions INSTANCE = new FfiConverterTypeTokenOptions(); + + public override TokenOptions Read(BigEndianStream stream) + { + return new TokenOptions( + Ttl: FfiConverterOptionalDuration.INSTANCE.Read(stream), + VideoGrants: FfiConverterOptionalTypeVideoGrants.INSTANCE.Read(stream), + SipGrants: FfiConverterOptionalTypeSIPGrants.INSTANCE.Read(stream), + Identity: FfiConverterOptionalString.INSTANCE.Read(stream), + Name: FfiConverterOptionalString.INSTANCE.Read(stream), + Metadata: FfiConverterOptionalString.INSTANCE.Read(stream), + Attributes: FfiConverterOptionalDictionaryStringString.INSTANCE.Read(stream), + Sha256: FfiConverterOptionalString.INSTANCE.Read(stream), + RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(TokenOptions value) + { + return 0 + + FfiConverterOptionalDuration.INSTANCE.AllocationSize(value.Ttl) + + FfiConverterOptionalTypeVideoGrants.INSTANCE.AllocationSize(value.VideoGrants) + + FfiConverterOptionalTypeSIPGrants.INSTANCE.AllocationSize(value.SipGrants) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Identity) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Name) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterOptionalDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Sha256) + + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( + value.RoomConfiguration + ); + } + + public override void Write(TokenOptions value, BigEndianStream stream) + { + FfiConverterOptionalDuration.INSTANCE.Write(value.Ttl, stream); + FfiConverterOptionalTypeVideoGrants.INSTANCE.Write(value.VideoGrants, stream); + FfiConverterOptionalTypeSIPGrants.INSTANCE.Write(value.SipGrants, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Identity, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Name, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Metadata, stream); + FfiConverterOptionalDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Sha256, stream); + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + } + } + + /// + /// Room permissions + /// + /// Maps to the JWT's `video` field. + /// + public record VideoGrants( + bool RoomCreate, + bool RoomList, + bool RoomRecord, + bool RoomAdmin, + bool RoomJoin, + string Room, + string DestinationRoom, + bool CanPublish, + bool CanSubscribe, + bool CanPublishData, + string[] CanPublishSources, + bool CanUpdateOwnMetadata, + bool IngressAdmin, + bool Hidden, + bool Recorder + ) { } + + class FfiConverterTypeVideoGrants : FfiConverterRustBuffer + { + public static FfiConverterTypeVideoGrants INSTANCE = new FfiConverterTypeVideoGrants(); + + public override VideoGrants Read(BigEndianStream stream) + { + return new VideoGrants( + RoomCreate: FfiConverterBoolean.INSTANCE.Read(stream), + RoomList: FfiConverterBoolean.INSTANCE.Read(stream), + RoomRecord: FfiConverterBoolean.INSTANCE.Read(stream), + RoomAdmin: FfiConverterBoolean.INSTANCE.Read(stream), + RoomJoin: FfiConverterBoolean.INSTANCE.Read(stream), + Room: FfiConverterString.INSTANCE.Read(stream), + DestinationRoom: FfiConverterString.INSTANCE.Read(stream), + CanPublish: FfiConverterBoolean.INSTANCE.Read(stream), + CanSubscribe: FfiConverterBoolean.INSTANCE.Read(stream), + CanPublishData: FfiConverterBoolean.INSTANCE.Read(stream), + CanPublishSources: FfiConverterSequenceString.INSTANCE.Read(stream), + CanUpdateOwnMetadata: FfiConverterBoolean.INSTANCE.Read(stream), + IngressAdmin: FfiConverterBoolean.INSTANCE.Read(stream), + Hidden: FfiConverterBoolean.INSTANCE.Read(stream), + Recorder: FfiConverterBoolean.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(VideoGrants value) + { + return 0 + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomCreate) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomList) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomRecord) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomAdmin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomJoin) + + FfiConverterString.INSTANCE.AllocationSize(value.Room) + + FfiConverterString.INSTANCE.AllocationSize(value.DestinationRoom) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublish) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanSubscribe) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublishData) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.CanPublishSources) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanUpdateOwnMetadata) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.IngressAdmin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Hidden) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Recorder); + } + + public override void Write(VideoGrants value, BigEndianStream stream) + { + FfiConverterBoolean.INSTANCE.Write(value.RoomCreate, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomList, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomRecord, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomAdmin, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomJoin, stream); + FfiConverterString.INSTANCE.Write(value.Room, stream); + FfiConverterString.INSTANCE.Write(value.DestinationRoom, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanPublish, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanSubscribe, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanPublishData, stream); + FfiConverterSequenceString.INSTANCE.Write(value.CanPublishSources, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanUpdateOwnMetadata, stream); + FfiConverterBoolean.INSTANCE.Write(value.IngressAdmin, stream); + FfiConverterBoolean.INSTANCE.Write(value.Hidden, stream); + FfiConverterBoolean.INSTANCE.Write(value.Recorder, stream); + } + } + + /// + /// An error that can occur during token generation or verification. + /// + public class AccessTokenException : UniffiException + { + AccessTokenException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class InvalidKeys : AccessTokenException + { + public InvalidKeys(string message) + : base(message) { } + } + + public class InvalidEnv : AccessTokenException + { + public InvalidEnv(string message) + : base(message) { } + } + + public class InvalidClaims : AccessTokenException + { + public InvalidClaims(string message) + : base(message) { } + } + + public class Encoding : AccessTokenException + { + public Encoding(string message) + : base(message) { } + } + } + + class FfiConverterTypeAccessTokenError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeAccessTokenError INSTANCE = + new FfiConverterTypeAccessTokenError(); + + public override AccessTokenException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new AccessTokenException.InvalidKeys( + FfiConverterString.INSTANCE.Read(stream) + ); + case 2: + return new AccessTokenException.InvalidEnv( + FfiConverterString.INSTANCE.Read(stream) + ); + case 3: + return new AccessTokenException.InvalidClaims( + FfiConverterString.INSTANCE.Read(stream) + ); + case 4: + return new AccessTokenException.Encoding(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(AccessTokenException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(AccessTokenException value, BigEndianStream stream) + { + switch (value) + { + case AccessTokenException.InvalidKeys: + stream.WriteInt(1); + break; + case AccessTokenException.InvalidEnv: + stream.WriteInt(2); + break; + case AccessTokenException.InvalidClaims: + stream.WriteInt(3); + break; + case AccessTokenException.Encoding: + stream.WriteInt(4); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Write()", + value + ) + ); + } + } + } + + public enum LogForwardFilter : int + { + Off, + Error, + Warn, + Info, + Debug, + Trace, + } + + class FfiConverterTypeLogForwardFilter : FfiConverterRustBuffer + { + public static FfiConverterTypeLogForwardFilter INSTANCE = + new FfiConverterTypeLogForwardFilter(); + + public override LogForwardFilter Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return LogForwardFilter.Off; + case 2: + return LogForwardFilter.Error; + case 3: + return LogForwardFilter.Warn; + case 4: + return LogForwardFilter.Info; + case 5: + return LogForwardFilter.Debug; + case 6: + return LogForwardFilter.Trace; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Read()", + value + ) + ); + } + } + + public override int AllocationSize(LogForwardFilter value) + { + return 4; + } + + public override void Write(LogForwardFilter value, BigEndianStream stream) + { + switch (value) + { + case LogForwardFilter.Off: + stream.WriteInt(1); + break; + case LogForwardFilter.Error: + stream.WriteInt(2); + break; + case LogForwardFilter.Warn: + stream.WriteInt(3); + break; + case LogForwardFilter.Info: + stream.WriteInt(4); + break; + case LogForwardFilter.Debug: + stream.WriteInt(5); + break; + case LogForwardFilter.Trace: + stream.WriteInt(6); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Write()", + value + ) + ); + } + } + } + + public enum LogForwardLevel : int + { + Error, + Warn, + Info, + Debug, + Trace, + } + + class FfiConverterTypeLogForwardLevel : FfiConverterRustBuffer + { + public static FfiConverterTypeLogForwardLevel INSTANCE = new FfiConverterTypeLogForwardLevel(); + + public override LogForwardLevel Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return LogForwardLevel.Error; + case 2: + return LogForwardLevel.Warn; + case 3: + return LogForwardLevel.Info; + case 4: + return LogForwardLevel.Debug; + case 5: + return LogForwardLevel.Trace; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Read()", + value + ) + ); + } + } + + public override int AllocationSize(LogForwardLevel value) + { + return 4; + } + + public override void Write(LogForwardLevel value, BigEndianStream stream) + { + switch (value) + { + case LogForwardLevel.Error: + stream.WriteInt(1); + break; + case LogForwardLevel.Warn: + stream.WriteInt(2); + break; + case LogForwardLevel.Info: + stream.WriteInt(3); + break; + case LogForwardLevel.Debug: + stream.WriteInt(4); + break; + case LogForwardLevel.Trace: + stream.WriteInt(5); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Write()", + value + ) + ); + } + } + } + + class FfiConverterOptionalUInt32 : FfiConverterRustBuffer + { + public static FfiConverterOptionalUInt32 INSTANCE = new FfiConverterOptionalUInt32(); + + public override uint? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterUInt32.INSTANCE.Read(stream); + } + + public override int AllocationSize(uint? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterUInt32.INSTANCE.AllocationSize((uint)value); + } + } + + public override void Write(uint? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterUInt32.INSTANCE.Write((uint)value, stream); + } + } + } + + class FfiConverterOptionalString : FfiConverterRustBuffer + { + public static FfiConverterOptionalString INSTANCE = new FfiConverterOptionalString(); + + public override string? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterString.INSTANCE.Read(stream); + } + + public override int AllocationSize(string? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterString.INSTANCE.AllocationSize((string)value); + } + } + + public override void Write(string? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterString.INSTANCE.Write((string)value, stream); + } + } + } + + class FfiConverterOptionalDuration : FfiConverterRustBuffer + { + public static FfiConverterOptionalDuration INSTANCE = new FfiConverterOptionalDuration(); + + public override TimeSpan? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterDuration.INSTANCE.Read(stream); + } + + public override int AllocationSize(TimeSpan? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterDuration.INSTANCE.AllocationSize((TimeSpan)value); + } + } + + public override void Write(TimeSpan? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterDuration.INSTANCE.Write((TimeSpan)value, stream); + } + } + } + + class FfiConverterOptionalTypeApiCredentials : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeApiCredentials INSTANCE = + new FfiConverterOptionalTypeApiCredentials(); + + public override ApiCredentials? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeApiCredentials.INSTANCE.Read(stream); + } + + public override int AllocationSize(ApiCredentials? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeApiCredentials.INSTANCE.AllocationSize((ApiCredentials)value); + } + } + + public override void Write(ApiCredentials? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeApiCredentials.INSTANCE.Write((ApiCredentials)value, stream); + } + } + } + + class FfiConverterOptionalTypeLogForwardEntry : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeLogForwardEntry INSTANCE = + new FfiConverterOptionalTypeLogForwardEntry(); + + public override LogForwardEntry? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeLogForwardEntry.INSTANCE.Read(stream); + } + + public override int AllocationSize(LogForwardEntry? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeLogForwardEntry.INSTANCE.AllocationSize((LogForwardEntry)value); + } + } + + public override void Write(LogForwardEntry? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeLogForwardEntry.INSTANCE.Write((LogForwardEntry)value, stream); + } + } + } + + class FfiConverterOptionalTypeRoomConfiguration : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeRoomConfiguration INSTANCE = + new FfiConverterOptionalTypeRoomConfiguration(); + + public override RoomConfiguration? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeRoomConfiguration.INSTANCE.Read(stream); + } + + public override int AllocationSize(RoomConfiguration? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeRoomConfiguration.INSTANCE.AllocationSize( + (RoomConfiguration)value + ); + } + } + + public override void Write(RoomConfiguration? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeRoomConfiguration.INSTANCE.Write((RoomConfiguration)value, stream); + } + } + } + + class FfiConverterOptionalTypeSIPGrants : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeSIPGrants INSTANCE = + new FfiConverterOptionalTypeSIPGrants(); + + public override SipGrants? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeSIPGrants.INSTANCE.Read(stream); + } + + public override int AllocationSize(SipGrants? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterTypeSIPGrants.INSTANCE.AllocationSize((SipGrants)value); + } + } + + public override void Write(SipGrants? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeSIPGrants.INSTANCE.Write((SipGrants)value, stream); + } + } + } + + class FfiConverterOptionalTypeVideoGrants : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeVideoGrants INSTANCE = + new FfiConverterOptionalTypeVideoGrants(); + + public override VideoGrants? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeVideoGrants.INSTANCE.Read(stream); + } + + public override int AllocationSize(VideoGrants? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterTypeVideoGrants.INSTANCE.AllocationSize((VideoGrants)value); + } + } + + public override void Write(VideoGrants? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeVideoGrants.INSTANCE.Write((VideoGrants)value, stream); + } + } + } + + class FfiConverterOptionalDictionaryStringString + : FfiConverterRustBuffer?> + { + public static FfiConverterOptionalDictionaryStringString INSTANCE = + new FfiConverterOptionalDictionaryStringString(); + + public override Dictionary? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterDictionaryStringString.INSTANCE.Read(stream); + } + + public override int AllocationSize(Dictionary? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize( + (Dictionary)value + ); + } + } + + public override void Write(Dictionary? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterDictionaryStringString.INSTANCE.Write( + (Dictionary)value, + stream + ); + } + } + } + + class FfiConverterSequenceString : FfiConverterRustBuffer + { + public static FfiConverterSequenceString INSTANCE = new FfiConverterSequenceString(); + + public override string[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + if (length == 0) + { + return System.Array.Empty(); + } + + var result = new string[length]; + for (int i = 0; i < length; i++) + { + result[i] = FfiConverterString.INSTANCE.Read(stream); + } + return result; + } + + public override int AllocationSize(string[] value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => FfiConverterString.INSTANCE.AllocationSize(item)); + return sizeForLength + sizeForItems; + } + + public override void Write(string[] value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Length); + value.ForEach(item => FfiConverterString.INSTANCE.Write(item, stream)); + } + } + + class FfiConverterSequenceTypeRoomAgentDispatch : FfiConverterRustBuffer + { + public static FfiConverterSequenceTypeRoomAgentDispatch INSTANCE = + new FfiConverterSequenceTypeRoomAgentDispatch(); + + public override RoomAgentDispatch[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + if (length == 0) + { + return System.Array.Empty(); + } + + var result = new RoomAgentDispatch[length]; + for (int i = 0; i < length; i++) + { + result[i] = FfiConverterTypeRoomAgentDispatch.INSTANCE.Read(stream); + } + return result; + } + + public override int AllocationSize(RoomAgentDispatch[] value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => FfiConverterTypeRoomAgentDispatch.INSTANCE.AllocationSize(item)); + return sizeForLength + sizeForItems; + } + + public override void Write(RoomAgentDispatch[] value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Length); + value.ForEach(item => FfiConverterTypeRoomAgentDispatch.INSTANCE.Write(item, stream)); + } + } + + class FfiConverterDictionaryStringString : FfiConverterRustBuffer> + { + public static FfiConverterDictionaryStringString INSTANCE = + new FfiConverterDictionaryStringString(); + + public override Dictionary Read(BigEndianStream stream) + { + var len = stream.ReadInt(); + var result = new Dictionary(len); + for (int i = 0; i < len; i++) + { + var key = FfiConverterString.INSTANCE.Read(stream); + var value = FfiConverterString.INSTANCE.Read(stream); + result[key] = value; + } + + return result; + } + + public override int AllocationSize(Dictionary value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => + FfiConverterString.INSTANCE.AllocationSize(item.Key) + FfiConverterString.INSTANCE.AllocationSize(item.Value) + ); + return sizeForLength + sizeForItems; + } + + public override void Write(Dictionary value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Count); + foreach (var item in value) + { + FfiConverterString.INSTANCE.Write(item.Key, stream); + FfiConverterString.INSTANCE.Write(item.Value, stream); + } + } + } + + class ConcurrentHandleMap + where T : notnull + { + readonly ConcurrentDictionary _map = new(); + + // Handles are odd numbers (1, 3, 5, ...) — the lowest bit must always be set. + // Rust uses (handle & 1) to distinguish foreign-language handles from Rust Arc + // pointers, which are always even due to memory alignment. See uniffi_core/src/ffi/handle.rs. + const long HANDLE_INITIAL = 1; + const long HANDLE_DELTA = 2; + long _currentHandle = HANDLE_INITIAL - HANDLE_DELTA; + + public ulong Insert(T obj) + { + var handle = (ulong)Interlocked.Add(ref _currentHandle, HANDLE_DELTA); + if (!_map.TryAdd(handle, obj)) + { + throw new InternalException("ConcurrentHandleMap: Duplicate handle"); + } + return handle; + } + + public bool TryGet(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryGetValue(handle, out result); + } + + public T Get(ulong handle) + { + if (_map.TryGetValue(handle, out var result)) + { + return result; + } + else + { + throw new InternalException("ConcurrentHandleMap: Invalid handle"); + } + } + + public bool Remove(ulong handle) + { + return _map.TryRemove(handle, out _); + } + + public bool Remove(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryRemove(handle, out result); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void UniFfiFutureCallback(ulong continuationHandle, byte pollResult); + + internal sealed class UniffiForeignFutureHandle : System.IDisposable + { + internal CancellationTokenSource Cts { get; } = new CancellationTokenSource(); +#if NET9_0_OR_GREATER + private readonly Lock _lock = new Lock(); +#else + private readonly object _lock = new object(); +#endif + + internal void MarkDropped() + { + lock (_lock) + { + if (!_callbackInvoked) + { + Cts.Cancel(); + } + } + } + + private bool _callbackInvoked = false; + + internal void InvokeCallbackOnce(Action invoke) + { + bool shouldInvoke; + lock (_lock) + { + shouldInvoke = !_callbackInvoked; + if (shouldInvoke) + _callbackInvoked = true; + } + if (shouldInvoke) + invoke(); + } + + // Dispose() is called exactly once — from the worker's finally (Task.Run path) or from the + // synchronous early-return path — always after InvokeCallbackOnce has set _callbackInvoked = true. + public void Dispose() + { + Cts.Dispose(); + } + } + + internal static class _UniFFIAsync + { + internal const byte UNIFFI_RUST_FUTURE_POLL_READY = 0; + + // internal const byte UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1; + + internal static ConcurrentHandleMap> _async_handle_map = + new ConcurrentHandleMap>(); + public static ConcurrentHandleMap _foreign_futures_map = + new ConcurrentHandleMap(); + + // FFI type for Rust future continuations + internal class UniffiRustFutureContinuationCallback + { + public static UniFfiFutureCallback callback = Callback; + + public static void Callback(ulong continuationHandle, byte pollResult) + { + if (_async_handle_map.Remove(continuationHandle, out TaskCompletionSource? task)) + { + task.SetResult(pollResult); + } + // else: continuation already completed (e.g. waker called more than once), ignore + } + } + + public class UniffiForeignFutureDroppedCallbackImpl + { + public static _UniFFILib.UniffiForeignFutureDroppedCallback callback = Callback; + + public static void Callback(ulong handle) + { + if ( + _foreign_futures_map.Remove(handle, out UniffiForeignFutureHandle? futureHandle) + && futureHandle is not null + ) + { + futureHandle.MarkDropped(); + } + } + } + + public delegate F CompleteFuncDelegate(ulong handle, ref UniffiRustCallStatus status); + + public delegate void CompleteActionDelegate(ulong handle, ref UniffiRustCallStatus status); + + private static async Task PollFuture(ulong rustFuture, Action pollFunc) + { + byte pollResult; + do + { + var tcs = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously + ); + IntPtr callback = Marshal.GetFunctionPointerForDelegate( + UniffiRustFutureContinuationCallback.callback + ); + ulong mapEntry = _async_handle_map.Insert(tcs); + pollFunc(rustFuture, callback, mapEntry); + pollResult = await tcs.Task; + } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY); + } + + public static async Task UniffiRustCallAsync( + ulong rustFuture, + Action pollFunc, + CompleteFuncDelegate completeFunc, + Action freeFunc, + Func liftFunc, + CallStatusErrorHandler errorHandler + ) + where E : System.Exception + { + try + { + await PollFuture(rustFuture, pollFunc); + var result = _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => completeFunc(rustFuture, ref status) + ); + return liftFunc(result); + } + finally + { + freeFunc(rustFuture); + } + } + + public static async Task UniffiRustCallAsync( + ulong rustFuture, + Action pollFunc, + CompleteActionDelegate completeFunc, + Action freeFunc, + CallStatusErrorHandler errorHandler + ) + where E : System.Exception + { + try + { + await PollFuture(rustFuture, pollFunc); + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => completeFunc(rustFuture, ref status) + ); + } + finally + { + freeFunc(rustFuture); + } + } + } +#pragma warning restore 8625 + public static class LivekitUniffiMethods + { + /// + /// Parses an access token without verifying its signature. + /// + /// This is useful when you want to inspect token contents without having the secret. + /// The token's expiration (exp) and not-before (nbf) times are still validated. + /// WARNING: Do not use this for authentication - the signature is not verified! + /// + /// + public static Claims TokenClaimsFromUnverified(string @token) + { + return FfiConverterTypeClaims.INSTANCE.Lift( + _UniffiHelpers.RustCallWithError( + FfiConverterTypeAccessTokenError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_token_claims_from_unverified( + FfiConverterString.INSTANCE.Lower(@token), + ref _status + ) + ) + ); + } + + /// + /// Generates an access token. + /// + /// If `credentials` are omitted, API key and secret will be read from the environment + /// variables `LIVEKIT_API_KEY` and `LIVEKIT_SECRET` respectively. + /// + /// + public static string TokenGenerate(TokenOptions @options, ApiCredentials? @credentials) + { + return FfiConverterString.INSTANCE.Lift( + _UniffiHelpers.RustCallWithError( + FfiConverterTypeAccessTokenError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_token_generate( + FfiConverterTypeTokenOptions.INSTANCE.Lower(@options), + FfiConverterOptionalTypeApiCredentials.INSTANCE.Lower(@credentials), + ref _status + ) + ) + ); + } + + /// + /// Verifies an access token. + /// + /// If `credentials` are omitted, API key and secret will be read from the environment + /// variables `LIVEKIT_API_KEY` and `LIVEKIT_SECRET` respectively. + /// + /// + public static Claims TokenVerify(string @token, ApiCredentials? @credentials) + { + return FfiConverterTypeClaims.INSTANCE.Lift( + _UniffiHelpers.RustCallWithError( + FfiConverterTypeAccessTokenError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_token_verify( + FfiConverterString.INSTANCE.Lower(@token), + FfiConverterOptionalTypeApiCredentials.INSTANCE.Lower(@credentials), + ref _status + ) + ) + ); + } + + /// + /// Returns the version specified in the crate's Cargo.toml. + /// + public static string BuildVersion() + { + return FfiConverterString.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_build_version(ref _status) + ) + ); + } + + /// + /// Bootstraps log forwarding. + /// + /// Generally, you will invoke this once early in program execution. However, + /// subsequent invocations are allowed to change the log level. + /// + public static void LogForwardBootstrap(LogForwardFilter @level) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_log_forward_bootstrap( + FfiConverterTypeLogForwardFilter.INSTANCE.Lower(@level), + ref _status + ) + ); + } + + /// + /// Asynchronously receives a forwarded log entry. + /// + /// Invoke repeatedly to receive log entries as they are produced + /// until `None` is returned, indicating forwarding has ended. Clients will + /// likely want to bridge this to the languages's equivalent of an asynchronous stream. + /// + public static async Task LogForwardReceive() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + _UniFFILib.uniffi_livekit_uniffi_fn_func_log_forward_receive(), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalTypeLogForwardEntry.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } +} diff --git a/Samples~/Meet/Assets/Runtime/MeetManager.cs b/Samples~/Meet/Assets/Runtime/MeetManager.cs index 3b69b189..eba391c2 100644 --- a/Samples~/Meet/Assets/Runtime/MeetManager.cs +++ b/Samples~/Meet/Assets/Runtime/MeetManager.cs @@ -201,7 +201,7 @@ private void OnToggleMicrophone() private void OnPublishData() { Debug.Log($"Published Data"); - Debug.Log(LivekitFfiMethods.BuildVersion()); + Debug.Log(LivekitUniffiMethods.BuildVersion()); } #endregion diff --git a/Scripts~/build_ffi_locally.sh b/Scripts~/build_ffi_locally.sh index 6a87b518..658a36c7 100755 --- a/Scripts~/build_ffi_locally.sh +++ b/Scripts~/build_ffi_locally.sh @@ -5,12 +5,6 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" MANIFEST="$ROOT/client-sdk-rust~/Cargo.toml" BASE_DST="$ROOT/Runtime/Plugins" BASE_TARGET="$ROOT/client-sdk-rust~/target" -UNIFFI_OUT_DIR="$ROOT/Runtime/Scripts/UniFFI" -UNIFFI_CONFIG="$SCRIPT_DIR/uniffi/uniffi.toml" -# uniffi-bindgen-cs release. The "+vX.Y.Z" suffix is the uniffi version it targets -# and must match the uniffi version used by livekit-ffi in client-sdk-rust~. -UNIFFI_BINDGEN_CS_REPO="https://github.com/NordSecurity/uniffi-bindgen-cs" -UNIFFI_BINDGEN_CS_TAG="v0.11.0+v0.31.0" RED='\033[0;31m' YELLOW='\033[0;33m' @@ -37,8 +31,8 @@ usage() { echo " release Optimized release build" echo " debug Debug build" echo "" - echo "macOS builds also regenerate the UniFFI C# bindings in Runtime/Scripts/UniFFI" - echo "(requires uniffi-bindgen-cs) and post-process them for C# 9." + echo "After macOS builds, the UniFFI C# bindings in Runtime/Scripts/UniFFI are regenerated" + echo "from the new library with generate_uniffi_bindings.sh (requires uniffi-bindgen-cs)." exit 1 } @@ -178,40 +172,10 @@ if [ "$PLATFORM" = "android" ]; then fi fi -# For macOS, regenerate the UniFFI C# bindings from the freshly built dylib and -# post-process them for C# 9 (Unity). uniffi-bindgen-cs resolves the crate config -# via `cargo metadata`, so it has to run from inside the Rust workspace. +# For macOS, regenerate the UniFFI C# bindings from the freshly built dylib. if [ "$PLATFORM" = "macos" ]; then echo "" - if ! command -v uniffi-bindgen-cs > /dev/null 2>&1; then - echo -e "${YELLOW}uniffi-bindgen-cs not found, skipping C# binding generation. Install it with:${RESET}" - echo " cargo install uniffi-bindgen-cs --git $UNIFFI_BINDGEN_CS_REPO --tag $UNIFFI_BINDGEN_CS_TAG" - else - INSTALLED_TAG="v$(uniffi-bindgen-cs --version | awk '{print $2}')" - if [ "$INSTALLED_TAG" != "$UNIFFI_BINDGEN_CS_TAG" ]; then - echo -e "${YELLOW}WARNING: uniffi-bindgen-cs $INSTALLED_TAG is installed, expected $UNIFFI_BINDGEN_CS_TAG (must match the uniffi version of livekit-ffi).${RESET}" - fi - - echo "Generating C# bindings into $UNIFFI_OUT_DIR..." - mkdir -p "$UNIFFI_OUT_DIR" - pushd "$ROOT/client-sdk-rust~" > /dev/null - uniffi-bindgen-cs --library "$SRC" --config "$UNIFFI_CONFIG" --out-dir "$UNIFFI_OUT_DIR" - BINDGEN_STATUS=$? - popd > /dev/null - - if [ $BINDGEN_STATUS -ne 0 ]; then - echo -e "${RED}uniffi-bindgen-cs failed.${RESET}" - exit 1 - fi - - # uniffi-bindgen-cs exits 0 without writing anything if the lib carries no UniFFI metadata - if [ -z "$(find "$UNIFFI_OUT_DIR" -maxdepth 1 -name '*.cs' -newer "$SRC")" ]; then - echo -e "${YELLOW}WARNING: No bindings were written. $(basename "$SRC") contains no UniFFI metadata; is client-sdk-rust~ on a commit where livekit-ffi exports UniFFI?${RESET}" - else - python3 "$SCRIPT_DIR/uniffi/downgrade_uniffi_bindings.py" "$UNIFFI_OUT_DIR" || exit 1 - echo -e "${GREEN}Generated C# bindings successfully.${RESET}" - fi - fi + "$SCRIPT_DIR/generate_uniffi_bindings.sh" "$SRC" || exit 1 echo "" echo -e "${YELLOW}WARNING: QUIT UNITY TO LOAD NEW LIB${RESET}" diff --git a/Scripts~/build_uniffi_locally.sh b/Scripts~/build_uniffi_locally.sh new file mode 100755 index 00000000..1453679c --- /dev/null +++ b/Scripts~/build_uniffi_locally.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Builds the experimental livekit-uniffi crate (client-sdk-rust~/livekit-uniffi), installs the +# resulting liblivekit_uniffi.dylib next to liblivekit_ffi.dylib in Runtime/Plugins and +# regenerates the UniFFI C# bindings from it with generate_uniffi_bindings.sh. +# +# The build is the plain-cargo equivalent of `cargo make build` in the crate's Makefile.toml, +# with a selectable build type instead of the release-only cargo-make task. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +MANIFEST="$ROOT/client-sdk-rust~/Cargo.toml" +BASE_DST="$ROOT/Runtime/Plugins" +BASE_TARGET="$ROOT/client-sdk-rust~/target" + +RED='\033[0;31m' +YELLOW='\033[0;33m' +GREEN='\033[0;32m' +RESET='\033[0m' + +# Prefer rustup-managed toolchains over any other rust in PATH (e.g. a Homebrew +# rust in /opt/homebrew/bin). The wrong rustc ignores rust-toolchain.toml and +# ships only its host target, so cross builds fail with "can't find crate for std". +CARGO_BIN="${CARGO_HOME:-$HOME/.cargo}/bin" +if [ -x "$CARGO_BIN/rustup" ]; then + export PATH="$CARGO_BIN:$PATH" +fi + +usage() { + echo "Usage: $0 [build_type]" + echo "" + echo "Platforms:" + echo " macos Build for aarch64-apple-darwin" + echo "" + echo "Build types (optional, defaults to 'debug'):" + echo " release Optimized release build" + echo " debug Debug build" + echo "" + echo "After the build, the UniFFI C# bindings in Runtime/Scripts/UniFFI are regenerated" + echo "from the new library with generate_uniffi_bindings.sh (requires uniffi-bindgen-cs)." + exit 1 +} + +if [ $# -lt 1 ] || [ $# -gt 2 ]; then + echo -e "${RED}Error: Expected one or two arguments.${RESET}" + usage +fi + +PLATFORM="$1" +BUILD_TYPE="${2:-debug}" + +case "$BUILD_TYPE" in + release) + BUILD_FLAG="--release" + BUILD_DIR="release" + ;; + debug) + BUILD_FLAG="" + BUILD_DIR="debug" + ;; + *) + echo -e "${RED}Error: Unknown build type '$BUILD_TYPE'. Expected 'release' or 'debug'.${RESET}" + usage + ;; +esac + +case "$PLATFORM" in + # MACOS + macos) + echo "Building livekit-uniffi for macOS (aarch64-apple-darwin) [$BUILD_TYPE]..." + pushd "$ROOT/client-sdk-rust~" > /dev/null + rustup target add aarch64-apple-darwin + cargo build \ + --manifest-path "$MANIFEST" \ + $BUILD_FLAG \ + -p livekit-uniffi \ + --target aarch64-apple-darwin + BUILD_STATUS=$? + popd > /dev/null + + SRC="$BASE_TARGET/aarch64-apple-darwin/$BUILD_DIR/liblivekit_uniffi.dylib" + DST="$BASE_DST/ffi-macos-arm64/liblivekit_uniffi.dylib" + ;; + *) + echo -e "${RED}Error: Unknown platform '$PLATFORM'.${RESET}" + usage + ;; +esac + +if [ $BUILD_STATUS -ne 0 ]; then + echo -e "${RED}Build failed. Aborting copy.${RESET}" + exit 1 +fi + +# Copy a built artifact into the package by writing a temp file next to the +# destination and renaming it into place, instead of overwriting in place. +# macOS caches code-signature state per inode: overwriting a signed dylib that a +# running Unity editor still has mapped makes every later load of that path die +# with SIGKILL "Code Signature Invalid" until the file gets a new inode. +install_file() { + local src="$1" dst="$2" tmp="$2.tmp" + if ! cp -f "$src" "$tmp" || ! mv -f "$tmp" "$dst"; then + rm -f "$tmp" + return 1 + fi +} + +# Copy the built lib +echo "" +echo "Copying to $DST..." +install_file "$SRC" "$DST" + +if [ $? -eq 0 ]; then + echo -e "${GREEN}Copied $(basename "$DST") successfully.${RESET}" +else + echo -e "${RED}Failed to copy $(basename "$DST"). Check that the source file exists and the destination directory is writable.${RESET}" + exit 1 +fi + +# Regenerate the UniFFI C# bindings from the freshly built dylib. +echo "" +"$SCRIPT_DIR/generate_uniffi_bindings.sh" "$SRC" || exit 1 + +echo "" +echo -e "${YELLOW}WARNING: QUIT UNITY TO LOAD NEW LIB${RESET}" diff --git a/Scripts~/generate_uniffi_bindings.sh b/Scripts~/generate_uniffi_bindings.sh new file mode 100755 index 00000000..fc788a3c --- /dev/null +++ b/Scripts~/generate_uniffi_bindings.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Regenerates the UniFFI C# bindings in Runtime/Scripts/UniFFI from a native library that +# carries UniFFI metadata and post-processes them for C# 9 (Unity). +# +# Usage: generate_uniffi_bindings.sh [LIBRARY] +# LIBRARY dylib to read the UniFFI metadata from. Defaults to the macOS +# liblivekit_uniffi.dylib installed in Runtime/Plugins. +# +# Called by build_uniffi_locally.sh and build_ffi_locally.sh after a macOS build. Run it on +# its own to regenerate the bindings from the installed library without rebuilding. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +RUST_ROOT="$ROOT/client-sdk-rust~" +OUT_DIR="$ROOT/Runtime/Scripts/UniFFI" +UNIFFI_CONFIG="$SCRIPT_DIR/uniffi/uniffi.toml" +DOWNGRADE="$SCRIPT_DIR/uniffi/downgrade_uniffi_bindings.py" +DEFAULT_LIBRARY="$ROOT/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib" +# uniffi-bindgen-cs release. The "+vX.Y.Z" suffix is the uniffi version it targets and must +# match the uniffi version used by the crate the library was built from (client-sdk-rust~). +UNIFFI_BINDGEN_CS_REPO="https://github.com/NordSecurity/uniffi-bindgen-cs" +UNIFFI_BINDGEN_CS_TAG="v0.11.0+v0.31.0" + +RED='\033[0;31m' +YELLOW='\033[0;33m' +GREEN='\033[0;32m' +RESET='\033[0m' + +usage() { + echo "Usage: $0 [LIBRARY]" + echo "" + echo " LIBRARY Native library with UniFFI metadata to generate the C# bindings from." + echo " Defaults to $DEFAULT_LIBRARY" + exit 1 +} + +if [ $# -gt 1 ]; then + echo -e "${RED}Error: Expected at most one argument.${RESET}" + usage +fi + +LIBRARY="${1:-$DEFAULT_LIBRARY}" +# uniffi-bindgen-cs runs from inside the Rust workspace below, so the path must be absolute. +case "$LIBRARY" in + /*) ;; + *) LIBRARY="$PWD/$LIBRARY" ;; +esac + +if [ ! -f "$LIBRARY" ]; then + echo -e "${RED}Error: Library not found: $LIBRARY${RESET}" + exit 1 +fi + +if ! command -v uniffi-bindgen-cs > /dev/null 2>&1; then + echo -e "${RED}Error: uniffi-bindgen-cs not found. Install it with:${RESET}" + echo " cargo install uniffi-bindgen-cs --git $UNIFFI_BINDGEN_CS_REPO --tag $UNIFFI_BINDGEN_CS_TAG" + exit 1 +fi + +INSTALLED_TAG="v$(uniffi-bindgen-cs --version | awk '{print $2}')" +if [ "$INSTALLED_TAG" != "$UNIFFI_BINDGEN_CS_TAG" ]; then + echo -e "${YELLOW}WARNING: uniffi-bindgen-cs $INSTALLED_TAG is installed, expected $UNIFFI_BINDGEN_CS_TAG (must match the uniffi version of the Rust crate).${RESET}" +fi + +# Generate into a scratch directory first so that a library without UniFFI metadata (bindgen +# then exits 0 without writing anything) or a failing post-processing step cannot leave +# Runtime/Scripts/UniFFI half-updated or empty. +TMP_OUT="$(mktemp -d)" +trap 'rm -rf "$TMP_OUT"' EXIT + +echo "Generating C# bindings from $LIBRARY..." +# uniffi-bindgen-cs resolves the crate's own uniffi.toml via `cargo metadata`, so it has to +# run from inside the Rust workspace. +pushd "$RUST_ROOT" > /dev/null +uniffi-bindgen-cs --library "$LIBRARY" --config "$UNIFFI_CONFIG" --out-dir "$TMP_OUT" +BINDGEN_STATUS=$? +popd > /dev/null + +if [ $BINDGEN_STATUS -ne 0 ]; then + echo -e "${RED}uniffi-bindgen-cs failed.${RESET}" + exit 1 +fi + +if [ -z "$(find "$TMP_OUT" -maxdepth 1 -name '*.cs')" ]; then + echo -e "${YELLOW}WARNING: No bindings were written. $(basename "$LIBRARY") contains no UniFFI metadata; existing bindings in $OUT_DIR left untouched.${RESET}" + exit 0 +fi + +# --no-polyfill: the Runtime assembly already defines the IsExternalInit marker type that +# records / init accessors need (Runtime/Scripts/Internal/IsExternalInit.cs). +python3 "$DOWNGRADE" --no-polyfill "$TMP_OUT" || exit 1 + +# Replace the generated files: drop every previously generated .cs, move the new set in and +# remove .meta files whose .cs is gone. Regenerated files keep their .meta (and GUID); Unity +# creates .meta files for new ones on import. +mkdir -p "$OUT_DIR" +rm -f "$OUT_DIR"/*.cs +mv "$TMP_OUT"/*.cs "$OUT_DIR"/ +for meta in "$OUT_DIR"/*.cs.meta; do + [ -e "$meta" ] || continue + [ -e "${meta%.meta}" ] || rm -f "$meta" +done + +echo -e "${GREEN}Generated C# bindings in $OUT_DIR:${RESET}" +ls -1 "$OUT_DIR"/*.cs diff --git a/Scripts~/uniffi/downgrade_uniffi_bindings.py b/Scripts~/uniffi/downgrade_uniffi_bindings.py index c6e64d4d..47e63da7 100755 --- a/Scripts~/uniffi/downgrade_uniffi_bindings.py +++ b/Scripts~/uniffi/downgrade_uniffi_bindings.py @@ -20,10 +20,14 @@ custom type mapping in uniffi.toml instead. Usage: - downgrade_uniffi_bindings.py [...] + downgrade_uniffi_bindings.py [--no-polyfill] [...] Directories are scanned for *.cs files (non-recursive). The script is idempotent: files that are already downgraded are left untouched. + +--no-polyfill skips the `IsExternalInit.cs` polyfill (and removes one written +earlier) for assemblies that already define the marker type; the Unity SDK +ships it in Runtime/Scripts/Internal/IsExternalInit.cs. """ import re @@ -216,12 +220,14 @@ def sync_polyfill(directory, needed): def main(argv): - if len(argv) < 2: + no_polyfill = "--no-polyfill" in argv[1:] + targets = [arg for arg in argv[1:] if arg != "--no-polyfill"] + if not targets or any(arg.startswith("--") for arg in targets): print(__doc__.strip()) return 2 status = 0 - for arg in argv[1:]: + for arg in targets: target = Path(arg) if target.is_dir(): directory = target @@ -245,7 +251,7 @@ def main(argv): polyfill_needed |= needs_polyfill print(f" {'downgraded' if changed else 'unchanged '} {path}") - sync_polyfill(directory, polyfill_needed) + sync_polyfill(directory, polyfill_needed and not no_polyfill) return status diff --git a/client-sdk-rust~ b/client-sdk-rust~ index 4dfca31a..267ebb84 160000 --- a/client-sdk-rust~ +++ b/client-sdk-rust~ @@ -1 +1 @@ -Subproject commit 4dfca31ae81939e40e8d15fe4dca0922ed5e687a +Subproject commit 267ebb84a0c2e1b3f68f5af8a27f0f7d92b6a7ff From 22af1ead395aa4a6b07456a5d540869bfa37dbc6 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:05:34 +0200 Subject: [PATCH 4/7] Uniffi crate compiles in Unity after two issues found and workarounds in post processing script --- AGENTS.md | 13 +- README.md | 4 +- .../ffi-macos-arm64/liblivekit_uniffi.dylib | 4 +- .../liblivekit_uniffi.dylib.meta | 33 + Runtime/Scripts/UniFFI/livekit_common.cs | 1613 ++ Runtime/Scripts/UniFFI/livekit_common.cs.meta | 11 + Runtime/Scripts/UniFFI/livekit_datatrack.cs | 3465 ++++ .../Scripts/UniFFI/livekit_datatrack.cs.meta | 11 + Runtime/Scripts/UniFFI/livekit_net.cs | 4459 ++++++ Runtime/Scripts/UniFFI/livekit_net.cs.meta | 11 + Runtime/Scripts/UniFFI/livekit_uniffi.cs | 13369 ++++++++++++++-- Runtime/Scripts/UniFFI/livekit_uniffi.cs.meta | 11 + Samples~/Meet/Assets/Runtime/MeetManager.cs | 2 +- Scripts~/build_uniffi_locally.sh | 22 +- Scripts~/generate_uniffi_bindings.sh | 8 +- Scripts~/uniffi/downgrade_uniffi_bindings.py | 224 +- Scripts~/uniffi/uniffi.toml | 26 +- client-sdk-rust~ | 2 +- 18 files changed, 22326 insertions(+), 962 deletions(-) create mode 100644 Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_common.cs create mode 100644 Runtime/Scripts/UniFFI/livekit_common.cs.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_datatrack.cs create mode 100644 Runtime/Scripts/UniFFI/livekit_datatrack.cs.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_net.cs create mode 100644 Runtime/Scripts/UniFFI/livekit_net.cs.meta create mode 100644 Runtime/Scripts/UniFFI/livekit_uniffi.cs.meta diff --git a/AGENTS.md b/AGENTS.md index 589a81dd..452fb8a9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ Scripts~/build_uniffi_locally.sh macos [build_type] # Build types: debug (default), release ``` - Plain-cargo equivalent of `cargo make build` in the crate's `Makefile.toml`, with a selectable build type -- Installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64/`, then regenerates the UniFFI C# bindings from it with `Scripts~/generate_uniffi_bindings.sh` +- Regenerates the UniFFI C# bindings from the built dylib with `Scripts~/generate_uniffi_bindings.sh` first and installs the dylib next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64/` only if that succeeded, so plugin and bindings never get out of sync (a mismatched pair throws in the bindings' checksum check on first use) - After the build, Unity must be restarted to load the new dylib ### Generate the UniFFI C# bindings @@ -45,14 +45,15 @@ Scripts~/build_uniffi_locally.sh macos [build_type] # Defaults to the installed Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib Scripts~/generate_uniffi_bindings.sh [library] ``` -- Runs `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of the Rust crate) from inside `client-sdk-rust~` because bindgen needs `cargo metadata`; configured by `Scripts~/uniffi/uniffi.toml` (namespace `LiveKit.Uniffi`, public API types) -- Post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` for C# 9, then replaces the `.cs` files in `Runtime/Scripts/UniFFI/` and removes `.meta` files of bindings that no longer exist. The folder always reflects the last library it was generated from; a library without UniFFI metadata leaves it untouched (with a warning) +- Runs `uniffi-bindgen-cs` (release tag pinned in the script; must match the uniffi version of the Rust crate) from inside `client-sdk-rust~` because bindgen needs `cargo metadata`; configured by `Scripts~/uniffi/uniffi.toml` (public API types, `Vec` custom types mapped to `System.ReadOnlyMemory`; deliberately no namespace override, so every crate in the library keeps its default `uniffi.` namespace and the per-crate copies of the UniFFI runtime don't collide) +- Post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` for C# 9 and applies its clearly marked uniffi-bindgen-cs workarounds (bug reports in `Scripts~/uniffi/UPSTREAM-*.md`), then replaces the `.cs` files in `Runtime/Scripts/UniFFI/` and removes `.meta` files of bindings that no longer exist. The folder always reflects the last library it was generated from; a library without UniFFI metadata leaves it untouched (with a warning) ### Other scripts in `Scripts~/` - `download_libs.py` — downloads the prebuilt FFI binaries for all platforms; the release tag is pinned in `version.ini` - `generate_proto.sh` — regenerates `Runtime/Scripts/Proto/` from the protobuf definitions in `client-sdk-rust~/livekit-ffi/protocol` (requires `protoc`) -- `uniffi/uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (C# namespace, access modifier); not auto-discovered, so keep it in sync with `generate_uniffi_bindings.sh` -- `uniffi/downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present unless `--no-polyfill`); idempotent, called by `generate_uniffi_bindings.sh` with `--no-polyfill` because the Runtime assembly already ships `Internal/IsExternalInit.cs` +- `uniffi/uniffi.toml` — uniffi-bindgen-cs config passed via `--config` (access modifier, custom type mappings; no namespace, see the file); not auto-discovered, so keep it in sync with `generate_uniffi_bindings.sh` +- `uniffi/downgrade_uniffi_bindings.py` — rewrites uniffi-bindgen-cs output for C# 9 in place (block-scoped namespace, inlined method-group locals, `Array.Empty()` for `return []`, `IsExternalInit.cs` polyfill when records/`init` are present unless `--no-polyfill`); idempotent, called by `generate_uniffi_bindings.sh` with `--no-polyfill` because the Runtime assembly already ships `Internal/IsExternalInit.cs`. Also carries two clearly marked workarounds (`WORKAROUND(uniffi-bindgen-cs)`) for bugs in uniffi-bindgen-cs output that spans several crates; remove them once a fixed release is pinned +- `uniffi/UPSTREAM-*.md` — bug reports for those two uniffi-bindgen-cs issues (external custom-type alias missing in consuming crates; external object converters passed the wrong crate's `BigEndianStream`), ready to file at NordSecurity; `uniffi/upstream-repro/issue1/` and `issue2/` are the generic, self-contained reproductions they point to (each: two crates, a .NET project, `build.sh`) - `build_docs.sh`, `prepare_release.py`, `unity_test_results_utils.py` — docs generation, release preparation, CI test-result parsing ### Run tests @@ -81,7 +82,7 @@ The SDK wraps a Rust native library (`liblivekit_ffi`) via P/Invoke. The communi - `TokenSource/` — token generation/fetching helpers and MonoBehaviour component - `UniTask/` — optional UniTask integration (own asmdef: `livekit.unity.Runtime.UniTask.asmdef`) - `Internal/`, `Proto/` — FFI plumbing and generated protobuf code -- `UniFFI/` — generated C# bindings for the UniFFI surface of the native library, currently `liblivekit_uniffi` from the experimental `livekit-uniffi` crate (namespace `LiveKit.Uniffi`; uniffi-bindgen-cs output, post-processed for C# 9) +- `UniFFI/` — generated C# bindings for the UniFFI surface of the native library, currently `liblivekit_uniffi` from the experimental `livekit-uniffi` crate; one file and namespace per crate (`uniffi.livekit_uniffi`, `uniffi.livekit_common`, `uniffi.livekit_net`, `uniffi.livekit_datatrack`; uniffi-bindgen-cs output, post-processed for C# 9) Key internal files: - `Internal/FFI/FFIClient.cs` — singleton managing request/response lifecycle with Rust via protobuf diff --git a/README.md b/README.md index 49761e2f..a96d324d 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ The experimental [livekit-uniffi](https://github.com/livekit/rust-sdks/tree/main `./Scripts~/build_uniffi_locally.sh macos [debug|release]` -It installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64` and then regenerates the C# bindings as described below. +It regenerates the C# bindings from the built library as described below and, only if that succeeds, installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime/Plugins/ffi-macos-arm64`. ### Generating UniFFI C# bindings @@ -97,7 +97,7 @@ It installs `liblivekit_uniffi.dylib` next to `liblivekit_ffi.dylib` in `Runtime `cargo install uniffi-bindgen-cs --git https://github.com/NordSecurity/uniffi-bindgen-cs --tag v0.11.0+v0.31.0` -The generator is configured by `Scripts~/uniffi/uniffi.toml` (C# namespace `LiveKit.Uniffi`, public API types). uniffi-bindgen-cs emits C# 10+ syntax, so the script post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler before replacing the files in `Runtime/Scripts/UniFFI`. +The generator is configured by `Scripts~/uniffi/uniffi.toml` (public API types, custom type mappings). The library contains several Rust crates and each keeps its default namespace, e.g. `uniffi.livekit_uniffi`. uniffi-bindgen-cs emits C# 10+ syntax, so the script post-processes the output with `Scripts~/uniffi/downgrade_uniffi_bindings.py` to keep it compatible with Unity's C# 9 compiler before replacing the files in `Runtime/Scripts/UniFFI`. That script also applies two clearly marked workarounds for uniffi-bindgen-cs bugs in multi-crate output; the bug reports for upstream are in `Scripts~/uniffi/UPSTREAM-*.md`. ### VSCode setup diff --git a/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib index 58144111..cf188a4c 100755 --- a/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib +++ b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d7ad0a8501c760db077813cdffb5927e064489e3738ba5c675adc28fa886e40 -size 4199592 +oid sha256:5f2fc47986856a85aeb0a17b551058e995dd3f0155c692ed980c83e9bfd138e2 +size 1447280 diff --git a/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib.meta b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib.meta new file mode 100644 index 00000000..f570951d --- /dev/null +++ b/Runtime/Plugins/ffi-macos-arm64/liblivekit_uniffi.dylib.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4979a6f1c3bff4908b6d324068c51ac2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + DefaultValueInitialized: true + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_common.cs b/Runtime/Scripts/UniFFI/livekit_common.cs new file mode 100644 index 00000000..11b58fa5 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_common.cs @@ -0,0 +1,1613 @@ +// +// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py +// + +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace uniffi.livekit_common +{ + using Bytes = System.ReadOnlyMemory; + + // This is a helper for safely working with byte buffers returned from the Rust code. + // A rust-owned buffer is represented by its capacity, its current length, and a + // pointer to the underlying data. + + [StructLayout(LayoutKind.Sequential)] + internal struct RustBuffer + { + public ulong capacity; + public ulong len; + public IntPtr data; + + public static RustBuffer Alloc(int size) + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + var buffer = _UniFFILib.ffi_livekit_common_rustbuffer_alloc( + Convert.ToUInt64(size), + ref status + ); + if (buffer.data == IntPtr.Zero) + { + throw new AllocationException( + $"RustBuffer.Alloc() returned null data pointer (size={size})" + ); + } + return buffer; + } + ); + } + + public static void Free(RustBuffer buffer) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_common_rustbuffer_free(buffer, ref status); + } + ); + } + + public static BigEndianStream MemoryStream(IntPtr data, long length) + { + unsafe + { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) + ); + } + } + + public BigEndianStream AsWriteableStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream( + (byte*)data.ToPointer(), + Convert.ToInt64(capacity), + Convert.ToInt64(capacity), + FileAccess.Write + ) + ); + } + } + } + + // This is a helper for safely passing byte references into the rust code. + // It's not actually used at the moment, because there aren't many things that you + // can take a direct pointer to managed memory, and if we're going to copy something + // then we might as well copy it into a `RustBuffer`. But it's here for API + // completeness. + + [StructLayout(LayoutKind.Sequential)] + internal struct ForeignBytes + { + public int length; + public IntPtr data; + } + + // The FfiConverter interface handles converter types to and from the FFI + // + // All implementing objects should be public to support external types. When a + // type is external we need to import it's FfiConverter. + internal abstract class FfiConverter + { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) + { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try + { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToUInt64(stream.Position); + return rbuf; + } + catch + { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) + { + var stream = rbuf.AsStream(); + try + { + var item = Read(stream); + if (stream.HasRemaining()) + { + throw new InternalException( + "junk remaining in buffer after lifting, something is very wrong!!" + ); + } + return item; + } + finally + { + RustBuffer.Free(rbuf); + } + } + } + + // FfiConverter that uses `RustBuffer` as the FfiType + internal abstract class FfiConverterRustBuffer : FfiConverter + { + public override CsType Lift(RustBuffer value) + { + return LiftFromRustBuffer(value); + } + + public override RustBuffer Lower(CsType value) + { + return LowerIntoRustBuffer(value); + } + } + + // A handful of classes and functions to support the generated data structures. + // This would be a good candidate for isolating in its own ffi-support lib. + // Error runtime. + [StructLayout(LayoutKind.Sequential)] + struct UniffiRustCallStatus + { + public sbyte code; + public RustBuffer error_buf; + + public bool IsSuccess() + { + return code == 0; + } + + public bool IsError() + { + return code == 1; + } + + public bool IsPanic() + { + return code == 2; + } + } + + // Base class for all uniffi exceptions + public class UniffiException : System.Exception + { + public UniffiException() + : base() { } + + public UniffiException(string message) + : base(message) { } + } + + public class UndeclaredErrorException : UniffiException + { + public UndeclaredErrorException(string message) + : base(message) { } + } + + public class PanicException : UniffiException + { + public PanicException(string message) + : base(message) { } + } + + public class AllocationException : UniffiException + { + public AllocationException(string message) + : base(message) { } + } + + public class InternalException : UniffiException + { + public InternalException(string message) + : base(message) { } + } + + public class InvalidEnumException : InternalException + { + public InvalidEnumException(string message) + : base(message) { } + } + + public class UniffiContractVersionException : UniffiException + { + public UniffiContractVersionException(string message) + : base(message) { } + } + + public class UniffiContractChecksumException : UniffiException + { + public UniffiContractChecksumException(string message) + : base(message) { } + } + + // Each top-level error class has a companion object that can lift the error from the call status's rust buffer + interface CallStatusErrorHandler + where E : System.Exception + { + E Lift(RustBuffer error_buf); + } + + // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + class NullCallStatusErrorHandler : CallStatusErrorHandler + { + public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); + + public UniffiException Lift(RustBuffer error_buf) + { + RustBuffer.Free(error_buf); + return new UndeclaredErrorException( + "library has returned an error not declared in UNIFFI interface file" + ); + } + } + + // Helpers for calling Rust + // In practice we usually need to be synchronized to call this safely, so it doesn't + // synchronize itself + class _UniffiHelpers + { + public delegate void RustCallAction(ref UniffiRustCallStatus status); + public delegate U RustCallFunc(ref UniffiRustCallStatus status); + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static U RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallFunc callback + ) + where E : System.Exception + { + var status = new UniffiRustCallStatus(); + var return_value = callback(ref status); + if (status.IsSuccess()) + { + return return_value; + } + else if (status.IsError()) + { + throw errorHandler.Lift(status.error_buf); + } + else if (status.IsPanic()) + { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) + { + throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); + } + else + { + throw new PanicException("Rust panic"); + } + } + else + { + throw new InternalException($"Unknown rust call status: {status.code}"); + } + } + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static void RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallAction callback + ) + where E : System.Exception + { + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + + // Call a rust function that returns a plain value + public static U RustCall(RustCallFunc callback) + { + return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); + } + + // Call a rust function that returns a plain value + public static void RustCall(RustCallAction callback) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + } + + static class FFIObjectUtil + { + public static void DisposeAll(params Object?[] list) + { + Dispose(list); + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(Object? obj) + { + if (obj == null) + { + return; + } + + if (obj is IDisposable disposable) + { + disposable.Dispose(); + return; + } + + var objType = obj.GetType(); + var typeCode = Type.GetTypeCode(objType); + if (typeCode != TypeCode.Object) + { + return; + } + + var genericArguments = objType.GetGenericArguments(); + if (genericArguments.Length == 0 && !objType.IsArray) + { + return; + } + + if (obj is System.Collections.IDictionary objDictionary) + { + //This extra code tests to not call "Dispose" for a Dictionary() + //for all values as "double" and alike doesn't support interface "IDisposable" + var valuesType = objType.GetGenericArguments()[1]; + var elementValuesTypeCode = Type.GetTypeCode(valuesType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in objDictionary.Values) + { + Dispose(value); + } + } + else if (obj is System.Collections.IEnumerable listValues) + { + //This extra code tests to not call "Dispose" for a List() + //for all keys as "int" and alike doesn't support interface "IDisposable" + var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; + var elementValuesTypeCode = Type.GetTypeCode(elementType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in listValues) + { + Dispose(value); + } + } + } + } + + // Big endian streams are not yet available in dotnet :'( + // https://github.com/dotnet/runtime/issues/26904 + + class StreamUnderflowException : System.Exception + { + public StreamUnderflowException() { } + } + + static class BigEndianStreamExtensions + { + public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static void WriteInt64(this Stream stream, long value) + { + int bytesToWrite = 8; +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static uint ReadUint32(this Stream stream, int bytesToRead = 4) + { + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + uint result = 0; + uint digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static ulong ReadUInt64(this Stream stream) + { + int bytesToRead = 8; + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + ulong result = 0; + ulong digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static void CheckRemaining(this Stream stream, int length) + { + if (stream.Length - stream.Position < length) + { + throw new StreamUnderflowException(); + } + } + + public static void ForEach(this T[] items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + } + + class BigEndianStream + { + Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public Stream InnerStream => stream; + + public bool HasRemaining() + { + return (stream.Length - Position) > 0; + } + + public long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] buffer) + { +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteUInt(uint value) => stream.WriteInt32((int)value); + + public void WriteInt(int value) => stream.WriteInt32(value); + + public void WriteULong(ulong value) => stream.WriteInt64((long)value); + + public void WriteLong(long value) => stream.WriteInt64(value); + + public void WriteFloat(float value) + { + unsafe + { + WriteInt(*((int*)&value)); + } + } + + public void WriteDouble(double value) => + stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); + + public byte[] ReadBytes(int length) + { + stream.CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); + + public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); + + public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); + + public ulong ReadULong() => stream.ReadUInt64(); + + public sbyte ReadSByte() => (sbyte)ReadByte(); + + public short ReadShort() => (short)ReadUShort(); + + public int ReadInt() => (int)ReadUInt(); + + public float ReadFloat() + { + unsafe + { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() => (long)ReadULong(); + + public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + + // This is an implementation detail that will be called internally by the public API. +#if NET8_0_OR_GREATER + static partial class _UniFFILib + { +#else + static class _UniFFILib + { +#endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceFree(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureDroppedCallbackStruct + { + public ulong @handle; + public IntPtr @free; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU8 + { + public byte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI8 + { + public sbyte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU16 + { + public ushort @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI16 + { + public short @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU32 + { + public uint @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI32 + { + public int @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU64 + { + public ulong @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI64 + { + public long @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF32 + { + public float @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF64 + { + public double @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultRustBuffer + { + public RustBuffer @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteRustBuffer( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultRustBuffer @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultVoid + { + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteVoid( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultVoid @result + ); + + static _UniFFILib() + { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + } + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_common_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_common_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rustbuffer_free( + RustBuffer @buf, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_common_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_u8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_common_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_i8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_common_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_u16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_common_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_i16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_common_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_u32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_common_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_i32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_common_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_u64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_common_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_i64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_common_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_f32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_common_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_f64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_common_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_common_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_common_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_common_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = _UniFFILib.ffi_livekit_common_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"uniffi.livekit_common: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() { } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } + + class FfiConverterByteArray : FfiConverterRustBuffer + { + public static FfiConverterByteArray INSTANCE = new FfiConverterByteArray(); + + public override byte[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + return stream.ReadBytes(length); + } + + public override int AllocationSize(byte[] value) + { + return 4 + value.Length; + } + + public override void Write(byte[] value, BigEndianStream stream) + { + stream.WriteInt(value.Length); + stream.WriteBytes(value); + } + } + + /** + * Typealias from the type name used in the UDL file to the custom type. This + * is needed because the UDL type name is used in function/method signatures. + * It's also what we have an external type that references a custom type. + */ + + class FfiConverterTypeBytes : FfiConverter + { + public static FfiConverterTypeBytes INSTANCE = new FfiConverterTypeBytes(); + + public override Bytes Lift(RustBuffer value) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Lift(value); + return new System.ReadOnlyMemory(builtinValue); + } + + public override RustBuffer Lower(Bytes value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.Lower(builtinValue); + } + + public override Bytes Read(BigEndianStream stream) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Read(stream); + return new System.ReadOnlyMemory(builtinValue); + } + + public override int AllocationSize(Bytes value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.AllocationSize(builtinValue); + } + + public override void Write(Bytes value, BigEndianStream stream) + { + var builtinValue = value.ToArray(); + FfiConverterByteArray.INSTANCE.Write(builtinValue, stream); + } + } +#pragma warning restore 8625 + public static class LivekitCommonMethods { } +} diff --git a/Runtime/Scripts/UniFFI/livekit_common.cs.meta b/Runtime/Scripts/UniFFI/livekit_common.cs.meta new file mode 100644 index 00000000..391b72f8 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_common.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 195852167b6904c8aacd771c6a3027f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_datatrack.cs b/Runtime/Scripts/UniFFI/livekit_datatrack.cs new file mode 100644 index 00000000..34970a04 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_datatrack.cs @@ -0,0 +1,3465 @@ +// +// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py +// + +#nullable enable + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using uniffi.livekit_common; + +namespace uniffi.livekit_datatrack +{ + // WORKAROUND(uniffi-bindgen-cs): `Bytes` is a custom type of uniffi.livekit_common, the only file uniffi-bindgen-cs + // aliases it in. See Scripts~/uniffi/UPSTREAM-external-custom-type-alias.md + using Bytes = System.ReadOnlyMemory; + + using InitializationVector = System.ReadOnlyMemory; + + // This is a helper for safely working with byte buffers returned from the Rust code. + // A rust-owned buffer is represented by its capacity, its current length, and a + // pointer to the underlying data. + + [StructLayout(LayoutKind.Sequential)] + internal struct RustBuffer + { + public ulong capacity; + public ulong len; + public IntPtr data; + + public static RustBuffer Alloc(int size) + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + var buffer = _UniFFILib.ffi_livekit_datatrack_rustbuffer_alloc( + Convert.ToUInt64(size), + ref status + ); + if (buffer.data == IntPtr.Zero) + { + throw new AllocationException( + $"RustBuffer.Alloc() returned null data pointer (size={size})" + ); + } + return buffer; + } + ); + } + + public static void Free(RustBuffer buffer) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_datatrack_rustbuffer_free(buffer, ref status); + } + ); + } + + public static BigEndianStream MemoryStream(IntPtr data, long length) + { + unsafe + { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) + ); + } + } + + public BigEndianStream AsWriteableStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream( + (byte*)data.ToPointer(), + Convert.ToInt64(capacity), + Convert.ToInt64(capacity), + FileAccess.Write + ) + ); + } + } + } + + // This is a helper for safely passing byte references into the rust code. + // It's not actually used at the moment, because there aren't many things that you + // can take a direct pointer to managed memory, and if we're going to copy something + // then we might as well copy it into a `RustBuffer`. But it's here for API + // completeness. + + [StructLayout(LayoutKind.Sequential)] + internal struct ForeignBytes + { + public int length; + public IntPtr data; + } + + // The FfiConverter interface handles converter types to and from the FFI + // + // All implementing objects should be public to support external types. When a + // type is external we need to import it's FfiConverter. + internal abstract class FfiConverter + { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) + { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try + { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToUInt64(stream.Position); + return rbuf; + } + catch + { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) + { + var stream = rbuf.AsStream(); + try + { + var item = Read(stream); + if (stream.HasRemaining()) + { + throw new InternalException( + "junk remaining in buffer after lifting, something is very wrong!!" + ); + } + return item; + } + finally + { + RustBuffer.Free(rbuf); + } + } + } + + // FfiConverter that uses `RustBuffer` as the FfiType + internal abstract class FfiConverterRustBuffer : FfiConverter + { + public override CsType Lift(RustBuffer value) + { + return LiftFromRustBuffer(value); + } + + public override RustBuffer Lower(CsType value) + { + return LowerIntoRustBuffer(value); + } + } + + // A handful of classes and functions to support the generated data structures. + // This would be a good candidate for isolating in its own ffi-support lib. + // Error runtime. + [StructLayout(LayoutKind.Sequential)] + struct UniffiRustCallStatus + { + public sbyte code; + public RustBuffer error_buf; + + public bool IsSuccess() + { + return code == 0; + } + + public bool IsError() + { + return code == 1; + } + + public bool IsPanic() + { + return code == 2; + } + } + + // Base class for all uniffi exceptions + public class UniffiException : System.Exception + { + public UniffiException() + : base() { } + + public UniffiException(string message) + : base(message) { } + } + + public class UndeclaredErrorException : UniffiException + { + public UndeclaredErrorException(string message) + : base(message) { } + } + + public class PanicException : UniffiException + { + public PanicException(string message) + : base(message) { } + } + + public class AllocationException : UniffiException + { + public AllocationException(string message) + : base(message) { } + } + + public class InternalException : UniffiException + { + public InternalException(string message) + : base(message) { } + } + + public class InvalidEnumException : InternalException + { + public InvalidEnumException(string message) + : base(message) { } + } + + public class UniffiContractVersionException : UniffiException + { + public UniffiContractVersionException(string message) + : base(message) { } + } + + public class UniffiContractChecksumException : UniffiException + { + public UniffiContractChecksumException(string message) + : base(message) { } + } + + // Each top-level error class has a companion object that can lift the error from the call status's rust buffer + interface CallStatusErrorHandler + where E : System.Exception + { + E Lift(RustBuffer error_buf); + } + + // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + class NullCallStatusErrorHandler : CallStatusErrorHandler + { + public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); + + public UniffiException Lift(RustBuffer error_buf) + { + RustBuffer.Free(error_buf); + return new UndeclaredErrorException( + "library has returned an error not declared in UNIFFI interface file" + ); + } + } + + // Helpers for calling Rust + // In practice we usually need to be synchronized to call this safely, so it doesn't + // synchronize itself + class _UniffiHelpers + { + public delegate void RustCallAction(ref UniffiRustCallStatus status); + public delegate U RustCallFunc(ref UniffiRustCallStatus status); + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static U RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallFunc callback + ) + where E : System.Exception + { + var status = new UniffiRustCallStatus(); + var return_value = callback(ref status); + if (status.IsSuccess()) + { + return return_value; + } + else if (status.IsError()) + { + throw errorHandler.Lift(status.error_buf); + } + else if (status.IsPanic()) + { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) + { + throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); + } + else + { + throw new PanicException("Rust panic"); + } + } + else + { + throw new InternalException($"Unknown rust call status: {status.code}"); + } + } + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static void RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallAction callback + ) + where E : System.Exception + { + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + + // Call a rust function that returns a plain value + public static U RustCall(RustCallFunc callback) + { + return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); + } + + // Call a rust function that returns a plain value + public static void RustCall(RustCallAction callback) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + } + + static class FFIObjectUtil + { + public static void DisposeAll(params Object?[] list) + { + Dispose(list); + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(Object? obj) + { + if (obj == null) + { + return; + } + + if (obj is IDisposable disposable) + { + disposable.Dispose(); + return; + } + + var objType = obj.GetType(); + var typeCode = Type.GetTypeCode(objType); + if (typeCode != TypeCode.Object) + { + return; + } + + var genericArguments = objType.GetGenericArguments(); + if (genericArguments.Length == 0 && !objType.IsArray) + { + return; + } + + if (obj is System.Collections.IDictionary objDictionary) + { + //This extra code tests to not call "Dispose" for a Dictionary() + //for all values as "double" and alike doesn't support interface "IDisposable" + var valuesType = objType.GetGenericArguments()[1]; + var elementValuesTypeCode = Type.GetTypeCode(valuesType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in objDictionary.Values) + { + Dispose(value); + } + } + else if (obj is System.Collections.IEnumerable listValues) + { + //This extra code tests to not call "Dispose" for a List() + //for all keys as "int" and alike doesn't support interface "IDisposable" + var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; + var elementValuesTypeCode = Type.GetTypeCode(elementType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in listValues) + { + Dispose(value); + } + } + } + } + + // Big endian streams are not yet available in dotnet :'( + // https://github.com/dotnet/runtime/issues/26904 + + class StreamUnderflowException : System.Exception + { + public StreamUnderflowException() { } + } + + static class BigEndianStreamExtensions + { + public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static void WriteInt64(this Stream stream, long value) + { + int bytesToWrite = 8; +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static uint ReadUint32(this Stream stream, int bytesToRead = 4) + { + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + uint result = 0; + uint digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static ulong ReadUInt64(this Stream stream) + { + int bytesToRead = 8; + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + ulong result = 0; + ulong digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static void CheckRemaining(this Stream stream, int length) + { + if (stream.Length - stream.Position < length) + { + throw new StreamUnderflowException(); + } + } + + public static void ForEach(this T[] items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + } + + class BigEndianStream + { + Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public Stream InnerStream => stream; + + public bool HasRemaining() + { + return (stream.Length - Position) > 0; + } + + public long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] buffer) + { +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteUInt(uint value) => stream.WriteInt32((int)value); + + public void WriteInt(int value) => stream.WriteInt32(value); + + public void WriteULong(ulong value) => stream.WriteInt64((long)value); + + public void WriteLong(long value) => stream.WriteInt64(value); + + public void WriteFloat(float value) + { + unsafe + { + WriteInt(*((int*)&value)); + } + } + + public void WriteDouble(double value) => + stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); + + public byte[] ReadBytes(int length) + { + stream.CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); + + public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); + + public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); + + public ulong ReadULong() => stream.ReadUInt64(); + + public sbyte ReadSByte() => (sbyte)ReadByte(); + + public short ReadShort() => (short)ReadUShort(); + + public int ReadInt() => (int)ReadUInt(); + + public float ReadFloat() + { + unsafe + { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() => (long)ReadULong(); + + public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + + // This is an implementation detail that will be called internally by the public API. +#if NET8_0_OR_GREATER + static partial class _UniFFILib + { +#else + static class _UniFFILib + { +#endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceFree(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureDroppedCallbackStruct + { + public ulong @handle; + public IntPtr @free; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU8 + { + public byte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI8 + { + public sbyte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU16 + { + public ushort @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI16 + { + public short @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU32 + { + public uint @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI32 + { + public int @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU64 + { + public ulong @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI64 + { + public long @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF32 + { + public float @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF64 + { + public double @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultRustBuffer + { + public RustBuffer @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteRustBuffer( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultRustBuffer @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultVoid + { + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteVoid( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultVoid @result + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceDecryptionProviderMethod0( + ulong @uniffiHandle, + RustBuffer @payload, + RustBuffer @senderIdentity, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceEncryptionProviderMethod0( + ulong @uniffiHandle, + RustBuffer @payload, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceDecryptionProvider + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @decrypt; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceEncryptionProvider + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @encrypt; + } + + static _UniFFILib() + { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + + UniffiCallbackInterfaceDecryptionProvider.Register(); + UniffiCallbackInterfaceEncryptionProvider.Register(); + } + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_datatrack_fn_clone_decryptionprovider( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_datatrack_fn_free_decryptionprovider( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_datatrack_fn_init_callback_vtable_decryptionprovider( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceDecryptionProvider*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_datatrack_fn_method_decryptionprovider_decrypt( + ulong @ptr, + RustBuffer @payload, + RustBuffer @senderIdentity, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_datatrack_fn_clone_encryptionprovider( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_datatrack_fn_free_encryptionprovider( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_datatrack_fn_init_callback_vtable_encryptionprovider( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceEncryptionProvider*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_datatrack_fn_method_encryptionprovider_encrypt( + ulong @ptr, + RustBuffer @payload, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_datatrack_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_datatrack_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rustbuffer_free( + RustBuffer @buf, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_datatrack_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_u8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_datatrack_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_i8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_datatrack_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_u16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_datatrack_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_i16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_datatrack_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_u32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_datatrack_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_i32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_datatrack_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_u64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_datatrack_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_i64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_datatrack_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_f32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_datatrack_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_f64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_datatrack_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_datatrack_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_datatrack_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_datatrack_checksum_method_decryptionprovider_decrypt(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_datatrack_checksum_method_encryptionprovider_encrypt(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_datatrack_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = + _UniFFILib.ffi_livekit_datatrack_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"uniffi.livekit_datatrack: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() + { + { + var checksum = + _UniFFILib.uniffi_livekit_datatrack_checksum_method_decryptionprovider_decrypt(); + if (checksum != 1225) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_datatrack: uniffi bindings expected function `uniffi_livekit_datatrack_checksum_method_decryptionprovider_decrypt` checksum `1225`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_datatrack_checksum_method_encryptionprovider_encrypt(); + if (checksum != 40311) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_datatrack: uniffi bindings expected function `uniffi_livekit_datatrack_checksum_method_encryptionprovider_encrypt` checksum `40311`, library returned `{checksum}`" + ); + } + } + } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterUInt8 : FfiConverter + { + public static FfiConverterUInt8 INSTANCE = new FfiConverterUInt8(); + + public override byte Lift(byte value) + { + return value; + } + + public override byte Read(BigEndianStream stream) + { + return stream.ReadByte(); + } + + public override byte Lower(byte value) + { + return value; + } + + public override int AllocationSize(byte value) + { + return 1; + } + + public override void Write(byte value, BigEndianStream stream) + { + stream.WriteByte(value); + } + } + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } + + class FfiConverterByteArray : FfiConverterRustBuffer + { + public static FfiConverterByteArray INSTANCE = new FfiConverterByteArray(); + + public override byte[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + return stream.ReadBytes(length); + } + + public override int AllocationSize(byte[] value) + { + return 4 + value.Length; + } + + public override void Write(byte[] value, BigEndianStream stream) + { + stream.WriteInt(value.Length); + stream.WriteBytes(value); + } + } + + /// + /// Provider for decrypting payloads for E2EE. + /// + public interface DecryptionProvider + { + /// + /// Decrypts the given payload received from a remote participant. + /// + /// Sender identity is required in order for the proper key to be used + /// for decryption. + /// + /// + Bytes Decrypt(EncryptedPayload @payload, string @senderIdentity); + } + + /// + /// Provider for decrypting payloads for E2EE. + /// + public class DecryptionProviderImpl : DecryptionProvider, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public DecryptionProviderImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~DecryptionProviderImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_datatrack_fn_free_decryptionprovider( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_datatrack_fn_clone_decryptionprovider( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Decrypts the given payload received from a remote participant. + /// + /// Sender identity is required in order for the proper key to be used + /// for decryption. + /// + /// + public Bytes Decrypt(EncryptedPayload @payload, string @senderIdentity) + { + return CallWithPointer(thisPtr => + FfiConverterTypeBytes.INSTANCE.Lift( + _UniffiHelpers.RustCallWithError( + FfiConverterTypeDecryptionError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_datatrack_fn_method_decryptionprovider_decrypt( + thisPtr, + FfiConverterTypeEncryptedPayload.INSTANCE.Lower(@payload), + FfiConverterString.INSTANCE.Lower(@senderIdentity), + ref _status + ) + ) + ) + ); + } + } + + class UniffiCallbackInterfaceDecryptionProvider + { + static void Decrypt( + ulong @uniffiHandle, + RustBuffer @payload, + RustBuffer @senderIdentity, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeDecryptionProvider.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + var result = uniffiObject.Decrypt( + FfiConverterTypeEncryptedPayload.INSTANCE.Lift(@payload), + FfiConverterString.INSTANCE.Lift(@senderIdentity) + ); + unsafe + { + *(RustBuffer*)uniffiOutReturn = FfiConverterTypeBytes.INSTANCE.Lower(result); + } + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (DecryptionException e) + { + try + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.ERROR; + _uniffi_out_err.error_buf = FfiConverterTypeDecryptionError.INSTANCE.Lower(e); + } + catch + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeDecryptionProvider.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if (!FfiConverterTypeDecryptionProvider.INSTANCE.handleMap.TryGet(@handle, out var obj)) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeDecryptionProvider.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceDecryptionProviderMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceDecryptionProviderMethod0(Decrypt); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceDecryptionProvider _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceDecryptionProvider + { + @decrypt = Marshal.GetFunctionPointerForDelegate(_m0), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_datatrack_fn_init_callback_vtable_decryptionprovider( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class ConcurrentHandleMap + where T : notnull + { + readonly ConcurrentDictionary _map = new(); + + // Handles are odd numbers (1, 3, 5, ...) — the lowest bit must always be set. + // Rust uses (handle & 1) to distinguish foreign-language handles from Rust Arc + // pointers, which are always even due to memory alignment. See uniffi_core/src/ffi/handle.rs. + const long HANDLE_INITIAL = 1; + const long HANDLE_DELTA = 2; + long _currentHandle = HANDLE_INITIAL - HANDLE_DELTA; + + public ulong Insert(T obj) + { + var handle = (ulong)Interlocked.Add(ref _currentHandle, HANDLE_DELTA); + if (!_map.TryAdd(handle, obj)) + { + throw new InternalException("ConcurrentHandleMap: Duplicate handle"); + } + return handle; + } + + public bool TryGet(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryGetValue(handle, out result); + } + + public T Get(ulong handle) + { + if (_map.TryGetValue(handle, out var result)) + { + return result; + } + else + { + throw new InternalException("ConcurrentHandleMap: Invalid handle"); + } + } + + public bool Remove(ulong handle) + { + return _map.TryRemove(handle, out _); + } + + public bool Remove(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryRemove(handle, out result); + } + } + + static class UniffiCallbackResponseStatus + { + public static sbyte SUCCESS = 0; + public static sbyte ERROR = 1; + public static sbyte UNEXPECTED_ERROR = 2; + } + + class FfiConverterTypeDecryptionProvider : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeDecryptionProvider INSTANCE = + new FfiConverterTypeDecryptionProvider(); + + static FfiConverterTypeDecryptionProvider() + { + UniffiCallbackInterfaceDecryptionProvider.Register(); + } + + public override ulong Lower(DecryptionProvider value) + { + if (value is DecryptionProviderImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override DecryptionProvider Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new DecryptionProviderImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override DecryptionProvider Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(DecryptionProvider value) + { + return 8; + } + + public override void Write(DecryptionProvider value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Provider for encrypting payloads for E2EE. + /// + public interface EncryptionProvider + { + /// + /// Encrypts the given payload being sent by the local participant. + /// + /// + EncryptedPayload Encrypt(Bytes @payload); + } + + /// + /// Provider for encrypting payloads for E2EE. + /// + public class EncryptionProviderImpl : EncryptionProvider, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public EncryptionProviderImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~EncryptionProviderImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_datatrack_fn_free_encryptionprovider( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_datatrack_fn_clone_encryptionprovider( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Encrypts the given payload being sent by the local participant. + /// + /// + public EncryptedPayload Encrypt(Bytes @payload) + { + return CallWithPointer(thisPtr => + FfiConverterTypeEncryptedPayload.INSTANCE.Lift( + _UniffiHelpers.RustCallWithError( + FfiConverterTypeEncryptionError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_datatrack_fn_method_encryptionprovider_encrypt( + thisPtr, + FfiConverterTypeBytes.INSTANCE.Lower(@payload), + ref _status + ) + ) + ) + ); + } + } + + class UniffiCallbackInterfaceEncryptionProvider + { + static void Encrypt( + ulong @uniffiHandle, + RustBuffer @payload, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeEncryptionProvider.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + var result = uniffiObject.Encrypt(FfiConverterTypeBytes.INSTANCE.Lift(@payload)); + unsafe + { + *(RustBuffer*)uniffiOutReturn = FfiConverterTypeEncryptedPayload.INSTANCE.Lower( + result + ); + } + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (EncryptionException e) + { + try + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.ERROR; + _uniffi_out_err.error_buf = FfiConverterTypeEncryptionError.INSTANCE.Lower(e); + } + catch + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeEncryptionProvider.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if (!FfiConverterTypeEncryptionProvider.INSTANCE.handleMap.TryGet(@handle, out var obj)) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeEncryptionProvider.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceEncryptionProviderMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceEncryptionProviderMethod0(Encrypt); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceEncryptionProvider _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceEncryptionProvider + { + @encrypt = Marshal.GetFunctionPointerForDelegate(_m0), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_datatrack_fn_init_callback_vtable_encryptionprovider( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeEncryptionProvider : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeEncryptionProvider INSTANCE = + new FfiConverterTypeEncryptionProvider(); + + static FfiConverterTypeEncryptionProvider() + { + UniffiCallbackInterfaceEncryptionProvider.Register(); + } + + public override ulong Lower(EncryptionProvider value) + { + if (value is EncryptionProviderImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override EncryptionProvider Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new EncryptionProviderImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override EncryptionProvider Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(EncryptionProvider value) + { + return 8; + } + + public override void Write(EncryptionProvider value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Encrypted payload and metadata required for decryption. + /// + public record EncryptedPayload(Bytes Payload, InitializationVector Iv, byte KeyIndex) { } + + class FfiConverterTypeEncryptedPayload : FfiConverterRustBuffer + { + public static FfiConverterTypeEncryptedPayload INSTANCE = + new FfiConverterTypeEncryptedPayload(); + + public override EncryptedPayload Read(BigEndianStream stream) + { + return new EncryptedPayload( + Payload: FfiConverterTypeBytes.INSTANCE.Read(stream), + Iv: FfiConverterTypeInitializationVector.INSTANCE.Read(stream), + KeyIndex: FfiConverterUInt8.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(EncryptedPayload value) + { + return 0 + + FfiConverterTypeBytes.INSTANCE.AllocationSize(value.Payload) + + FfiConverterTypeInitializationVector.INSTANCE.AllocationSize(value.Iv) + + FfiConverterUInt8.INSTANCE.AllocationSize(value.KeyIndex); + } + + public override void Write(EncryptedPayload value, BigEndianStream stream) + { + FfiConverterTypeBytes.INSTANCE.Write(value.Payload, stream); + FfiConverterTypeInitializationVector.INSTANCE.Write(value.Iv, stream); + FfiConverterUInt8.INSTANCE.Write(value.KeyIndex, stream); + } + } + + /// + /// Encoding used for frames pushed on a data track. + /// + /// The serialization format of the frame bytes (e.g. [`Protobuf`]); the structure + /// of those bytes is described by a schema, see [`DataTrackSchemaEncoding`]. + /// + /// [`Protobuf`]: DataTrackFrameEncoding::Protobuf + /// + public record DataTrackFrameEncoding + { + /// + /// ROS 1, must be described by a [`Ros1Msg`] schema. + /// + /// [`Ros1Msg`]: DataTrackSchemaEncoding::Ros1Msg + /// + public record Ros1 : DataTrackFrameEncoding { } + + /// + /// CDR, must be described by a [`Ros2Msg`], [`Ros2Idl`], or [`OmgIdl`] schema. + /// + /// [`Ros2Msg`]: DataTrackSchemaEncoding::Ros2Msg + /// [`Ros2Idl`]: DataTrackSchemaEncoding::Ros2Idl + /// [`OmgIdl`]: DataTrackSchemaEncoding::OmgIdl + /// + public record Cdr : DataTrackFrameEncoding { } + + /// + /// Protocol Buffer, must be described by a [`Protobuf`] schema. + /// + /// [`Protobuf`]: DataTrackSchemaEncoding::Protobuf + /// + public record Protobuf : DataTrackFrameEncoding { } + + /// + /// FlatBuffer, must be described by a [`Flatbuffer`] schema. + /// + /// [`Flatbuffer`]: DataTrackSchemaEncoding::Flatbuffer + /// + public record Flatbuffer : DataTrackFrameEncoding { } + + /// + /// CBOR, self-describing. + /// + public record Cbor : DataTrackFrameEncoding { } + + /// + /// MessagePack, self-describing. + /// + public record Msgpack : DataTrackFrameEncoding { } + + /// + /// JSON, self-describing or described by a [`JsonSchema`] schema. + /// + /// [`JsonSchema`]: DataTrackSchemaEncoding::JsonSchema + /// + public record Json : DataTrackFrameEncoding { } + + /// + /// Another well-known encoding not known to this client version. + /// + public record Other : DataTrackFrameEncoding { } + + /// + /// An application-specific encoding identified by the contained string. + /// + /// Prefer using one of the well-known encodings unless the format is not enumerated. + /// The identifier must be non-empty and not exceed the server's length limit. + /// + public record Custom(string V1) : DataTrackFrameEncoding { } + } + + class FfiConverterTypeDataTrackFrameEncoding : FfiConverterRustBuffer + { + public static FfiConverterRustBuffer INSTANCE = + new FfiConverterTypeDataTrackFrameEncoding(); + + public override DataTrackFrameEncoding Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new DataTrackFrameEncoding.Ros1(); + case 2: + return new DataTrackFrameEncoding.Cdr(); + case 3: + return new DataTrackFrameEncoding.Protobuf(); + case 4: + return new DataTrackFrameEncoding.Flatbuffer(); + case 5: + return new DataTrackFrameEncoding.Cbor(); + case 6: + return new DataTrackFrameEncoding.Msgpack(); + case 7: + return new DataTrackFrameEncoding.Json(); + case 8: + return new DataTrackFrameEncoding.Other(); + case 9: + return new DataTrackFrameEncoding.Custom(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackFrameEncoding.Read()", + value + ) + ); + } + } + + public override int AllocationSize(DataTrackFrameEncoding value) + { + switch (value) + { + case DataTrackFrameEncoding.Ros1 variant_value: + return 4; + case DataTrackFrameEncoding.Cdr variant_value: + return 4; + case DataTrackFrameEncoding.Protobuf variant_value: + return 4; + case DataTrackFrameEncoding.Flatbuffer variant_value: + return 4; + case DataTrackFrameEncoding.Cbor variant_value: + return 4; + case DataTrackFrameEncoding.Msgpack variant_value: + return 4; + case DataTrackFrameEncoding.Json variant_value: + return 4; + case DataTrackFrameEncoding.Other variant_value: + return 4; + case DataTrackFrameEncoding.Custom variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.V1); + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackFrameEncoding.AllocationSize()", + value + ) + ); + } + } + + public override void Write(DataTrackFrameEncoding value, BigEndianStream stream) + { + switch (value) + { + case DataTrackFrameEncoding.Ros1 variant_value: + stream.WriteInt(1); + break; + case DataTrackFrameEncoding.Cdr variant_value: + stream.WriteInt(2); + break; + case DataTrackFrameEncoding.Protobuf variant_value: + stream.WriteInt(3); + break; + case DataTrackFrameEncoding.Flatbuffer variant_value: + stream.WriteInt(4); + break; + case DataTrackFrameEncoding.Cbor variant_value: + stream.WriteInt(5); + break; + case DataTrackFrameEncoding.Msgpack variant_value: + stream.WriteInt(6); + break; + case DataTrackFrameEncoding.Json variant_value: + stream.WriteInt(7); + break; + case DataTrackFrameEncoding.Other variant_value: + stream.WriteInt(8); + break; + case DataTrackFrameEncoding.Custom variant_value: + stream.WriteInt(9); + FfiConverterString.INSTANCE.Write(variant_value.V1, stream); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackFrameEncoding.Write()", + value + ) + ); + } + } + } + + /// + /// Encoding used for a schema definition. + /// + /// Identifies the interface definition language the schema is written in (e.g. a + /// `.proto` file for [`Protobuf`]). This in turn dictates the wire format of the + /// frames the schema describes, captured by [`DataTrackFrameEncoding`]. + /// + /// [`Protobuf`]: DataTrackSchemaEncoding::Protobuf + /// + public record DataTrackSchemaEncoding + { + /// + /// Protocol Buffer IDL, describes [`Protobuf`] encoded frames. + /// + /// [`Protobuf`]: DataTrackFrameEncoding::Protobuf + /// + public record Protobuf : DataTrackSchemaEncoding { } + + /// + /// FlatBuffer IDL, describes [`Flatbuffer`] encoded frames. + /// + /// [`Flatbuffer`]: DataTrackFrameEncoding::Flatbuffer + /// + public record Flatbuffer : DataTrackSchemaEncoding { } + + /// + /// ROS 1 Message, describes [`Ros1`] encoded frames. + /// + /// [`Ros1`]: DataTrackFrameEncoding::Ros1 + /// + public record Ros1Msg : DataTrackSchemaEncoding { } + + /// + /// ROS 2 Message, describes [`Cdr`] encoded frames. + /// + /// [`Cdr`]: DataTrackFrameEncoding::Cdr + /// + public record Ros2Msg : DataTrackSchemaEncoding { } + + /// + /// ROS 2 IDL, describes [`Cdr`] encoded frames. + /// + /// [`Cdr`]: DataTrackFrameEncoding::Cdr + /// + public record Ros2Idl : DataTrackSchemaEncoding { } + + /// + /// OMG IDL, describes [`Cdr`] encoded frames. + /// + /// [`Cdr`]: DataTrackFrameEncoding::Cdr + /// + public record OmgIdl : DataTrackSchemaEncoding { } + + /// + /// JSON Schema, describes [`Json`] encoded frames. + /// + /// [`Json`]: DataTrackFrameEncoding::Json + /// + public record JsonSchema : DataTrackSchemaEncoding { } + + /// + /// Another well-known encoding not known to this client version. + /// + public record Other : DataTrackSchemaEncoding { } + + /// + /// An application-specific encoding identified by the contained string. + /// + /// Prefer using one of the well-known encodings unless the format is not enumerated. + /// The identifier must be non-empty and not exceed the server's length limit. + /// + public record Custom(string V1) : DataTrackSchemaEncoding { } + } + + class FfiConverterTypeDataTrackSchemaEncoding : FfiConverterRustBuffer + { + public static FfiConverterRustBuffer INSTANCE = + new FfiConverterTypeDataTrackSchemaEncoding(); + + public override DataTrackSchemaEncoding Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new DataTrackSchemaEncoding.Protobuf(); + case 2: + return new DataTrackSchemaEncoding.Flatbuffer(); + case 3: + return new DataTrackSchemaEncoding.Ros1Msg(); + case 4: + return new DataTrackSchemaEncoding.Ros2Msg(); + case 5: + return new DataTrackSchemaEncoding.Ros2Idl(); + case 6: + return new DataTrackSchemaEncoding.OmgIdl(); + case 7: + return new DataTrackSchemaEncoding.JsonSchema(); + case 8: + return new DataTrackSchemaEncoding.Other(); + case 9: + return new DataTrackSchemaEncoding.Custom(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackSchemaEncoding.Read()", + value + ) + ); + } + } + + public override int AllocationSize(DataTrackSchemaEncoding value) + { + switch (value) + { + case DataTrackSchemaEncoding.Protobuf variant_value: + return 4; + case DataTrackSchemaEncoding.Flatbuffer variant_value: + return 4; + case DataTrackSchemaEncoding.Ros1Msg variant_value: + return 4; + case DataTrackSchemaEncoding.Ros2Msg variant_value: + return 4; + case DataTrackSchemaEncoding.Ros2Idl variant_value: + return 4; + case DataTrackSchemaEncoding.OmgIdl variant_value: + return 4; + case DataTrackSchemaEncoding.JsonSchema variant_value: + return 4; + case DataTrackSchemaEncoding.Other variant_value: + return 4; + case DataTrackSchemaEncoding.Custom variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.V1); + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackSchemaEncoding.AllocationSize()", + value + ) + ); + } + } + + public override void Write(DataTrackSchemaEncoding value, BigEndianStream stream) + { + switch (value) + { + case DataTrackSchemaEncoding.Protobuf variant_value: + stream.WriteInt(1); + break; + case DataTrackSchemaEncoding.Flatbuffer variant_value: + stream.WriteInt(2); + break; + case DataTrackSchemaEncoding.Ros1Msg variant_value: + stream.WriteInt(3); + break; + case DataTrackSchemaEncoding.Ros2Msg variant_value: + stream.WriteInt(4); + break; + case DataTrackSchemaEncoding.Ros2Idl variant_value: + stream.WriteInt(5); + break; + case DataTrackSchemaEncoding.OmgIdl variant_value: + stream.WriteInt(6); + break; + case DataTrackSchemaEncoding.JsonSchema variant_value: + stream.WriteInt(7); + break; + case DataTrackSchemaEncoding.Other variant_value: + stream.WriteInt(8); + break; + case DataTrackSchemaEncoding.Custom variant_value: + stream.WriteInt(9); + FfiConverterString.INSTANCE.Write(variant_value.V1, stream); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeDataTrackSchemaEncoding.Write()", + value + ) + ); + } + } + } + + public class DataTrackSubscribeException : UniffiException + { + DataTrackSubscribeException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class Unpublished : DataTrackSubscribeException + { + public Unpublished(string message) + : base(message) { } + } + + public class Timeout : DataTrackSubscribeException + { + public Timeout(string message) + : base(message) { } + } + + public class Disconnected : DataTrackSubscribeException + { + public Disconnected(string message) + : base(message) { } + } + + public class Internal : DataTrackSubscribeException + { + public Internal(string message) + : base(message) { } + } + } + + class FfiConverterTypeDataTrackSubscribeError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeDataTrackSubscribeError INSTANCE = + new FfiConverterTypeDataTrackSubscribeError(); + + public override DataTrackSubscribeException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new DataTrackSubscribeException.Unpublished( + FfiConverterString.INSTANCE.Read(stream) + ); + case 2: + return new DataTrackSubscribeException.Timeout( + FfiConverterString.INSTANCE.Read(stream) + ); + case 3: + return new DataTrackSubscribeException.Disconnected( + FfiConverterString.INSTANCE.Read(stream) + ); + case 4: + return new DataTrackSubscribeException.Internal( + FfiConverterString.INSTANCE.Read(stream) + ); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDataTrackSubscribeError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(DataTrackSubscribeException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(DataTrackSubscribeException value, BigEndianStream stream) + { + switch (value) + { + case DataTrackSubscribeException.Unpublished: + stream.WriteInt(1); + break; + case DataTrackSubscribeException.Timeout: + stream.WriteInt(2); + break; + case DataTrackSubscribeException.Disconnected: + stream.WriteInt(3); + break; + case DataTrackSubscribeException.Internal: + stream.WriteInt(4); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDataTrackSubscribeError.Write()", + value + ) + ); + } + } + } + + /// + /// An error indicating a payload could not be decrypted. + /// + public class DecryptionException : UniffiException + { + DecryptionException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class Failed : DecryptionException + { + public Failed(string message) + : base(message) { } + } + } + + class FfiConverterTypeDecryptionError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeDecryptionError INSTANCE = new FfiConverterTypeDecryptionError(); + + public override DecryptionException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new DecryptionException.Failed(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDecryptionError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(DecryptionException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(DecryptionException value, BigEndianStream stream) + { + switch (value) + { + case DecryptionException.Failed: + stream.WriteInt(1); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDecryptionError.Write()", + value + ) + ); + } + } + } + + /// + /// An error indicating a payload could not be encrypted. + /// + public class EncryptionException : UniffiException + { + EncryptionException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class Failed : EncryptionException + { + public Failed(string message) + : base(message) { } + } + } + + class FfiConverterTypeEncryptionError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeEncryptionError INSTANCE = new FfiConverterTypeEncryptionError(); + + public override EncryptionException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new EncryptionException.Failed(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeEncryptionError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(EncryptionException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(EncryptionException value, BigEndianStream stream) + { + switch (value) + { + case EncryptionException.Failed: + stream.WriteInt(1); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeEncryptionError.Write()", + value + ) + ); + } + } + } + + /// + /// An error that can occur when publishing a data track. + /// + public class PublishException : UniffiException + { + PublishException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + /// + /// Local participant does not have permission to publish data tracks. + /// + /// Ensure the participant's token contains the `canPublishData` grant. + /// + public class NotAllowed : PublishException + { + public NotAllowed(string message) + : base(message) { } + } + + /// + /// A track with the same name is already published by the local participant. + /// + public class DuplicateName : PublishException + { + public DuplicateName(string message) + : base(message) { } + } + + /// + /// The track name is invalid. + /// + /// This occurs when the name is empty or exceeds the allowed maximum length. + /// + public class InvalidName : PublishException + { + public InvalidName(string message) + : base(message) { } + } + + /// + /// Request to publish the track took long to complete. + /// + public class Timeout : PublishException + { + public Timeout(string message) + : base(message) { } + } + + /// + /// No additional data tracks can be published by the local participant. + /// + public class LimitReached : PublishException + { + public LimitReached(string message) + : base(message) { } + } + + /// + /// Cannot publish data track when the room is disconnected. + /// + public class Disconnected : PublishException + { + public Disconnected(string message) + : base(message) { } + } + + /// + /// Schema metadata is invalid. + /// + public class InvalidSchema : PublishException + { + public InvalidSchema(string message) + : base(message) { } + } + + /// + /// Internal error, please report on GitHub. + /// + public class Internal : PublishException + { + public Internal(string message) + : base(message) { } + } + } + + class FfiConverterTypePublishError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypePublishError INSTANCE = new FfiConverterTypePublishError(); + + public override PublishException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new PublishException.NotAllowed(FfiConverterString.INSTANCE.Read(stream)); + case 2: + return new PublishException.DuplicateName(FfiConverterString.INSTANCE.Read(stream)); + case 3: + return new PublishException.InvalidName(FfiConverterString.INSTANCE.Read(stream)); + case 4: + return new PublishException.Timeout(FfiConverterString.INSTANCE.Read(stream)); + case 5: + return new PublishException.LimitReached(FfiConverterString.INSTANCE.Read(stream)); + case 6: + return new PublishException.Disconnected(FfiConverterString.INSTANCE.Read(stream)); + case 7: + return new PublishException.InvalidSchema(FfiConverterString.INSTANCE.Read(stream)); + case 8: + return new PublishException.Internal(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePublishError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(PublishException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(PublishException value, BigEndianStream stream) + { + switch (value) + { + case PublishException.NotAllowed: + stream.WriteInt(1); + break; + case PublishException.DuplicateName: + stream.WriteInt(2); + break; + case PublishException.InvalidName: + stream.WriteInt(3); + break; + case PublishException.Timeout: + stream.WriteInt(4); + break; + case PublishException.LimitReached: + stream.WriteInt(5); + break; + case PublishException.Disconnected: + stream.WriteInt(6); + break; + case PublishException.InvalidSchema: + stream.WriteInt(7); + break; + case PublishException.Internal: + stream.WriteInt(8); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePublishError.Write()", + value + ) + ); + } + } + } + + /// + /// Reason why a data track frame could not be pushed. + /// + public class PushFrameErrorReason : UniffiException + { + PushFrameErrorReason(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + /// + /// Track is no longer published. + /// + public class TrackUnpublished : PushFrameErrorReason + { + public TrackUnpublished(string message) + : base(message) { } + } + + /// + /// Frame was dropped due to the pipeline queue being full. + /// + public class QueueFull : PushFrameErrorReason + { + public QueueFull(string message) + : base(message) { } + } + } + + class FfiConverterTypePushFrameErrorReason + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypePushFrameErrorReason INSTANCE = + new FfiConverterTypePushFrameErrorReason(); + + public override PushFrameErrorReason Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new PushFrameErrorReason.TrackUnpublished( + FfiConverterString.INSTANCE.Read(stream) + ); + case 2: + return new PushFrameErrorReason.QueueFull(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePushFrameErrorReason.Read()", + value + ) + ); + } + } + + public override int AllocationSize(PushFrameErrorReason value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(PushFrameErrorReason value, BigEndianStream stream) + { + switch (value) + { + case PushFrameErrorReason.TrackUnpublished: + stream.WriteInt(1); + break; + case PushFrameErrorReason.QueueFull: + stream.WriteInt(2); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePushFrameErrorReason.Write()", + value + ) + ); + } + } + } + + /** + * Typealias from the type name used in the UDL file to the custom type. This + * is needed because the UDL type name is used in function/method signatures. + * It's also what we have an external type that references a custom type. + */ + + class FfiConverterTypeInitializationVector : FfiConverter + { + public static FfiConverterTypeInitializationVector INSTANCE = + new FfiConverterTypeInitializationVector(); + + public override InitializationVector Lift(RustBuffer value) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Lift(value); + return new System.ReadOnlyMemory(builtinValue); + } + + public override RustBuffer Lower(InitializationVector value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.Lower(builtinValue); + } + + public override InitializationVector Read(BigEndianStream stream) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Read(stream); + return new System.ReadOnlyMemory(builtinValue); + } + + public override int AllocationSize(InitializationVector value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.AllocationSize(builtinValue); + } + + public override void Write(InitializationVector value, BigEndianStream stream) + { + var builtinValue = value.ToArray(); + FfiConverterByteArray.INSTANCE.Write(builtinValue, stream); + } + } + + class FfiConverterTypeBytes : FfiConverterRustBuffer + { + public static FfiConverterTypeBytes INSTANCE = new FfiConverterTypeBytes(); + + public override Bytes Read(BigEndianStream stream) + { + return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Read( + new uniffi.livekit_common.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(Bytes value) + { + return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.AllocationSize(value); + } + + public override void Write(Bytes value, BigEndianStream stream) + { + uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Write( + value, + new uniffi.livekit_common.BigEndianStream(stream.InnerStream) + ); + } + } +#pragma warning restore 8625 + public static class LivekitDatatrackMethods { } +} diff --git a/Runtime/Scripts/UniFFI/livekit_datatrack.cs.meta b/Runtime/Scripts/UniFFI/livekit_datatrack.cs.meta new file mode 100644 index 00000000..3ae5b912 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_datatrack.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99a8ddbd7cf7b4563ab02fd59568889a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_net.cs b/Runtime/Scripts/UniFFI/livekit_net.cs new file mode 100644 index 00000000..44549966 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_net.cs @@ -0,0 +1,4459 @@ +// +// This file was generated by uniffi-bindgen-cs v0.11.0+v0.31.0 +// See https://github.com/NordSecurity/uniffi-bindgen-cs for more information. +// Post-processed for C# 9 by Scripts~/uniffi/downgrade_uniffi_bindings.py +// + +#nullable enable + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace uniffi.livekit_net +{ + // This is a helper for safely working with byte buffers returned from the Rust code. + // A rust-owned buffer is represented by its capacity, its current length, and a + // pointer to the underlying data. + + [StructLayout(LayoutKind.Sequential)] + internal struct RustBuffer + { + public ulong capacity; + public ulong len; + public IntPtr data; + + public static RustBuffer Alloc(int size) + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + var buffer = _UniFFILib.ffi_livekit_net_rustbuffer_alloc( + Convert.ToUInt64(size), + ref status + ); + if (buffer.data == IntPtr.Zero) + { + throw new AllocationException( + $"RustBuffer.Alloc() returned null data pointer (size={size})" + ); + } + return buffer; + } + ); + } + + public static void Free(RustBuffer buffer) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_net_rustbuffer_free(buffer, ref status); + } + ); + } + + public static BigEndianStream MemoryStream(IntPtr data, long length) + { + unsafe + { + return new BigEndianStream(new UnmanagedMemoryStream((byte*)data.ToPointer(), length)); + } + } + + public BigEndianStream AsStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream((byte*)data.ToPointer(), Convert.ToInt64(len)) + ); + } + } + + public BigEndianStream AsWriteableStream() + { + unsafe + { + return new BigEndianStream( + new UnmanagedMemoryStream( + (byte*)data.ToPointer(), + Convert.ToInt64(capacity), + Convert.ToInt64(capacity), + FileAccess.Write + ) + ); + } + } + } + + // This is a helper for safely passing byte references into the rust code. + // It's not actually used at the moment, because there aren't many things that you + // can take a direct pointer to managed memory, and if we're going to copy something + // then we might as well copy it into a `RustBuffer`. But it's here for API + // completeness. + + [StructLayout(LayoutKind.Sequential)] + internal struct ForeignBytes + { + public int length; + public IntPtr data; + } + + // The FfiConverter interface handles converter types to and from the FFI + // + // All implementing objects should be public to support external types. When a + // type is external we need to import it's FfiConverter. + internal abstract class FfiConverter + { + // Convert an FFI type to a C# type + public abstract CsType Lift(FfiType value); + + // Convert C# type to an FFI type + public abstract FfiType Lower(CsType value); + + // Read a C# type from a `ByteBuffer` + public abstract CsType Read(BigEndianStream stream); + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + public abstract int AllocationSize(CsType value); + + // Write a C# type to a `ByteBuffer` + public abstract void Write(CsType value, BigEndianStream stream); + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + public RustBuffer LowerIntoRustBuffer(CsType value) + { + var rbuf = RustBuffer.Alloc(AllocationSize(value)); + try + { + var stream = rbuf.AsWriteableStream(); + Write(value, stream); + rbuf.len = Convert.ToUInt64(stream.Position); + return rbuf; + } + catch + { + RustBuffer.Free(rbuf); + throw; + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + protected CsType LiftFromRustBuffer(RustBuffer rbuf) + { + var stream = rbuf.AsStream(); + try + { + var item = Read(stream); + if (stream.HasRemaining()) + { + throw new InternalException( + "junk remaining in buffer after lifting, something is very wrong!!" + ); + } + return item; + } + finally + { + RustBuffer.Free(rbuf); + } + } + } + + // FfiConverter that uses `RustBuffer` as the FfiType + internal abstract class FfiConverterRustBuffer : FfiConverter + { + public override CsType Lift(RustBuffer value) + { + return LiftFromRustBuffer(value); + } + + public override RustBuffer Lower(CsType value) + { + return LowerIntoRustBuffer(value); + } + } + + // A handful of classes and functions to support the generated data structures. + // This would be a good candidate for isolating in its own ffi-support lib. + // Error runtime. + [StructLayout(LayoutKind.Sequential)] + struct UniffiRustCallStatus + { + public sbyte code; + public RustBuffer error_buf; + + public bool IsSuccess() + { + return code == 0; + } + + public bool IsError() + { + return code == 1; + } + + public bool IsPanic() + { + return code == 2; + } + } + + // Base class for all uniffi exceptions + public class UniffiException : System.Exception + { + public UniffiException() + : base() { } + + public UniffiException(string message) + : base(message) { } + } + + public class UndeclaredErrorException : UniffiException + { + public UndeclaredErrorException(string message) + : base(message) { } + } + + public class PanicException : UniffiException + { + public PanicException(string message) + : base(message) { } + } + + public class AllocationException : UniffiException + { + public AllocationException(string message) + : base(message) { } + } + + public class InternalException : UniffiException + { + public InternalException(string message) + : base(message) { } + } + + public class InvalidEnumException : InternalException + { + public InvalidEnumException(string message) + : base(message) { } + } + + public class UniffiContractVersionException : UniffiException + { + public UniffiContractVersionException(string message) + : base(message) { } + } + + public class UniffiContractChecksumException : UniffiException + { + public UniffiContractChecksumException(string message) + : base(message) { } + } + + // Each top-level error class has a companion object that can lift the error from the call status's rust buffer + interface CallStatusErrorHandler + where E : System.Exception + { + E Lift(RustBuffer error_buf); + } + + // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + class NullCallStatusErrorHandler : CallStatusErrorHandler + { + public static NullCallStatusErrorHandler INSTANCE = new NullCallStatusErrorHandler(); + + public UniffiException Lift(RustBuffer error_buf) + { + RustBuffer.Free(error_buf); + return new UndeclaredErrorException( + "library has returned an error not declared in UNIFFI interface file" + ); + } + } + + // Helpers for calling Rust + // In practice we usually need to be synchronized to call this safely, so it doesn't + // synchronize itself + class _UniffiHelpers + { + public delegate void RustCallAction(ref UniffiRustCallStatus status); + public delegate U RustCallFunc(ref UniffiRustCallStatus status); + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static U RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallFunc callback + ) + where E : System.Exception + { + var status = new UniffiRustCallStatus(); + var return_value = callback(ref status); + if (status.IsSuccess()) + { + return return_value; + } + else if (status.IsError()) + { + throw errorHandler.Lift(status.error_buf); + } + else if (status.IsPanic()) + { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) + { + throw new PanicException(FfiConverterString.INSTANCE.Lift(status.error_buf)); + } + else + { + throw new PanicException("Rust panic"); + } + } + else + { + throw new InternalException($"Unknown rust call status: {status.code}"); + } + } + + // Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err + public static void RustCallWithError( + CallStatusErrorHandler errorHandler, + RustCallAction callback + ) + where E : System.Exception + { + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + + // Call a rust function that returns a plain value + public static U RustCall(RustCallFunc callback) + { + return _UniffiHelpers.RustCallWithError(NullCallStatusErrorHandler.INSTANCE, callback); + } + + // Call a rust function that returns a plain value + public static void RustCall(RustCallAction callback) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + callback(ref status); + return 0; + } + ); + } + } + + static class FFIObjectUtil + { + public static void DisposeAll(params Object?[] list) + { + Dispose(list); + } + + // Dispose is implemented by recursive type inspection at runtime. This is because + // generating correct Dispose calls for recursive complex types, e.g. List> + // is quite cumbersome. + private static void Dispose(Object? obj) + { + if (obj == null) + { + return; + } + + if (obj is IDisposable disposable) + { + disposable.Dispose(); + return; + } + + var objType = obj.GetType(); + var typeCode = Type.GetTypeCode(objType); + if (typeCode != TypeCode.Object) + { + return; + } + + var genericArguments = objType.GetGenericArguments(); + if (genericArguments.Length == 0 && !objType.IsArray) + { + return; + } + + if (obj is System.Collections.IDictionary objDictionary) + { + //This extra code tests to not call "Dispose" for a Dictionary() + //for all values as "double" and alike doesn't support interface "IDisposable" + var valuesType = objType.GetGenericArguments()[1]; + var elementValuesTypeCode = Type.GetTypeCode(valuesType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in objDictionary.Values) + { + Dispose(value); + } + } + else if (obj is System.Collections.IEnumerable listValues) + { + //This extra code tests to not call "Dispose" for a List() + //for all keys as "int" and alike doesn't support interface "IDisposable" + var elementType = objType.IsArray ? objType.GetElementType() : genericArguments[0]; + var elementValuesTypeCode = Type.GetTypeCode(elementType); + if (elementValuesTypeCode != TypeCode.Object) + { + return; + } + foreach (var value in listValues) + { + Dispose(value); + } + } + } + } + + // Big endian streams are not yet available in dotnet :'( + // https://github.com/dotnet/runtime/issues/26904 + + class StreamUnderflowException : System.Exception + { + public StreamUnderflowException() { } + } + + static class BigEndianStreamExtensions + { + public static void WriteInt32(this Stream stream, int value, int bytesToWrite = 4) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static void WriteInt64(this Stream stream, long value) + { + int bytesToWrite = 8; +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToWrite]; +#else + byte[] buffer = new byte[bytesToWrite]; +#endif + var posByte = bytesToWrite; + while (posByte != 0) + { + posByte--; + buffer[posByte] = (byte)(value); + value >>= 8; + } + +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public static uint ReadUint32(this Stream stream, int bytesToRead = 4) + { + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + uint result = 0; + uint digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static ulong ReadUInt64(this Stream stream) + { + int bytesToRead = 8; + CheckRemaining(stream, bytesToRead); +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[bytesToRead]; + stream.Read(buffer); +#else + byte[] buffer = new byte[bytesToRead]; + stream.Read(buffer, 0, bytesToRead); +#endif + ulong result = 0; + ulong digitMultiplier = 1; + int posByte = bytesToRead; + while (posByte != 0) + { + posByte--; + result |= buffer[posByte] * digitMultiplier; + digitMultiplier <<= 8; + } + + return result; + } + + public static void CheckRemaining(this Stream stream, int length) + { + if (stream.Length - stream.Position < length) + { + throw new StreamUnderflowException(); + } + } + + public static void ForEach(this T[] items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + } + + class BigEndianStream + { + Stream stream; + + public BigEndianStream(Stream stream) + { + this.stream = stream; + } + + public Stream InnerStream => stream; + + public bool HasRemaining() + { + return (stream.Length - Position) > 0; + } + + public long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public void WriteBytes(byte[] buffer) + { +#if NET8_0_OR_GREATER + stream.Write(buffer); +#else + stream.Write(buffer, 0, buffer.Length); +#endif + } + + public void WriteByte(byte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteSByte(sbyte value) => stream.WriteInt32(value, bytesToWrite: 1); + + public void WriteUShort(ushort value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteShort(short value) => stream.WriteInt32(value, bytesToWrite: 2); + + public void WriteUInt(uint value) => stream.WriteInt32((int)value); + + public void WriteInt(int value) => stream.WriteInt32(value); + + public void WriteULong(ulong value) => stream.WriteInt64((long)value); + + public void WriteLong(long value) => stream.WriteInt64(value); + + public void WriteFloat(float value) + { + unsafe + { + WriteInt(*((int*)&value)); + } + } + + public void WriteDouble(double value) => + stream.WriteInt64(BitConverter.DoubleToInt64Bits(value)); + + public byte[] ReadBytes(int length) + { + stream.CheckRemaining(length); + byte[] result = new byte[length]; + stream.Read(result, 0, length); + return result; + } + + public byte ReadByte() => (byte)stream.ReadUint32(bytesToRead: 1); + + public ushort ReadUShort() => (ushort)stream.ReadUint32(bytesToRead: 2); + + public uint ReadUInt() => (uint)stream.ReadUint32(bytesToRead: 4); + + public ulong ReadULong() => stream.ReadUInt64(); + + public sbyte ReadSByte() => (sbyte)ReadByte(); + + public short ReadShort() => (short)ReadUShort(); + + public int ReadInt() => (int)ReadUInt(); + + public float ReadFloat() + { + unsafe + { + int value = ReadInt(); + return *((float*)&value); + } + } + + public long ReadLong() => (long)ReadULong(); + + public double ReadDouble() => BitConverter.Int64BitsToDouble(ReadLong()); + } + + // Contains loading, initialization code, + // and the FFI Function declarations in a com.sun.jna.Library. + + // This is an implementation detail that will be called internally by the public API. +#if NET8_0_OR_GREATER + static partial class _UniFFILib + { +#else + static class _UniFFILib + { +#endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiRustFutureContinuationCallback(ulong @data, sbyte @pollResult); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureDroppedCallback(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceFree(ulong @handle); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate ulong UniffiCallbackInterfaceClone(ulong @handle); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureDroppedCallbackStruct + { + public ulong @handle; + public IntPtr @free; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU8 + { + public byte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI8 + { + public sbyte @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI8( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI8 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU16 + { + public ushort @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI16 + { + public short @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI16( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI16 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU32 + { + public uint @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI32 + { + public int @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultU64 + { + public ulong @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteU64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultU64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultI64 + { + public long @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteI64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultI64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF32 + { + public float @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF32( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF32 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultF64 + { + public double @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteF64( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultF64 @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultRustBuffer + { + public RustBuffer @returnValue; + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteRustBuffer( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultRustBuffer @result + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiForeignFutureResultVoid + { + public UniffiRustCallStatus @callStatus; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiForeignFutureCompleteVoid( + ulong @callbackData, + _UniFFILib.UniffiForeignFutureResultVoid @result + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceHttpClientMethod0( + ulong @uniffiHandle, + RustBuffer @method, + RustBuffer @url, + RustBuffer @headers, + RustBuffer @body, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceWsClientMethod0( + ulong @uniffiHandle, + RustBuffer @url, + RustBuffer @headers, + ulong @timeoutMs, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceWsConnectionMethod0( + ulong @uniffiHandle, + RustBuffer @frame, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceWsConnectionMethod1( + ulong @uniffiHandle, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceWsConnectionMethod2( + ulong @uniffiHandle, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceHttpClient + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @request; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceWsClient + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @connect; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceWsConnection + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @send; + public IntPtr @recv; + public IntPtr @close; + } + + static _UniFFILib() + { + _UniFFILib.uniffiCheckContractApiVersion(); + _UniFFILib.uniffiCheckApiChecksums(); + + UniffiCallbackInterfaceHttpClient.Register(); + UniffiCallbackInterfaceWsClient.Register(); + UniffiCallbackInterfaceWsConnection.Register(); + } + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_clone_httpclient( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_free_httpclient( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_init_callback_vtable_httpclient( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceHttpClient*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_method_httpclient_request( + ulong @ptr, + RustBuffer @method, + RustBuffer @url, + RustBuffer @headers, + RustBuffer @body + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_clone_wsclient( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_free_wsclient( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_init_callback_vtable_wsclient( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceWsClient*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_method_wsclient_connect( + ulong @ptr, + RustBuffer @url, + RustBuffer @headers, + ulong @timeoutMs + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_clone_wsconnection( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_free_wsconnection( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_init_callback_vtable_wsconnection( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceWsConnection*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_method_wsconnection_send(ulong @ptr, RustBuffer @frame); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_method_wsconnection_recv(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_method_wsconnection_close(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte uniffi_livekit_net_fn_func_has_http_client(ref UniffiRustCallStatus _uniffi_out_err); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte uniffi_livekit_net_fn_func_has_ws_client(ref UniffiRustCallStatus _uniffi_out_err); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_func_self_test_http_get(RustBuffer @url); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_net_fn_func_self_test_ws_echo(RustBuffer @url, RustBuffer @payload); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_func_set_http_client( + ulong @c, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_net_fn_func_set_ws_client( + ulong @c, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_net_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_net_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rustbuffer_free(RustBuffer @buf, ref UniffiRustCallStatus _uniffi_out_err); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_net_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_u8(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_net_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_i8(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_net_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_u16(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_net_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_i16(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_net_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_u32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_net_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_i32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_net_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_u64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_net_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_i64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_net_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_f32(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_net_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_f64(ulong @handle, IntPtr @callback, ulong @callbackData); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_net_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_net_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_net_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_has_http_client(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_has_ws_client(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_self_test_http_get(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_self_test_ws_echo(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_set_http_client(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_func_set_ws_client(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_method_httpclient_request(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_method_wsclient_connect(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_method_wsconnection_send(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_method_wsconnection_recv(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_net_checksum_method_wsconnection_close(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_net_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = _UniFFILib.ffi_livekit_net_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"uniffi.livekit_net: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() + { + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_has_http_client(); + if (checksum != 31874) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_has_http_client` checksum `31874`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_has_ws_client(); + if (checksum != 1427) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_has_ws_client` checksum `1427`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_self_test_http_get(); + if (checksum != 58956) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_self_test_http_get` checksum `58956`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_self_test_ws_echo(); + if (checksum != 54359) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_self_test_ws_echo` checksum `54359`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_set_http_client(); + if (checksum != 2382) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_set_http_client` checksum `2382`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_func_set_ws_client(); + if (checksum != 25157) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_func_set_ws_client` checksum `25157`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_method_httpclient_request(); + if (checksum != 22088) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_method_httpclient_request` checksum `22088`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_method_wsclient_connect(); + if (checksum != 54712) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_method_wsclient_connect` checksum `54712`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_method_wsconnection_send(); + if (checksum != 64949) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_method_wsconnection_send` checksum `64949`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_method_wsconnection_recv(); + if (checksum != 4606) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_method_wsconnection_recv` checksum `4606`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_net_checksum_method_wsconnection_close(); + if (checksum != 13655) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_net: uniffi bindings expected function `uniffi_livekit_net_checksum_method_wsconnection_close` checksum `13655`, library returned `{checksum}`" + ); + } + } + } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterUInt16 : FfiConverter + { + public static FfiConverterUInt16 INSTANCE = new FfiConverterUInt16(); + + public override ushort Lift(ushort value) + { + return value; + } + + public override ushort Read(BigEndianStream stream) + { + return stream.ReadUShort(); + } + + public override ushort Lower(ushort value) + { + return value; + } + + public override int AllocationSize(ushort value) + { + return 2; + } + + public override void Write(ushort value, BigEndianStream stream) + { + stream.WriteUShort(value); + } + } + + class FfiConverterUInt64 : FfiConverter + { + public static FfiConverterUInt64 INSTANCE = new FfiConverterUInt64(); + + public override ulong Lift(ulong value) + { + return value; + } + + public override ulong Read(BigEndianStream stream) + { + return stream.ReadULong(); + } + + public override ulong Lower(ulong value) + { + return value; + } + + public override int AllocationSize(ulong value) + { + return 8; + } + + public override void Write(ulong value, BigEndianStream stream) + { + stream.WriteULong(value); + } + } + + class FfiConverterBoolean : FfiConverter + { + public static FfiConverterBoolean INSTANCE = new FfiConverterBoolean(); + + public override bool Lift(sbyte value) + { + return value != 0; + } + + public override bool Read(BigEndianStream stream) + { + return Lift(stream.ReadSByte()); + } + + public override sbyte Lower(bool value) + { + return value ? (sbyte)1 : (sbyte)0; + } + + public override int AllocationSize(bool value) + { + return (sbyte)1; + } + + public override void Write(bool value, BigEndianStream stream) + { + stream.WriteSByte(Lower(value)); + } + } + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } + + class FfiConverterByteArray : FfiConverterRustBuffer + { + public static FfiConverterByteArray INSTANCE = new FfiConverterByteArray(); + + public override byte[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + return stream.ReadBytes(length); + } + + public override int AllocationSize(byte[] value) + { + return 4 + value.Length; + } + + public override void Write(byte[] value, BigEndianStream stream) + { + stream.WriteInt(value.Length); + stream.WriteBytes(value); + } + } + + /// + /// A host- or Rust-provided HTTP transport. Performs the request/response HTTP + /// calls LiveKit needs (connection validation, region discovery, token endpoints); + /// knows nothing about LiveKit/protobuf. + /// + /// Implementors provide the single [`request`](HttpClient::request) primitive; + /// [`HttpClientExt`] layers `get`/`post` on top, so adding verbs never widens the + /// implementation (or, for foreign impls, the FFI) surface. + /// + public interface HttpClient + { + /// + /// Perform one HTTP request, sending `body` if present. + /// + /// + Task Request(HttpMethod @method, string @url, Header[] @headers, byte[]? @body); + } + + /// + /// A host- or Rust-provided HTTP transport. Performs the request/response HTTP + /// calls LiveKit needs (connection validation, region discovery, token endpoints); + /// knows nothing about LiveKit/protobuf. + /// + /// Implementors provide the single [`request`](HttpClient::request) primitive; + /// [`HttpClientExt`] layers `get`/`post` on top, so adding verbs never widens the + /// implementation (or, for foreign impls, the FFI) surface. + /// + public class HttpClientImpl : HttpClient, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public HttpClientImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~HttpClientImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_net_fn_free_httpclient(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_net_fn_clone_httpclient(this.pointer, ref status); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Perform one HTTP request, sending `body` if present. + /// + /// + public async Task Request( + HttpMethod @method, + string @url, + Header[] @headers, + byte[]? @body + ) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_net_fn_method_httpclient_request( + thisPtr, + FfiConverterTypeHttpMethod.INSTANCE.Lower(@method), + FfiConverterString.INSTANCE.Lower(@url), + FfiConverterSequenceTypeHeader.INSTANCE.Lower(@headers), + FfiConverterOptionalByteArray.INSTANCE.Lower(@body) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_rust_buffer(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_net_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeHttpResponse.INSTANCE.Lift(result), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + } + + class UniffiCallbackInterfaceHttpClient + { + static void Request( + ulong @uniffiHandle, + RustBuffer @method, + RustBuffer @url, + RustBuffer @headers, + RustBuffer @body, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if (!FfiConverterTypeHttpClient.INSTANCE.handleMap.TryGet(handle, out var uniffiObject)) + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + var result = await uniffiObject.Request( + FfiConverterTypeHttpMethod.INSTANCE.Lift(@method), + FfiConverterString.INSTANCE.Lift(@url), + FfiConverterSequenceTypeHeader.INSTANCE.Lift(@headers), + FfiConverterOptionalByteArray.INSTANCE.Lift(@body) + ) +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + ret.@returnValue = FfiConverterTypeHttpResponse.INSTANCE.Lower(result); + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (TransportException e) + { + try + { + ret.@callStatus.code = UniffiCallbackResponseStatus.ERROR; + ret.@callStatus.error_buf = + FfiConverterTypeTransportError.INSTANCE.Lower(e); + } + catch + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeHttpClient.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if (!FfiConverterTypeHttpClient.INSTANCE.handleMap.TryGet(@handle, out var obj)) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeHttpClient.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceHttpClientMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceHttpClientMethod0(Request); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceHttpClient _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceHttpClient + { + @request = Marshal.GetFunctionPointerForDelegate(_m0), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_net_fn_init_callback_vtable_httpclient( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class ConcurrentHandleMap + where T : notnull + { + readonly ConcurrentDictionary _map = new(); + + // Handles are odd numbers (1, 3, 5, ...) — the lowest bit must always be set. + // Rust uses (handle & 1) to distinguish foreign-language handles from Rust Arc + // pointers, which are always even due to memory alignment. See uniffi_core/src/ffi/handle.rs. + const long HANDLE_INITIAL = 1; + const long HANDLE_DELTA = 2; + long _currentHandle = HANDLE_INITIAL - HANDLE_DELTA; + + public ulong Insert(T obj) + { + var handle = (ulong)Interlocked.Add(ref _currentHandle, HANDLE_DELTA); + if (!_map.TryAdd(handle, obj)) + { + throw new InternalException("ConcurrentHandleMap: Duplicate handle"); + } + return handle; + } + + public bool TryGet(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryGetValue(handle, out result); + } + + public T Get(ulong handle) + { + if (_map.TryGetValue(handle, out var result)) + { + return result; + } + else + { + throw new InternalException("ConcurrentHandleMap: Invalid handle"); + } + } + + public bool Remove(ulong handle) + { + return _map.TryRemove(handle, out _); + } + + public bool Remove(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryRemove(handle, out result); + } + } + + static class UniffiCallbackResponseStatus + { + public static sbyte SUCCESS = 0; + public static sbyte ERROR = 1; + public static sbyte UNEXPECTED_ERROR = 2; + } + + class FfiConverterTypeHttpClient : FfiConverter + { + public ConcurrentHandleMap handleMap = new ConcurrentHandleMap(); + + public static FfiConverterTypeHttpClient INSTANCE = new FfiConverterTypeHttpClient(); + + static FfiConverterTypeHttpClient() + { + UniffiCallbackInterfaceHttpClient.Register(); + } + + public override ulong Lower(HttpClient value) + { + if (value is HttpClientImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override HttpClient Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new HttpClientImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override HttpClient Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(HttpClient value) + { + return 8; + } + + public override void Write(HttpClient value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// A host- or Rust-provided WebSocket transport. Opens the LiveKit signalling + /// WebSocket; knows nothing about LiveKit/protobuf. + /// + public interface WsClient + { + /// + /// Open a WebSocket. `url` is the full ws(s):// URL including query string. + /// `timeout_ms` bounds connection establishment. Milliseconds, not `Duration`: + /// a `Duration` parameter breaks uniffi-dart's foreign codegen. + /// + /// + Task Connect(string @url, Header[] @headers, ulong @timeoutMs); + } + + /// + /// A host- or Rust-provided WebSocket transport. Opens the LiveKit signalling + /// WebSocket; knows nothing about LiveKit/protobuf. + /// + public class WsClientImpl : WsClient, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public WsClientImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~WsClientImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_net_fn_free_wsclient(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_net_fn_clone_wsclient(this.pointer, ref status); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Open a WebSocket. `url` is the full ws(s):// URL including query string. + /// `timeout_ms` bounds connection establishment. Milliseconds, not `Duration`: + /// a `Duration` parameter breaks uniffi-dart's foreign codegen. + /// + /// + public async Task Connect(string @url, Header[] @headers, ulong @timeoutMs) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_net_fn_method_wsclient_connect( + thisPtr, + FfiConverterString.INSTANCE.Lower(@url), + FfiConverterSequenceTypeHeader.INSTANCE.Lower(@headers), + FfiConverterUInt64.INSTANCE.Lower(@timeoutMs) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_rust_buffer(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_net_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeWsConnectResult.INSTANCE.Lift(result), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + } + + class UniffiCallbackInterfaceWsClient + { + static void Connect( + ulong @uniffiHandle, + RustBuffer @url, + RustBuffer @headers, + ulong @timeoutMs, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if (!FfiConverterTypeWsClient.INSTANCE.handleMap.TryGet(handle, out var uniffiObject)) + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + var result = await uniffiObject.Connect( + FfiConverterString.INSTANCE.Lift(@url), + FfiConverterSequenceTypeHeader.INSTANCE.Lift(@headers), + FfiConverterUInt64.INSTANCE.Lift(@timeoutMs) + ) +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + ret.@returnValue = FfiConverterTypeWsConnectResult.INSTANCE.Lower(result); + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (TransportException e) + { + try + { + ret.@callStatus.code = UniffiCallbackResponseStatus.ERROR; + ret.@callStatus.error_buf = + FfiConverterTypeTransportError.INSTANCE.Lower(e); + } + catch + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeWsClient.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if (!FfiConverterTypeWsClient.INSTANCE.handleMap.TryGet(@handle, out var obj)) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeWsClient.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceWsClientMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceWsClientMethod0(Connect); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceWsClient _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceWsClient + { + @connect = Marshal.GetFunctionPointerForDelegate(_m0), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_net_fn_init_callback_vtable_wsclient( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeWsClient : FfiConverter + { + public ConcurrentHandleMap handleMap = new ConcurrentHandleMap(); + + public static FfiConverterTypeWsClient INSTANCE = new FfiConverterTypeWsClient(); + + static FfiConverterTypeWsClient() + { + UniffiCallbackInterfaceWsClient.Register(); + } + + public override ulong Lower(WsClient value) + { + if (value is WsClientImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override WsClient Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new WsClientImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override WsClient Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(WsClient value) + { + return 8; + } + + public override void Write(WsClient value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// A single open WebSocket connection. Control frames (ping/pong, close handshake) + /// are the implementation's own responsibility; only binary application frames cross here. + /// + public interface WsConnection + { + /// + /// Send one binary application frame. + /// + /// + Task Send(byte[] @frame); + + /// + /// Receive the next binary frame. `Ok(None)` means the peer/transport closed. + /// + /// + Task Recv(); + + /// + /// Close the connection, sending a close frame if the transport supports it. + /// + Task Close(); + } + + /// + /// A single open WebSocket connection. Control frames (ping/pong, close handshake) + /// are the implementation's own responsibility; only binary application frames cross here. + /// + public class WsConnectionImpl : WsConnection, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public WsConnectionImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~WsConnectionImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_net_fn_free_wsconnection(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_net_fn_clone_wsconnection( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Send one binary application frame. + /// + /// + public async Task Send(byte[] @frame) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_net_fn_method_wsconnection_send( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@frame) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_net_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_void(future), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + + /// + /// Receive the next binary frame. `Ok(None)` means the peer/transport closed. + /// + /// + public async Task Recv() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_net_fn_method_wsconnection_recv(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_rust_buffer(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_net_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalByteArray.INSTANCE.Lift(result), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + + /// + /// Close the connection, sending a close frame if the transport supports it. + /// + public async Task Close() + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_net_fn_method_wsconnection_close(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_net_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_void(future), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class UniffiCallbackInterfaceWsConnection + { + static void Send( + ulong @uniffiHandle, + RustBuffer @frame, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if (!FfiConverterTypeWsConnection.INSTANCE.handleMap.TryGet(handle, out var uniffiObject)) + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + await uniffiObject.Send(FfiConverterByteArray.INSTANCE.Lift(@frame)) +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (TransportException e) + { + try + { + ret.@callStatus.code = UniffiCallbackResponseStatus.ERROR; + ret.@callStatus.error_buf = + FfiConverterTypeTransportError.INSTANCE.Lower(e); + } + catch + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void Recv( + ulong @uniffiHandle, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if (!FfiConverterTypeWsConnection.INSTANCE.handleMap.TryGet(handle, out var uniffiObject)) + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultRustBuffer(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + var result = await uniffiObject.Recv() +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + ret.@returnValue = FfiConverterOptionalByteArray.INSTANCE.Lower(result); + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (TransportException e) + { + try + { + ret.@callStatus.code = UniffiCallbackResponseStatus.ERROR; + ret.@callStatus.error_buf = + FfiConverterTypeTransportError.INSTANCE.Lower(e); + } + catch + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteRustBuffer>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void Close( + ulong @uniffiHandle, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if (!FfiConverterTypeWsConnection.INSTANCE.handleMap.TryGet(handle, out var uniffiObject)) + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + await uniffiObject.Close() +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeWsConnection.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if (!FfiConverterTypeWsConnection.INSTANCE.handleMap.TryGet(@handle, out var obj)) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeWsConnection.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod0(Send); + static _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod1 _m1 = + new _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod1(Recv); + static _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod2 _m2 = + new _UniFFILib.UniffiCallbackInterfaceWsConnectionMethod2(Close); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceWsConnection _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceWsConnection + { + @send = Marshal.GetFunctionPointerForDelegate(_m0), + @recv = Marshal.GetFunctionPointerForDelegate(_m1), + @close = Marshal.GetFunctionPointerForDelegate(_m2), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_net_fn_init_callback_vtable_wsconnection( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeWsConnection : FfiConverter + { + public ConcurrentHandleMap handleMap = new ConcurrentHandleMap(); + + public static FfiConverterTypeWsConnection INSTANCE = new FfiConverterTypeWsConnection(); + + static FfiConverterTypeWsConnection() + { + UniffiCallbackInterfaceWsConnection.Register(); + } + + public override ulong Lower(WsConnection value) + { + if (value is WsConnectionImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override WsConnection Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new WsConnectionImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override WsConnection Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(WsConnection value) + { + return 8; + } + + public override void Write(WsConnection value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// A single HTTP/WebSocket request header. + /// + public record Header(string Name, string Value) { } + + class FfiConverterTypeHeader : FfiConverterRustBuffer
+ { + public static FfiConverterTypeHeader INSTANCE = new FfiConverterTypeHeader(); + + public override Header Read(BigEndianStream stream) + { + return new Header( + Name: FfiConverterString.INSTANCE.Read(stream), + Value: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(Header value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterString.INSTANCE.AllocationSize(value.Value); + } + + public override void Write(Header value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterString.INSTANCE.Write(value.Value, stream); + } + } + + /// + /// The result of an HTTP request performed by the transport. + /// + /// + /// Response headers, in receipt order. Lets callers read e.g. `Cache-Control`. + /// + public record HttpResponse( + ushort Status, + /// + /// Response headers, in receipt order. Lets callers read e.g. `Cache-Control`. + /// + Header[] Headers, + byte[] Body + ) { } + + class FfiConverterTypeHttpResponse : FfiConverterRustBuffer + { + public static FfiConverterTypeHttpResponse INSTANCE = new FfiConverterTypeHttpResponse(); + + public override HttpResponse Read(BigEndianStream stream) + { + return new HttpResponse( + Status: FfiConverterUInt16.INSTANCE.Read(stream), + Headers: FfiConverterSequenceTypeHeader.INSTANCE.Read(stream), + Body: FfiConverterByteArray.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(HttpResponse value) + { + return 0 + + FfiConverterUInt16.INSTANCE.AllocationSize(value.Status) + + FfiConverterSequenceTypeHeader.INSTANCE.AllocationSize(value.Headers) + + FfiConverterByteArray.INSTANCE.AllocationSize(value.Body); + } + + public override void Write(HttpResponse value, BigEndianStream stream) + { + FfiConverterUInt16.INSTANCE.Write(value.Status, stream); + FfiConverterSequenceTypeHeader.INSTANCE.Write(value.Headers, stream); + FfiConverterByteArray.INSTANCE.Write(value.Body, stream); + } + } + + /// + /// The connection opened by [`WsClient::connect`]. + /// + /// A record wrapper, not a bare `Arc`: uniffi 0.31 cannot + /// lift a trait object returned from an async `with_foreign` method. + /// + public record WsConnectResult(WsConnection Connection) : IDisposable + { + public void Dispose() + { + FFIObjectUtil.DisposeAll(this.Connection); + } + } + + class FfiConverterTypeWsConnectResult : FfiConverterRustBuffer + { + public static FfiConverterTypeWsConnectResult INSTANCE = new FfiConverterTypeWsConnectResult(); + + public override WsConnectResult Read(BigEndianStream stream) + { + return new WsConnectResult(Connection: FfiConverterTypeWsConnection.INSTANCE.Read(stream)); + } + + public override int AllocationSize(WsConnectResult value) + { + return 0 + FfiConverterTypeWsConnection.INSTANCE.AllocationSize(value.Connection); + } + + public override void Write(WsConnectResult value, BigEndianStream stream) + { + FfiConverterTypeWsConnection.INSTANCE.Write(value.Connection, stream); + } + } + + /// + /// The HTTP method for an [`HttpClient::request`]. + /// + public enum HttpMethod : int + { + Get, + Post, + } + + class FfiConverterTypeHttpMethod : FfiConverterRustBuffer + { + public static FfiConverterTypeHttpMethod INSTANCE = new FfiConverterTypeHttpMethod(); + + public override HttpMethod Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return HttpMethod.Get; + case 2: + return HttpMethod.Post; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeHttpMethod.Read()", + value + ) + ); + } + } + + public override int AllocationSize(HttpMethod value) + { + return 4; + } + + public override void Write(HttpMethod value, BigEndianStream stream) + { + switch (value) + { + case HttpMethod.Get: + stream.WriteInt(1); + break; + case HttpMethod.Post: + stream.WriteInt(2); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeHttpMethod.Write()", + value + ) + ); + } + } + } + + /// + /// Errors a transport implementation may return. Mapped onto `SignalError` by the caller. + /// + public class TransportException : UniffiException + { + TransportException() + : base() { } + + TransportException(String @Message) + : base(@Message) { } + + // Each variant is a nested class + + public class Timeout : TransportException + { + public Timeout() + : base() { } + } + + public class Connection : TransportException + { + // Members + public string @v1; + + // Constructor + public Connection(string @v1) + : base("@v1" + "=" + @v1) + { + this.@v1 = @v1; + } + } + + public class Http : TransportException + { + // Members + public ushort @status; + + // Constructor + public Http(ushort @status) + : base("@status" + "=" + @status) + { + this.@status = @status; + } + } + + public class Closed : TransportException + { + public Closed() + : base() { } + } + + public class Other : TransportException + { + // Members + public string @v1; + + // Constructor + public Other(string @v1) + : base("@v1" + "=" + @v1) + { + this.@v1 = @v1; + } + } + } + + class FfiConverterTypeTransportError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeTransportError INSTANCE = new FfiConverterTypeTransportError(); + + public override TransportException Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new TransportException.Timeout(); + case 2: + return new TransportException.Connection(FfiConverterString.INSTANCE.Read(stream)); + case 3: + return new TransportException.Http(FfiConverterUInt16.INSTANCE.Read(stream)); + case 4: + return new TransportException.Closed(); + case 5: + return new TransportException.Other(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeTransportError.Read()", + value + ) + ); + } + } + + public override int AllocationSize(TransportException value) + { + switch (value) + { + case TransportException.Timeout variant_value: + return 4; + + case TransportException.Connection variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@v1); + + case TransportException.Http variant_value: + return 4 + FfiConverterUInt16.INSTANCE.AllocationSize(variant_value.@status); + + case TransportException.Closed variant_value: + return 4; + + case TransportException.Other variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@v1); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeTransportError.AllocationSize()", + value + ) + ); + } + } + + public override void Write(TransportException value, BigEndianStream stream) + { + switch (value) + { + case TransportException.Timeout variant_value: + stream.WriteInt(1); + break; + case TransportException.Connection variant_value: + stream.WriteInt(2); + FfiConverterString.INSTANCE.Write(variant_value.@v1, stream); + break; + case TransportException.Http variant_value: + stream.WriteInt(3); + FfiConverterUInt16.INSTANCE.Write(variant_value.@status, stream); + break; + case TransportException.Closed variant_value: + stream.WriteInt(4); + break; + case TransportException.Other variant_value: + stream.WriteInt(5); + FfiConverterString.INSTANCE.Write(variant_value.@v1, stream); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeTransportError.Write()", + value + ) + ); + } + } + } + + class FfiConverterOptionalByteArray : FfiConverterRustBuffer + { + public static FfiConverterOptionalByteArray INSTANCE = new FfiConverterOptionalByteArray(); + + public override byte[]? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterByteArray.INSTANCE.Read(stream); + } + + public override int AllocationSize(byte[]? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterByteArray.INSTANCE.AllocationSize((byte[])value); + } + } + + public override void Write(byte[]? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterByteArray.INSTANCE.Write((byte[])value, stream); + } + } + } + + class FfiConverterSequenceTypeHeader : FfiConverterRustBuffer + { + public static FfiConverterSequenceTypeHeader INSTANCE = new FfiConverterSequenceTypeHeader(); + + public override Header[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + if (length == 0) + { + return System.Array.Empty
(); + } + + var result = new Header[length]; + for (int i = 0; i < length; i++) + { + result[i] = FfiConverterTypeHeader.INSTANCE.Read(stream); + } + return result; + } + + public override int AllocationSize(Header[] value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => FfiConverterTypeHeader.INSTANCE.AllocationSize(item)); + return sizeForLength + sizeForItems; + } + + public override void Write(Header[] value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Length); + value.ForEach(item => FfiConverterTypeHeader.INSTANCE.Write(item, stream)); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate void UniFfiFutureCallback(ulong continuationHandle, byte pollResult); + + internal sealed class UniffiForeignFutureHandle : System.IDisposable + { + internal CancellationTokenSource Cts { get; } = new CancellationTokenSource(); +#if NET9_0_OR_GREATER + private readonly Lock _lock = new Lock(); +#else + private readonly object _lock = new object(); +#endif + + internal void MarkDropped() + { + lock (_lock) + { + if (!_callbackInvoked) + { + Cts.Cancel(); + } + } + } + + private bool _callbackInvoked = false; + + internal void InvokeCallbackOnce(Action invoke) + { + bool shouldInvoke; + lock (_lock) + { + shouldInvoke = !_callbackInvoked; + if (shouldInvoke) + _callbackInvoked = true; + } + if (shouldInvoke) + invoke(); + } + + // Dispose() is called exactly once — from the worker's finally (Task.Run path) or from the + // synchronous early-return path — always after InvokeCallbackOnce has set _callbackInvoked = true. + public void Dispose() + { + Cts.Dispose(); + } + } + + internal static class _UniFFIAsync + { + internal const byte UNIFFI_RUST_FUTURE_POLL_READY = 0; + + // internal const byte UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1; + + internal static ConcurrentHandleMap> _async_handle_map = + new ConcurrentHandleMap>(); + public static ConcurrentHandleMap _foreign_futures_map = + new ConcurrentHandleMap(); + + // FFI type for Rust future continuations + internal class UniffiRustFutureContinuationCallback + { + public static UniFfiFutureCallback callback = Callback; + + public static void Callback(ulong continuationHandle, byte pollResult) + { + if (_async_handle_map.Remove(continuationHandle, out TaskCompletionSource? task)) + { + task.SetResult(pollResult); + } + // else: continuation already completed (e.g. waker called more than once), ignore + } + } + + public class UniffiForeignFutureDroppedCallbackImpl + { + public static _UniFFILib.UniffiForeignFutureDroppedCallback callback = Callback; + + public static void Callback(ulong handle) + { + if ( + _foreign_futures_map.Remove(handle, out UniffiForeignFutureHandle? futureHandle) + && futureHandle is not null + ) + { + futureHandle.MarkDropped(); + } + } + } + + public delegate F CompleteFuncDelegate(ulong handle, ref UniffiRustCallStatus status); + + public delegate void CompleteActionDelegate(ulong handle, ref UniffiRustCallStatus status); + + private static async Task PollFuture(ulong rustFuture, Action pollFunc) + { + byte pollResult; + do + { + var tcs = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously + ); + IntPtr callback = Marshal.GetFunctionPointerForDelegate( + UniffiRustFutureContinuationCallback.callback + ); + ulong mapEntry = _async_handle_map.Insert(tcs); + pollFunc(rustFuture, callback, mapEntry); + pollResult = await tcs.Task; + } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY); + } + + public static async Task UniffiRustCallAsync( + ulong rustFuture, + Action pollFunc, + CompleteFuncDelegate completeFunc, + Action freeFunc, + Func liftFunc, + CallStatusErrorHandler errorHandler + ) + where E : System.Exception + { + try + { + await PollFuture(rustFuture, pollFunc); + var result = _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => completeFunc(rustFuture, ref status) + ); + return liftFunc(result); + } + finally + { + freeFunc(rustFuture); + } + } + + public static async Task UniffiRustCallAsync( + ulong rustFuture, + Action pollFunc, + CompleteActionDelegate completeFunc, + Action freeFunc, + CallStatusErrorHandler errorHandler + ) + where E : System.Exception + { + try + { + await PollFuture(rustFuture, pollFunc); + _UniffiHelpers.RustCallWithError( + errorHandler, + (ref UniffiRustCallStatus status) => completeFunc(rustFuture, ref status) + ); + } + finally + { + freeFunc(rustFuture); + } + } + } +#pragma warning restore 8625 + public static class LivekitNetMethods + { + /// + /// Test probe: whether a host has explicitly registered an HTTP client via + /// [`set_http_client`]. + /// + /// Reports registration, not resolvability: on native builds [`http_client`] + /// still yields the built-in client when this returns `false`. + /// + public static bool HasHttpClient() + { + return FfiConverterBoolean.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_net_fn_func_has_http_client(ref _status) + ) + ); + } + + /// + /// Test probe: whether a host has explicitly registered a WebSocket client via + /// [`set_ws_client`]. + /// + /// Reports registration, not resolvability: on native builds [`ws_client`] still + /// yields the built-in client when this returns `false`. + /// + public static bool HasWsClient() + { + return FfiConverterBoolean.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_net_fn_func_has_ws_client(ref _status) + ) + ); + } + + /// + /// Self-test: GET `url` via the registered HTTP client; returns the full response + /// (status + headers + body) so callers can assert the whole struct round-trips the FFI. + /// Errors if no client is registered or the transport fails. + /// + /// + public static async Task SelfTestHttpGet(string @url) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + _UniFFILib.uniffi_livekit_net_fn_func_self_test_http_get( + FfiConverterString.INSTANCE.Lower(@url) + ), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_rust_buffer(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_net_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeHttpResponse.INSTANCE.Lift(result), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + + /// + /// Self-test: connect, send `payload`, receive one frame, close; return the echoed bytes. + /// Errors if no client is registered, the transport fails, or the peer closes first. + /// + /// + public static async Task SelfTestWsEcho(string @url, byte[] @payload) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + _UniFFILib.uniffi_livekit_net_fn_func_self_test_ws_echo( + FfiConverterString.INSTANCE.Lower(@url), + FfiConverterByteArray.INSTANCE.Lower(@payload) + ), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_net_rust_future_poll_rust_buffer(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_net_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_net_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterByteArray.INSTANCE.Lift(result), + // Error + FfiConverterTypeTransportError.INSTANCE + ); + } + + /// + /// Register the process-wide HTTP client. Call once at startup, before the first + /// request. A later call is ignored (first registration wins). + /// + public static void SetHttpClient(HttpClient @c) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_net_fn_func_set_http_client( + FfiConverterTypeHttpClient.INSTANCE.Lower(@c), + ref _status + ) + ); + } + + /// + /// Register the process-wide WebSocket client. Call once at startup, before the + /// first `connect`. A later call is ignored (first registration wins). + /// + /// Independent of [`set_http_client`]: a consumer that only needs HTTP (e.g. a + /// token source) can register that alone, and vice versa. + /// + public static void SetWsClient(WsClient @c) + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_net_fn_func_set_ws_client( + FfiConverterTypeWsClient.INSTANCE.Lower(@c), + ref _status + ) + ); + } + } +} diff --git a/Runtime/Scripts/UniFFI/livekit_net.cs.meta b/Runtime/Scripts/UniFFI/livekit_net.cs.meta new file mode 100644 index 00000000..2144e558 --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_net.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d86c323a46e8145359a1b38d10c1321a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/UniFFI/livekit_uniffi.cs b/Runtime/Scripts/UniFFI/livekit_uniffi.cs index aab88772..7709e2a9 100644 --- a/Runtime/Scripts/UniFFI/livekit_uniffi.cs +++ b/Runtime/Scripts/UniFFI/livekit_uniffi.cs @@ -16,9 +16,18 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using uniffi.livekit_common; +using uniffi.livekit_datatrack; -namespace LiveKit.Uniffi +namespace uniffi.livekit_uniffi { + // WORKAROUND(uniffi-bindgen-cs): `Bytes` is a custom type of uniffi.livekit_common, the only file uniffi-bindgen-cs + // aliases it in. See Scripts~/uniffi/UPSTREAM-external-custom-type-alias.md + using Bytes = System.ReadOnlyMemory; + + using DataTrackSid = String; + using FfiConverterTypeDataTrackSid = FfiConverterString; + // This is a helper for safely working with byte buffers returned from the Rust code. // A rust-owned buffer is represented by its capacity, its current length, and a // pointer to the underlying data. @@ -843,24 +852,167 @@ public delegate void UniffiForeignFutureCompleteVoid( _UniFFILib.UniffiForeignFutureResultVoid @result ); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod0( + ulong @uniffiHandle, + ulong @reader, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod1( + ulong @uniffiHandle, + ulong @reader, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod2( + ulong @uniffiHandle, + RustBuffer @streamId, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceOutgoingDataStreamManagerDelegateMethod0( + ulong @uniffiHandle, + RustBuffer @packets, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod0( + ulong @uniffiHandle, + RustBuffer @identity, + IntPtr /*int*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod1( + ulong @uniffiHandle, + RustBuffer @identity, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod2( + ulong @uniffiHandle, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod0( + ulong @uniffiHandle, + RustBuffer @request, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod1( + ulong @uniffiHandle, + RustBuffer @packets, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod0( + ulong @uniffiHandle, + RustBuffer @request, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod1( + ulong @uniffiHandle, + ulong @track, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod2( + ulong @uniffiHandle, + RustBuffer @sid, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ); + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceIncomingDataStreamManagerDelegate + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @onByteStreamOpened; + public IntPtr @onTextStreamOpened; + public IntPtr @onStreamClosed; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceOutgoingDataStreamManagerDelegate + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @onPacketsAvailable; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceRemoteParticipantRegistryDelegate + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @remoteClientProtocol; + public IntPtr @remoteCapabilities; + public IntPtr @remoteIdentities; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceLocalDataTrackManagerDelegate + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @onSignalRequest; + public IntPtr @onPacketsAvailable; + } + + [StructLayout(LayoutKind.Sequential)] + public struct UniffiVTableCallbackInterfaceRemoteDataTrackManagerDelegate + { + public IntPtr @uniffiFree; + public IntPtr @uniffiClone; + public IntPtr @onSignalRequest; + public IntPtr @onTrackPublished; + public IntPtr @onTrackUnpublished; + } + static _UniFFILib() { _UniFFILib.uniffiCheckContractApiVersion(); _UniFFILib.uniffiCheckApiChecksums(); - } -#if NET8_0_OR_GREATER - [LibraryImport("livekit_uniffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - RustBuffer uniffi_livekit_uniffi_fn_func_token_claims_from_unverified( - RustBuffer @token, - ref UniffiRustCallStatus _uniffi_out_err - ); + UniffiCallbackInterfaceIncomingDataStreamManagerDelegate.Register(); + UniffiCallbackInterfaceLocalDataTrackManagerDelegate.Register(); + UniffiCallbackInterfaceOutgoingDataStreamManagerDelegate.Register(); + UniffiCallbackInterfaceRemoteDataTrackManagerDelegate.Register(); + UniffiCallbackInterfaceRemoteParticipantRegistryDelegate.Register(); + } #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -870,9 +1022,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer uniffi_livekit_uniffi_fn_func_token_generate( - RustBuffer @options, - RustBuffer @credentials, + ulong uniffi_livekit_uniffi_fn_clone_bytestreamreader( + ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -884,9 +1035,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer uniffi_livekit_uniffi_fn_func_token_verify( - RustBuffer @token, - RustBuffer @credentials, + void uniffi_livekit_uniffi_fn_free_bytestreamreader( + ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -898,7 +1048,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer uniffi_livekit_uniffi_fn_func_build_version( + RustBuffer uniffi_livekit_uniffi_fn_method_bytestreamreader_info( + ulong @ptr, ref UniffiRustCallStatus _uniffi_out_err ); @@ -910,10 +1061,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void uniffi_livekit_uniffi_fn_func_log_forward_bootstrap( - RustBuffer @level, - ref UniffiRustCallStatus _uniffi_out_err - ); + ulong uniffi_livekit_uniffi_fn_method_bytestreamreader_next(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -923,7 +1071,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ulong uniffi_livekit_uniffi_fn_func_log_forward_receive(); + ulong uniffi_livekit_uniffi_fn_method_bytestreamreader_read_all(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -933,8 +1081,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer ffi_livekit_uniffi_rustbuffer_alloc( - ulong @size, + ulong uniffi_livekit_uniffi_fn_clone_incomingdatastreammanager( + ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -946,8 +1094,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer ffi_livekit_uniffi_rustbuffer_from_bytes( - ForeignBytes @bytes, + void uniffi_livekit_uniffi_fn_free_incomingdatastreammanager( + ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -959,8 +1107,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rustbuffer_free( - RustBuffer @buf, + ulong uniffi_livekit_uniffi_fn_constructor_incomingdatastreammanager_new( + ulong @delegate, + RustBuffer @maxPayloadByteLength, ref UniffiRustCallStatus _uniffi_out_err ); @@ -972,9 +1121,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer ffi_livekit_uniffi_rustbuffer_reserve( - RustBuffer @buf, - ulong @additional, + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_abort_all_streams( + ulong @ptr, ref UniffiRustCallStatus _uniffi_out_err ); @@ -986,10 +1134,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_u8( - ulong @handle, - IntPtr @callback, - ulong @callbackData + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_abort_streams_from( + ulong @ptr, + RustBuffer @identity, + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1000,7 +1148,12 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_u8(ulong @handle); + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_handle_packet_received( + ulong @ptr, + RustBuffer @packet, + RustBuffer @encryptionType, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1010,7 +1163,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_u8(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_open_stream_count(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1020,7 +1173,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - byte ffi_livekit_uniffi_rust_future_complete_u8( + ulong uniffi_livekit_uniffi_fn_clone_incomingdatastreammanagerdelegate( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1033,10 +1186,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_i8( + void uniffi_livekit_uniffi_fn_free_incomingdatastreammanagerdelegate( ulong @handle, - IntPtr @callback, - ulong @callbackData + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1047,17 +1199,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_i8(ulong @handle); - -#if NET8_0_OR_GREATER - [LibraryImport("livekit_uniffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - void ffi_livekit_uniffi_rust_future_free_i8(ulong @handle); + void uniffi_livekit_uniffi_fn_init_callback_vtable_incomingdatastreammanagerdelegate( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceIncomingDataStreamManagerDelegate*/ + @vtable + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1067,8 +1212,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - sbyte ffi_livekit_uniffi_rust_future_complete_i8( - ulong @handle, + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_byte_stream_opened( + ulong @ptr, + ulong @reader, + RustBuffer @identity, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1080,10 +1227,11 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_u16( - ulong @handle, - IntPtr @callback, - ulong @callbackData + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_text_stream_opened( + ulong @ptr, + ulong @reader, + RustBuffer @identity, + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1094,7 +1242,12 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_u16(ulong @handle); + void uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_stream_closed( + ulong @ptr, + RustBuffer @streamId, + RustBuffer @identity, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1104,7 +1257,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_u16(ulong @handle); + ulong uniffi_livekit_uniffi_fn_clone_textstreamreader( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1114,7 +1270,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort ffi_livekit_uniffi_rust_future_complete_u16( + void uniffi_livekit_uniffi_fn_free_textstreamreader( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1127,10 +1283,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_i16( - ulong @handle, - IntPtr @callback, - ulong @callbackData + RustBuffer uniffi_livekit_uniffi_fn_method_textstreamreader_info( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1141,7 +1296,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_i16(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_textstreamreader_next(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1151,7 +1306,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_i16(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_textstreamreader_read_all(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1161,7 +1316,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - short ffi_livekit_uniffi_rust_future_complete_i16( + ulong uniffi_livekit_uniffi_fn_clone_bytestreamwriter( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1174,10 +1329,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_u32( + void uniffi_livekit_uniffi_fn_free_bytestreamwriter( ulong @handle, - IntPtr @callback, - ulong @callbackData + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1188,7 +1342,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_u32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_bytestreamwriter_close(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1198,7 +1352,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_u32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_bytestreamwriter_close_with_reason( + ulong @ptr, + RustBuffer @reason + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1208,8 +1365,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - uint ffi_livekit_uniffi_rust_future_complete_u32( - ulong @handle, + RustBuffer uniffi_livekit_uniffi_fn_method_bytestreamwriter_info( + ulong @ptr, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1221,11 +1378,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_i32( - ulong @handle, - IntPtr @callback, - ulong @callbackData - ); + ulong uniffi_livekit_uniffi_fn_method_bytestreamwriter_is_open(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1235,7 +1388,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_i32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_bytestreamwriter_write(ulong @ptr, RustBuffer @data); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1245,7 +1398,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_i32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_clone_outgoingdatastreammanager( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1255,7 +1411,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - int ffi_livekit_uniffi_rust_future_complete_i32( + void uniffi_livekit_uniffi_fn_free_outgoingdatastreammanager( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1268,10 +1424,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_u64( - ulong @handle, - IntPtr @callback, - ulong @callbackData + ulong uniffi_livekit_uniffi_fn_constructor_outgoingdatastreammanager_new( + ulong @delegate, + ulong @registry, + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1282,7 +1438,11 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_u64(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_bytes( + ulong @ptr, + RustBuffer @data, + RustBuffer @options + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1292,7 +1452,11 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_u64(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_file( + ulong @ptr, + RustBuffer @path, + RustBuffer @options + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1302,9 +1466,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ulong ffi_livekit_uniffi_rust_future_complete_u64( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_text( + ulong @ptr, + RustBuffer @text, + RustBuffer @options ); #if NET8_0_OR_GREATER @@ -1315,10 +1480,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_i64( - ulong @handle, - IntPtr @callback, - ulong @callbackData + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_stream_bytes( + ulong @ptr, + RustBuffer @options ); #if NET8_0_OR_GREATER @@ -1329,7 +1493,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_i64(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_stream_text( + ulong @ptr, + RustBuffer @options + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1339,7 +1506,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_i64(ulong @handle); + ulong uniffi_livekit_uniffi_fn_clone_outgoingdatastreammanagerdelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1349,7 +1519,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - long ffi_livekit_uniffi_rust_future_complete_i64( + void uniffi_livekit_uniffi_fn_free_outgoingdatastreammanagerdelegate( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1362,10 +1532,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_f32( - ulong @handle, - IntPtr @callback, - ulong @callbackData + void uniffi_livekit_uniffi_fn_init_callback_vtable_outgoingdatastreammanagerdelegate( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceOutgoingDataStreamManagerDelegate*/ + @vtable ); #if NET8_0_OR_GREATER @@ -1376,7 +1545,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_f32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_outgoingdatastreammanagerdelegate_on_packets_available( + ulong @ptr, + RustBuffer @packets + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1386,7 +1558,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_f32(ulong @handle); + ulong uniffi_livekit_uniffi_fn_clone_remoteparticipantregistrydelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1396,7 +1571,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - float ffi_livekit_uniffi_rust_future_complete_f32( + void uniffi_livekit_uniffi_fn_free_remoteparticipantregistrydelegate( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1409,10 +1584,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_f64( - ulong @handle, - IntPtr @callback, - ulong @callbackData + void uniffi_livekit_uniffi_fn_init_callback_vtable_remoteparticipantregistrydelegate( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceRemoteParticipantRegistryDelegate*/ + @vtable ); #if NET8_0_OR_GREATER @@ -1423,7 +1597,11 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_f64(ulong @handle); + int uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_client_protocol( + ulong @ptr, + RustBuffer @identity, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1433,7 +1611,11 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_f64(ulong @handle); + RustBuffer uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_capabilities( + ulong @ptr, + RustBuffer @identity, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1443,8 +1625,8 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - double ffi_livekit_uniffi_rust_future_complete_f64( - ulong @handle, + RustBuffer uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_identities( + ulong @ptr, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1456,10 +1638,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_rust_buffer( + ulong uniffi_livekit_uniffi_fn_clone_textstreamwriter( ulong @handle, - IntPtr @callback, - ulong @callbackData + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1470,7 +1651,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_rust_buffer(ulong @handle); + void uniffi_livekit_uniffi_fn_free_textstreamwriter( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1480,7 +1664,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_rust_buffer(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_textstreamwriter_close(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1490,9 +1674,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - RustBuffer ffi_livekit_uniffi_rust_future_complete_rust_buffer( - ulong @handle, - ref UniffiRustCallStatus _uniffi_out_err + ulong uniffi_livekit_uniffi_fn_method_textstreamwriter_close_with_reason( + ulong @ptr, + RustBuffer @reason ); #if NET8_0_OR_GREATER @@ -1503,10 +1687,9 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_poll_void( - ulong @handle, - IntPtr @callback, - ulong @callbackData + RustBuffer uniffi_livekit_uniffi_fn_method_textstreamwriter_info( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err ); #if NET8_0_OR_GREATER @@ -1517,7 +1700,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_cancel_void(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_textstreamwriter_is_open(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1527,7 +1710,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_free_void(ulong @handle); + ulong uniffi_livekit_uniffi_fn_method_textstreamwriter_write(ulong @ptr, RustBuffer @text); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1537,7 +1720,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - void ffi_livekit_uniffi_rust_future_complete_void( + ulong uniffi_livekit_uniffi_fn_clone_incomingstreamqueue( ulong @handle, ref UniffiRustCallStatus _uniffi_out_err ); @@ -1550,7 +1733,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); + void uniffi_livekit_uniffi_fn_free_incomingstreamqueue( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1560,7 +1746,10 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort uniffi_livekit_uniffi_checksum_func_token_generate(); + void uniffi_livekit_uniffi_fn_method_incomingstreamqueue_close( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1570,7 +1759,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort uniffi_livekit_uniffi_checksum_func_token_verify(); + ulong uniffi_livekit_uniffi_fn_method_incomingstreamqueue_next_closed_stream(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1580,7 +1769,7 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort uniffi_livekit_uniffi_checksum_func_build_version(); + ulong uniffi_livekit_uniffi_fn_method_incomingstreamqueue_next_opened_stream(ulong @ptr); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1590,7 +1779,1436 @@ public static partial [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] public static extern #endif - ushort uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); + ulong uniffi_livekit_uniffi_fn_clone_outgoingpacketqueue( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_outgoingpacketqueue( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_outgoingpacketqueue_close( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_outgoingpacketqueue_next_packets(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_localdatatrack( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_localdatatrack( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_method_localdatatrack_info( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte uniffi_livekit_uniffi_fn_method_localdatatrack_is_published( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrack_try_push( + ulong @ptr, + RustBuffer @frame, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrack_unpublish( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_localdatatrack_wait_for_unpublish(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_localdatatrackmanager( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_localdatatrackmanager( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_constructor_localdatatrackmanager_new( + ulong @delegate, + RustBuffer @encryptionProvider, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrackmanager_handle_sfu_publish_response( + ulong @ptr, + RustBuffer @res, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrackmanager_handle_sfu_request_response( + ulong @ptr, + RustBuffer @res, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_localdatatrackmanager_publish_responses_for_sync_state( + ulong @ptr + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_localdatatrackmanager_publish_track( + ulong @ptr, + RustBuffer @options + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrackmanager_republish_tracks( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_localdatatrackmanagerdelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_localdatatrackmanagerdelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_init_callback_vtable_localdatatrackmanagerdelegate( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceLocalDataTrackManagerDelegate*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrackmanagerdelegate_on_signal_request( + ulong @ptr, + RustBuffer @request, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_localdatatrackmanagerdelegate_on_packets_available( + ulong @ptr, + RustBuffer @packets, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_datatrackstream( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_datatrackstream( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_datatrackstream_next(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_remotedatatrack( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_remotedatatrack( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_method_remotedatatrack_info( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte uniffi_livekit_uniffi_fn_method_remotedatatrack_is_published( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_method_remotedatatrack_publisher_identity( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrack_set_pipeline_options( + ulong @ptr, + RustBuffer @options, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_remotedatatrack_subscribe(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_remotedatatrack_subscribe_with_options( + ulong @ptr, + RustBuffer @options + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_method_remotedatatrack_wait_for_unpublish(ulong @ptr); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_remotedatatrackmanager( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_remotedatatrackmanager( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_constructor_remotedatatrackmanager_new( + ulong @delegate, + RustBuffer @decryptionProvider, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_packet_received( + ulong @ptr, + RustBuffer @packet, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_sfu_join_response( + ulong @ptr, + RustBuffer @res, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_sfu_participant_update( + ulong @ptr, + RustBuffer @res, + RustBuffer @localParticipantIdentity, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_subscriber_handles( + ulong @ptr, + RustBuffer @res, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_resend_subscription_updates( + ulong @ptr, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_clone_remotedatatrackmanagerdelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_free_remotedatatrackmanagerdelegate( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_init_callback_vtable_remotedatatrackmanagerdelegate( + IntPtr /*_UniFFILib.UniffiVTableCallbackInterfaceRemoteDataTrackManagerDelegate*/ + @vtable + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_signal_request( + ulong @ptr, + RustBuffer @request, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_track_published( + ulong @ptr, + ulong @track, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_track_unpublished( + ulong @ptr, + RustBuffer @sid, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_claims_from_unverified( + RustBuffer @token, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_generate( + RustBuffer @options, + RustBuffer @credentials, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_token_verify( + RustBuffer @token, + RustBuffer @credentials, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_build_version( + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_polled_incoming_data_stream_manager( + RustBuffer @maxPayloadByteLength, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer uniffi_livekit_uniffi_fn_func_polled_outgoing_data_stream_manager( + ulong @registry, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void uniffi_livekit_uniffi_fn_func_log_forward_bootstrap( + RustBuffer @level, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong uniffi_livekit_uniffi_fn_func_log_forward_receive(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_alloc( + ulong @size, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_from_bytes( + ForeignBytes @bytes, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rustbuffer_free( + RustBuffer @buf, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rustbuffer_reserve( + RustBuffer @buf, + ulong @additional, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + byte ffi_livekit_uniffi_rust_future_complete_u8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i8( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i8(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + sbyte ffi_livekit_uniffi_rust_future_complete_i8( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort ffi_livekit_uniffi_rust_future_complete_u16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i16( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i16(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + short ffi_livekit_uniffi_rust_future_complete_i16( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_uniffi_rust_future_complete_u32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + int ffi_livekit_uniffi_rust_future_complete_i32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_u64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_u64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ulong ffi_livekit_uniffi_rust_future_complete_u64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_i64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_i64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + long ffi_livekit_uniffi_rust_future_complete_i64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_f32( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_f32(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + float ffi_livekit_uniffi_rust_future_complete_f32( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_f64( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_f64(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + double ffi_livekit_uniffi_rust_future_complete_f64( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_rust_buffer( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_rust_buffer(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + RustBuffer ffi_livekit_uniffi_rust_future_complete_rust_buffer( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_poll_void( + ulong @handle, + IntPtr @callback, + ulong @callbackData + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_cancel_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_free_void(ulong @handle); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + void ffi_livekit_uniffi_rust_future_complete_void( + ulong @handle, + ref UniffiRustCallStatus _uniffi_out_err + ); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_generate(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_token_verify(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_build_version(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_polled_incoming_data_stream_manager(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_polled_outgoing_data_stream_manager(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); #if NET8_0_OR_GREATER [LibraryImport("livekit_uniffi")] @@ -1602,1014 +3220,10370 @@ public static extern #endif ushort uniffi_livekit_uniffi_checksum_func_log_forward_receive(); -#if NET8_0_OR_GREATER - [LibraryImport("livekit_uniffi")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - public static partial -#else - [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] - public static extern -#endif - uint ffi_livekit_uniffi_uniffi_contract_version(); +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamreader_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamreader_next(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamreader_read_all(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_all_streams(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_streams_from(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_handle_packet_received(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_open_stream_count(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_byte_stream_opened(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_text_stream_opened(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_stream_closed(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamreader_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamreader_next(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamreader_read_all(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close_with_reason(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamwriter_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamwriter_is_open(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_bytestreamwriter_write(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_bytes(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_file(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_text(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_bytes(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_text(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanagerdelegate_on_packets_available(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_client_protocol(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_capabilities(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_identities(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamwriter_close(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamwriter_close_with_reason(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamwriter_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamwriter_is_open(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_textstreamwriter_write(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_close(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_closed_stream(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_opened_stream(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_close(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_next_packets(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrack_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrack_is_published(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrack_try_push(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrack_unpublish(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrack_wait_for_unpublish(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_publish_response(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_request_response(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_responses_for_sync_state(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_track(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_republish_tracks(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_signal_request(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_packets_available(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_datatrackstream_next(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_info(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_is_published(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_publisher_identity(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_set_pipeline_options(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe_with_options(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrack_wait_for_unpublish(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_packet_received(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_join_response(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_participant_update(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_subscriber_handles(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_resend_subscription_updates(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_signal_request(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_published(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_unpublished(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_constructor_incomingdatastreammanager_new(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_constructor_outgoingdatastreammanager_new(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_constructor_localdatatrackmanager_new(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + ushort uniffi_livekit_uniffi_checksum_constructor_remotedatatrackmanager_new(); + +#if NET8_0_OR_GREATER + [LibraryImport("livekit_uniffi")] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] + public static partial +#else + [DllImport("livekit_uniffi", CallingConvention = CallingConvention.Cdecl)] + public static extern +#endif + uint ffi_livekit_uniffi_uniffi_contract_version(); + + static void uniffiCheckContractApiVersion() + { + var scaffolding_contract_version = _UniFFILib.ffi_livekit_uniffi_uniffi_contract_version(); + if (30 != scaffolding_contract_version) + { + throw new UniffiContractVersionException( + $"uniffi.livekit_uniffi: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + ); + } + } + + static void uniffiCheckApiChecksums() + { + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); + if (checksum != 50037) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified` checksum `50037`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_generate(); + if (checksum != 61939) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_generate` checksum `61939`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_verify(); + if (checksum != 22102) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_verify` checksum `22102`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_build_version(); + if (checksum != 21480) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_build_version` checksum `21480`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_func_polled_incoming_data_stream_manager(); + if (checksum != 59681) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_polled_incoming_data_stream_manager` checksum `59681`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_func_polled_outgoing_data_stream_manager(); + if (checksum != 16176) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_polled_outgoing_data_stream_manager` checksum `16176`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); + if (checksum != 25458) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap` checksum `25458`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_receive(); + if (checksum != 58503) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_receive` checksum `58503`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamreader_info(); + if (checksum != 10285) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamreader_info` checksum `10285`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamreader_next(); + if (checksum != 62806) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamreader_next` checksum `62806`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamreader_read_all(); + if (checksum != 8858) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamreader_read_all` checksum `8858`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_all_streams(); + if (checksum != 37772) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_all_streams` checksum `37772`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_streams_from(); + if (checksum != 27277) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_abort_streams_from` checksum `27277`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_handle_packet_received(); + if (checksum != 28698) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_handle_packet_received` checksum `28698`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_open_stream_count(); + if (checksum != 51052) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanager_open_stream_count` checksum `51052`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_byte_stream_opened(); + if (checksum != 16413) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_byte_stream_opened` checksum `16413`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_text_stream_opened(); + if (checksum != 64529) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_text_stream_opened` checksum `64529`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_stream_closed(); + if (checksum != 35314) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingdatastreammanagerdelegate_on_stream_closed` checksum `35314`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamreader_info(); + if (checksum != 6861) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamreader_info` checksum `6861`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamreader_next(); + if (checksum != 56274) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamreader_next` checksum `56274`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamreader_read_all(); + if (checksum != 26313) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamreader_read_all` checksum `26313`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close(); + if (checksum != 39404) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close` checksum `39404`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close_with_reason(); + if (checksum != 54863) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamwriter_close_with_reason` checksum `54863`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamwriter_info(); + if (checksum != 5482) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamwriter_info` checksum `5482`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamwriter_is_open(); + if (checksum != 18526) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamwriter_is_open` checksum `18526`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_bytestreamwriter_write(); + if (checksum != 51985) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_bytestreamwriter_write` checksum `51985`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_bytes(); + if (checksum != 36743) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_bytes` checksum `36743`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_file(); + if (checksum != 17211) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_file` checksum `17211`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_text(); + if (checksum != 44627) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_send_text` checksum `44627`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_bytes(); + if (checksum != 17761) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_bytes` checksum `17761`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_text(); + if (checksum != 34690) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanager_stream_text` checksum `34690`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanagerdelegate_on_packets_available(); + if (checksum != 2572) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingdatastreammanagerdelegate_on_packets_available` checksum `2572`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_client_protocol(); + if (checksum != 48134) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_client_protocol` checksum `48134`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_capabilities(); + if (checksum != 2201) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_capabilities` checksum `2201`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_identities(); + if (checksum != 42803) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remoteparticipantregistrydelegate_remote_identities` checksum `42803`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamwriter_close(); + if (checksum != 43279) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamwriter_close` checksum `43279`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamwriter_close_with_reason(); + if (checksum != 39023) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamwriter_close_with_reason` checksum `39023`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamwriter_info(); + if (checksum != 27425) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamwriter_info` checksum `27425`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamwriter_is_open(); + if (checksum != 24885) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamwriter_is_open` checksum `24885`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_textstreamwriter_write(); + if (checksum != 50876) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_textstreamwriter_write` checksum `50876`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_close(); + if (checksum != 4954) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_close` checksum `4954`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_closed_stream(); + if (checksum != 10337) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_closed_stream` checksum `10337`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_opened_stream(); + if (checksum != 44884) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_incomingstreamqueue_next_opened_stream` checksum `44884`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_close(); + if (checksum != 35032) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_close` checksum `35032`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_next_packets(); + if (checksum != 15206) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_outgoingpacketqueue_next_packets` checksum `15206`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrack_info(); + if (checksum != 60151) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrack_info` checksum `60151`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrack_is_published(); + if (checksum != 760) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrack_is_published` checksum `760`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrack_try_push(); + if (checksum != 40290) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrack_try_push` checksum `40290`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrack_unpublish(); + if (checksum != 40300) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrack_unpublish` checksum `40300`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrack_wait_for_unpublish(); + if (checksum != 39190) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrack_wait_for_unpublish` checksum `39190`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_publish_response(); + if (checksum != 52069) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_publish_response` checksum `52069`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_request_response(); + if (checksum != 14552) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_handle_sfu_request_response` checksum `14552`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_responses_for_sync_state(); + if (checksum != 22678) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_responses_for_sync_state` checksum `22678`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_track(); + if (checksum != 31959) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_publish_track` checksum `31959`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_republish_tracks(); + if (checksum != 2671) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanager_republish_tracks` checksum `2671`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_signal_request(); + if (checksum != 28972) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_signal_request` checksum `28972`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_packets_available(); + if (checksum != 19578) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_localdatatrackmanagerdelegate_on_packets_available` checksum `19578`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_datatrackstream_next(); + if (checksum != 35219) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_datatrackstream_next` checksum `35219`, library returned `{checksum}`" + ); + } + } + { + var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_info(); + if (checksum != 11559) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_info` checksum `11559`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_is_published(); + if (checksum != 63127) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_is_published` checksum `63127`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_publisher_identity(); + if (checksum != 40711) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_publisher_identity` checksum `40711`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_set_pipeline_options(); + if (checksum != 47812) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_set_pipeline_options` checksum `47812`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe(); + if (checksum != 23718) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe` checksum `23718`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe_with_options(); + if (checksum != 44043) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_subscribe_with_options` checksum `44043`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrack_wait_for_unpublish(); + if (checksum != 30281) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrack_wait_for_unpublish` checksum `30281`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_packet_received(); + if (checksum != 20834) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_packet_received` checksum `20834`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_join_response(); + if (checksum != 40603) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_join_response` checksum `40603`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_participant_update(); + if (checksum != 65460) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_sfu_participant_update` checksum `65460`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_subscriber_handles(); + if (checksum != 10984) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_handle_subscriber_handles` checksum `10984`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_resend_subscription_updates(); + if (checksum != 45173) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanager_resend_subscription_updates` checksum `45173`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_signal_request(); + if (checksum != 17633) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_signal_request` checksum `17633`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_published(); + if (checksum != 34810) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_published` checksum `34810`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_unpublished(); + if (checksum != 204) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_method_remotedatatrackmanagerdelegate_on_track_unpublished` checksum `204`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_constructor_incomingdatastreammanager_new(); + if (checksum != 27033) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_constructor_incomingdatastreammanager_new` checksum `27033`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_constructor_outgoingdatastreammanager_new(); + if (checksum != 53243) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_constructor_outgoingdatastreammanager_new` checksum `53243`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_constructor_localdatatrackmanager_new(); + if (checksum != 49140) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_constructor_localdatatrackmanager_new` checksum `49140`, library returned `{checksum}`" + ); + } + } + { + var checksum = + _UniFFILib.uniffi_livekit_uniffi_checksum_constructor_remotedatatrackmanager_new(); + if (checksum != 42521) + { + throw new UniffiContractChecksumException( + $"uniffi.livekit_uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_constructor_remotedatatrackmanager_new` checksum `42521`, library returned `{checksum}`" + ); + } + } + } + } + + // Public interface members begin here. + +#pragma warning disable 8625 + + class FfiConverterUInt32 : FfiConverter + { + public static FfiConverterUInt32 INSTANCE = new FfiConverterUInt32(); + + public override uint Lift(uint value) + { + return value; + } + + public override uint Read(BigEndianStream stream) + { + return stream.ReadUInt(); + } + + public override uint Lower(uint value) + { + return value; + } + + public override int AllocationSize(uint value) + { + return 4; + } + + public override void Write(uint value, BigEndianStream stream) + { + stream.WriteUInt(value); + } + } + + class FfiConverterInt32 : FfiConverter + { + public static FfiConverterInt32 INSTANCE = new FfiConverterInt32(); + + public override int Lift(int value) + { + return value; + } + + public override int Read(BigEndianStream stream) + { + return stream.ReadInt(); + } + + public override int Lower(int value) + { + return value; + } + + public override int AllocationSize(int value) + { + return 4; + } + + public override void Write(int value, BigEndianStream stream) + { + stream.WriteInt(value); + } + } + + class FfiConverterUInt64 : FfiConverter + { + public static FfiConverterUInt64 INSTANCE = new FfiConverterUInt64(); + + public override ulong Lift(ulong value) + { + return value; + } + + public override ulong Read(BigEndianStream stream) + { + return stream.ReadULong(); + } + + public override ulong Lower(ulong value) + { + return value; + } + + public override int AllocationSize(ulong value) + { + return 8; + } + + public override void Write(ulong value, BigEndianStream stream) + { + stream.WriteULong(value); + } + } + + class FfiConverterInt64 : FfiConverter + { + public static FfiConverterInt64 INSTANCE = new FfiConverterInt64(); + + public override long Lift(long value) + { + return value; + } + + public override long Read(BigEndianStream stream) + { + return stream.ReadLong(); + } + + public override long Lower(long value) + { + return value; + } + + public override int AllocationSize(long value) + { + return 8; + } + + public override void Write(long value, BigEndianStream stream) + { + stream.WriteLong(value); + } + } + + class FfiConverterBoolean : FfiConverter + { + public static FfiConverterBoolean INSTANCE = new FfiConverterBoolean(); + + public override bool Lift(sbyte value) + { + return value != 0; + } + + public override bool Read(BigEndianStream stream) + { + return Lift(stream.ReadSByte()); + } + + public override sbyte Lower(bool value) + { + return value ? (sbyte)1 : (sbyte)0; + } + + public override int AllocationSize(bool value) + { + return (sbyte)1; + } + + public override void Write(bool value, BigEndianStream stream) + { + stream.WriteSByte(Lower(value)); + } + } + + class FfiConverterString : FfiConverter + { + public static FfiConverterString INSTANCE = new FfiConverterString(); + + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + public override string Lift(RustBuffer value) + { + try + { + var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); + return System.Text.Encoding.UTF8.GetString(bytes); + } + finally + { + RustBuffer.Free(value); + } + } + + public override string Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + var bytes = stream.ReadBytes(length); + return System.Text.Encoding.UTF8.GetString(bytes); + } + + public override RustBuffer Lower(string value) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + var rbuf = RustBuffer.Alloc(bytes.Length); + rbuf.AsWriteableStream().WriteBytes(bytes); + return rbuf; + } + + // TODO(CS) + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per unicode codepoint which will always be + // enough. + public override int AllocationSize(string value) + { + const int sizeForLength = 4; + var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); + return sizeForLength + sizeForString; + } + + public override void Write(string value, BigEndianStream stream) + { + var bytes = System.Text.Encoding.UTF8.GetBytes(value); + stream.WriteInt(bytes.Length); + stream.WriteBytes(bytes); + } + } + + class FfiConverterByteArray : FfiConverterRustBuffer + { + public static FfiConverterByteArray INSTANCE = new FfiConverterByteArray(); + + public override byte[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + return stream.ReadBytes(length); + } + + public override int AllocationSize(byte[] value) + { + return 4 + value.Length; + } + + public override void Write(byte[] value, BigEndianStream stream) + { + stream.WriteInt(value.Length); + stream.WriteBytes(value); + } + } + + class FfiConverterDuration : FfiConverterRustBuffer + { + public static FfiConverterDuration INSTANCE = new FfiConverterDuration(); + + // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs + private const uint NanosecondsPerTick = 100; + + public override TimeSpan Read(BigEndianStream stream) + { + var seconds = stream.ReadULong(); + var nanoseconds = stream.ReadUInt(); + var ticks = seconds * TimeSpan.TicksPerSecond; + ticks += nanoseconds / NanosecondsPerTick; + return new TimeSpan(Convert.ToInt64(ticks)); + } + + public override int AllocationSize(TimeSpan value) + { + // 8 bytes for seconds, 4 bytes for nanoseconds + return 12; + } + + public override void Write(TimeSpan value, BigEndianStream stream) + { + stream.WriteULong(Convert.ToUInt64(value.Ticks / TimeSpan.TicksPerSecond)); + stream.WriteUInt( + Convert.ToUInt32(value.Ticks % TimeSpan.TicksPerSecond * NanosecondsPerTick) + ); + } + } + + /// + /// Reader for an incoming byte data stream. + /// + public interface IByteStreamReader + { + /// + /// Information about the underlying stream. + /// + ByteStreamInfo Info(); + + /// + /// Returns the next chunk, or `None` once the stream has closed. + /// + /// + Task Next(); + + /// + /// Reads every chunk, concatenating them into a single buffer returned once the stream closes. + /// + /// + Task ReadAll(); + } + + /// + /// Reader for an incoming byte data stream. + /// + public class ByteStreamReader : IByteStreamReader, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public ByteStreamReader(ulong pointer) + { + this.pointer = pointer; + } + + ~ByteStreamReader() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_bytestreamreader(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_bytestreamreader( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Information about the underlying stream. + /// + public ByteStreamInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeByteStreamInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamreader_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Returns the next chunk, or `None` once the stream has closed. + /// + /// + public async Task Next() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamreader_next(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalTypeBytes.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Reads every chunk, concatenating them into a single buffer returned once the stream closes. + /// + /// + public async Task ReadAll() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamreader_read_all( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeBytes.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + } + + class FfiConverterTypeByteStreamReader : FfiConverter + { + public static FfiConverterTypeByteStreamReader INSTANCE = + new FfiConverterTypeByteStreamReader(); + + public override ulong Lower(ByteStreamReader value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override ByteStreamReader Lift(ulong value) + { + return new ByteStreamReader(value); + } + + public override ByteStreamReader Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(ByteStreamReader value) + { + return 8; + } + + public override void Write(ByteStreamReader value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Writer for an open byte data stream. + /// + public interface IByteStreamWriter + { + /// + /// Closes the stream normally. + /// + /// + Task Close(); + + /// + /// Closes the stream abnormally with a reason. + /// + /// + Task CloseWithReason(string @reason); + + /// + /// Information about the underlying stream. + /// + ByteStreamInfo Info(); + + /// + /// Whether the stream is still open — false once it has been closed locally or a send has failed. + /// + Task IsOpen(); + + /// + /// Appends bytes to the stream. + /// + /// + Task Write(Bytes @data); + } + + /// + /// Writer for an open byte data stream. + /// + public class ByteStreamWriter : IByteStreamWriter, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public ByteStreamWriter(ulong pointer) + { + this.pointer = pointer; + } + + ~ByteStreamWriter() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_bytestreamwriter(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_bytestreamwriter( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Closes the stream normally. + /// + /// + public async Task Close() + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamwriter_close(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Closes the stream abnormally with a reason. + /// + /// + public async Task CloseWithReason(string @reason) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamwriter_close_with_reason( + thisPtr, + FfiConverterString.INSTANCE.Lower(@reason) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Information about the underlying stream. + /// + public ByteStreamInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeByteStreamInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamwriter_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Whether the stream is still open — false once it has been closed locally or a send has failed. + /// + public async Task IsOpen() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamwriter_is_open(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_i8(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_i8(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_i8(future), + // Lift + (result) => FfiConverterBoolean.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + + /// + /// Appends bytes to the stream. + /// + /// + public async Task Write(Bytes @data) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_bytestreamwriter_write( + thisPtr, + FfiConverterTypeBytes.INSTANCE.Lower(@data) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + } + + class FfiConverterTypeByteStreamWriter : FfiConverter + { + public static FfiConverterTypeByteStreamWriter INSTANCE = + new FfiConverterTypeByteStreamWriter(); + + public override ulong Lower(ByteStreamWriter value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override ByteStreamWriter Lift(ulong value) + { + return new ByteStreamWriter(value); + } + + public override ByteStreamWriter Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(ByteStreamWriter value) + { + return 8; + } + + public override void Write(ByteStreamWriter value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// A stream of [`DataTrackFrame`]s received from a [`RemoteDataTrack`]. + /// + public interface IDataTrackStream + { + /// + /// Returns the next received frame or `None` if the subscription has ended. + /// + Task Next(); + } + + /// + /// A stream of [`DataTrackFrame`]s received from a [`RemoteDataTrack`]. + /// + public class DataTrackStream : IDataTrackStream, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public DataTrackStream(ulong pointer) + { + this.pointer = pointer; + } + + ~DataTrackStream() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_datatrackstream(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_datatrackstream( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Returns the next received frame or `None` if the subscription has ended. + /// + public async Task Next() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_datatrackstream_next(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalTypeDataTrackFrame.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeDataTrackStream : FfiConverter + { + public static FfiConverterTypeDataTrackStream INSTANCE = new FfiConverterTypeDataTrackStream(); + + public override ulong Lower(DataTrackStream value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override DataTrackStream Lift(ulong value) + { + return new DataTrackStream(value); + } + + public override DataTrackStream Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(DataTrackStream value) + { + return 8; + } + + public override void Write(DataTrackStream value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Receives inbound data-stream packets and processes them on the incoming manager's actor loop, + /// surfacing opened readers through a foreign delegate. + /// + /// Mirrors [`crate::data_track::remote::RemoteDataTrackManager`]: `handle_packet_received` is a + /// cheap synchronous enqueue (safe to call from a native data-channel callback), while + /// decompression and reassembly happen on the spawned `run` task in packet order. + /// + public interface IIncomingDataStreamManager + { + /// + /// Aborts all open incoming streams so their readers error instead of hanging (e.g. on + /// disconnect). Handler wiring on the foreign side survives, so streams that arrive later + /// (e.g. after a reconnect) are still processed. + /// + void AbortAllStreams(); + + /// + /// Aborts open incoming streams sent by `identity` (e.g. when that participant disconnects + /// mid-send), so their readers error instead of hanging. + /// + void AbortStreamsFrom(string @identity); + + /// + /// Handles an encoded [`livekit_protocol::DataPacket`] received over the data channel. + /// + /// Fire-and-forget: the packet is decoded and enqueued in order; processing happens on the + /// manager's run loop. Non-data-stream or undecodable packets are ignored. + /// + /// `encryption_type` is how this packet arrived on the wire, and must be passed by the host + /// because it cannot be recovered from the bytes: `encrypted_packet` is a member of the + /// `DataPacket.value` oneof, so decrypting replaces it with the decrypted stream packet. + /// Hosts without end-to-end encryption pass [`EncryptionType::None`]; hosts with E2EE pass + /// the type they decrypted with, letting the manager reject chunks whose encryption doesn't + /// match their stream's header ([`DataStreamError::EncryptionTypeMismatch`]). + /// + void HandlePacketReceived(Bytes @packet, EncryptionType @encryptionType); + + /// + /// Number of currently open incoming streams: streams announced by a header that are still + /// awaiting more packets. Inline (single-packet) streams complete immediately and are never + /// counted. + /// + /// The query runs on the manager's loop in order with previously submitted events, so a + /// packet or abort passed beforehand is reflected in the answer — useful in tests to wait for + /// a header to register (or an abort to land) without racing the run loop. + /// + Task OpenStreamCount(); + } + + /// + /// Receives inbound data-stream packets and processes them on the incoming manager's actor loop, + /// surfacing opened readers through a foreign delegate. + /// + /// Mirrors [`crate::data_track::remote::RemoteDataTrackManager`]: `handle_packet_received` is a + /// cheap synchronous enqueue (safe to call from a native data-channel callback), while + /// decompression and reassembly happen on the spawned `run` task in packet order. + /// + public class IncomingDataStreamManager : IIncomingDataStreamManager, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public IncomingDataStreamManager(ulong pointer) + { + this.pointer = pointer; + } + + ~IncomingDataStreamManager() + { + Destroy(); + } + + /// + /// Creates a manager that surfaces opened streams to `delegate`. + /// + /// `max_payload_byte_length` caps the decompressed size of a single incoming stream + /// (`None` = default, 5 GB) and is **fixed for the lifetime of the manager**. If the host + /// sources it from per-connection options that aren't final until connect — and can differ + /// between sessions of the same host object — construct a fresh manager for each session + /// rather than lazily memoizing one, or the first session's value is silently pinned. (Same + /// class of rough edge as a single room instance being `connect()`ed multiple times.) + /// Rebuilding is cheap and safe: dropping the manager cancels its tasks, and handler wiring + /// lives on the foreign side. + /// + public IncomingDataStreamManager( + IncomingDataStreamManagerDelegate @delegate, + ulong? @maxPayloadByteLength + ) + : this( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_constructor_incomingdatastreammanager_new( + FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.Lower(@delegate), + FfiConverterOptionalUInt64.INSTANCE.Lower(@maxPayloadByteLength), + ref _status + ) + ) + ) { } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_incomingdatastreammanager( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_incomingdatastreammanager( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Aborts all open incoming streams so their readers error instead of hanging (e.g. on + /// disconnect). Handler wiring on the foreign side survives, so streams that arrive later + /// (e.g. after a reconnect) are still processed. + /// + public void AbortAllStreams() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_abort_all_streams( + thisPtr, + ref _status + ) + ) + ); + } + + /// + /// Aborts open incoming streams sent by `identity` (e.g. when that participant disconnects + /// mid-send), so their readers error instead of hanging. + /// + public void AbortStreamsFrom(string @identity) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_abort_streams_from( + thisPtr, + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ); + } + + /// + /// Handles an encoded [`livekit_protocol::DataPacket`] received over the data channel. + /// + /// Fire-and-forget: the packet is decoded and enqueued in order; processing happens on the + /// manager's run loop. Non-data-stream or undecodable packets are ignored. + /// + /// `encryption_type` is how this packet arrived on the wire, and must be passed by the host + /// because it cannot be recovered from the bytes: `encrypted_packet` is a member of the + /// `DataPacket.value` oneof, so decrypting replaces it with the decrypted stream packet. + /// Hosts without end-to-end encryption pass [`EncryptionType::None`]; hosts with E2EE pass + /// the type they decrypted with, letting the manager reject chunks whose encryption doesn't + /// match their stream's header ([`DataStreamError::EncryptionTypeMismatch`]). + /// + public void HandlePacketReceived(Bytes @packet, EncryptionType @encryptionType) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_handle_packet_received( + thisPtr, + FfiConverterTypeBytes.INSTANCE.Lower(@packet), + FfiConverterTypeEncryptionType.INSTANCE.Lower(@encryptionType), + ref _status + ) + ) + ); + } + + /// + /// Number of currently open incoming streams: streams announced by a header that are still + /// awaiting more packets. Inline (single-packet) streams complete immediately and are never + /// counted. + /// + /// The query runs on the manager's loop in order with previously submitted events, so a + /// packet or abort passed beforehand is reflected in the answer — useful in tests to wait for + /// a header to register (or an abort to land) without racing the run loop. + /// + public async Task OpenStreamCount() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanager_open_stream_count( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterUInt64.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeIncomingDataStreamManager : FfiConverter + { + public static FfiConverterTypeIncomingDataStreamManager INSTANCE = + new FfiConverterTypeIncomingDataStreamManager(); + + public override ulong Lower(IncomingDataStreamManager value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override IncomingDataStreamManager Lift(ulong value) + { + return new IncomingDataStreamManager(value); + } + + public override IncomingDataStreamManager Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(IncomingDataStreamManager value) + { + return 8; + } + + public override void Write(IncomingDataStreamManager value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Delegate for receiving output events from [`IncomingDataStreamManager`]. + /// + /// Only stream lifecycle events (opened/closed) are surfaced. The manager's deprecated v1 raw + /// chunk/trailer notifications are intentionally not forwarded over the FFI boundary. + /// + public interface IncomingDataStreamManagerDelegate + { + /// + /// A byte stream was opened by `identity` and is ready to be read. + /// + void OnByteStreamOpened(ByteStreamReader @reader, string @identity); + + /// + /// A text stream was opened by `identity` and is ready to be read. + /// + void OnTextStreamOpened(TextStreamReader @reader, string @identity); + + /// + /// A previously opened stream terminated on the wire and will produce no further data: its + /// trailer arrived, its (single-packet) inline payload completed, it failed, or it was + /// aborted. Emitted exactly once per opened stream, after the corresponding open event. + /// + /// Hosts delivering streams on ordered topics use this to know when a stream's handler chain + /// can advance — a stream that is still open must not block streams opened after it forever. + /// + void OnStreamClosed(string @streamId, string @identity); + } + + /// + /// Delegate for receiving output events from [`IncomingDataStreamManager`]. + /// + /// Only stream lifecycle events (opened/closed) are surfaced. The manager's deprecated v1 raw + /// chunk/trailer notifications are intentionally not forwarded over the FFI boundary. + /// + public class IncomingDataStreamManagerDelegateImpl : IncomingDataStreamManagerDelegate, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public IncomingDataStreamManagerDelegateImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~IncomingDataStreamManagerDelegateImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_incomingdatastreammanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_incomingdatastreammanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// A byte stream was opened by `identity` and is ready to be read. + /// + public void OnByteStreamOpened(ByteStreamReader @reader, string @identity) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_byte_stream_opened( + thisPtr, + FfiConverterTypeByteStreamReader.INSTANCE.Lower(@reader), + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ); + } + + /// + /// A text stream was opened by `identity` and is ready to be read. + /// + public void OnTextStreamOpened(TextStreamReader @reader, string @identity) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_text_stream_opened( + thisPtr, + FfiConverterTypeTextStreamReader.INSTANCE.Lower(@reader), + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ); + } + + /// + /// A previously opened stream terminated on the wire and will produce no further data: its + /// trailer arrived, its (single-packet) inline payload completed, it failed, or it was + /// aborted. Emitted exactly once per opened stream, after the corresponding open event. + /// + /// Hosts delivering streams on ordered topics use this to know when a stream's handler chain + /// can advance — a stream that is still open must not block streams opened after it forever. + /// + public void OnStreamClosed(string @streamId, string @identity) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingdatastreammanagerdelegate_on_stream_closed( + thisPtr, + FfiConverterString.INSTANCE.Lower(@streamId), + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ); + } + } + + class UniffiCallbackInterfaceIncomingDataStreamManagerDelegate + { + static void OnByteStreamOpened( + ulong @uniffiHandle, + ulong @reader, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnByteStreamOpened( + FfiConverterTypeByteStreamReader.INSTANCE.Lift(@reader), + FfiConverterString.INSTANCE.Lift(@identity) + ); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void OnTextStreamOpened( + ulong @uniffiHandle, + ulong @reader, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnTextStreamOpened( + FfiConverterTypeTextStreamReader.INSTANCE.Lift(@reader), + FfiConverterString.INSTANCE.Lift(@identity) + ); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void OnStreamClosed( + ulong @uniffiHandle, + RustBuffer @streamId, + RustBuffer @identity, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnStreamClosed( + FfiConverterString.INSTANCE.Lift(@streamId), + FfiConverterString.INSTANCE.Lift(@identity) + ); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if ( + !FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + @handle, + out var obj + ) + ) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeIncomingDataStreamManagerDelegate.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod0( + OnByteStreamOpened + ); + static _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod1 _m1 = + new _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod1( + OnTextStreamOpened + ); + static _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod2 _m2 = + new _UniFFILib.UniffiCallbackInterfaceIncomingDataStreamManagerDelegateMethod2( + OnStreamClosed + ); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceIncomingDataStreamManagerDelegate _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceIncomingDataStreamManagerDelegate + { + @onByteStreamOpened = Marshal.GetFunctionPointerForDelegate(_m0), + @onTextStreamOpened = Marshal.GetFunctionPointerForDelegate(_m1), + @onStreamClosed = Marshal.GetFunctionPointerForDelegate(_m2), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_uniffi_fn_init_callback_vtable_incomingdatastreammanagerdelegate( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class ConcurrentHandleMap + where T : notnull + { + readonly ConcurrentDictionary _map = new(); + + // Handles are odd numbers (1, 3, 5, ...) — the lowest bit must always be set. + // Rust uses (handle & 1) to distinguish foreign-language handles from Rust Arc + // pointers, which are always even due to memory alignment. See uniffi_core/src/ffi/handle.rs. + const long HANDLE_INITIAL = 1; + const long HANDLE_DELTA = 2; + long _currentHandle = HANDLE_INITIAL - HANDLE_DELTA; + + public ulong Insert(T obj) + { + var handle = (ulong)Interlocked.Add(ref _currentHandle, HANDLE_DELTA); + if (!_map.TryAdd(handle, obj)) + { + throw new InternalException("ConcurrentHandleMap: Duplicate handle"); + } + return handle; + } + + public bool TryGet(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryGetValue(handle, out result); + } + + public T Get(ulong handle) + { + if (_map.TryGetValue(handle, out var result)) + { + return result; + } + else + { + throw new InternalException("ConcurrentHandleMap: Invalid handle"); + } + } + + public bool Remove(ulong handle) + { + return _map.TryRemove(handle, out _); + } + + public bool Remove(ulong handle, [NotNullWhen(true)] out T? result) + { + return _map.TryRemove(handle, out result); + } + } + + static class UniffiCallbackResponseStatus + { + public static sbyte SUCCESS = 0; + public static sbyte ERROR = 1; + public static sbyte UNEXPECTED_ERROR = 2; + } + + class FfiConverterTypeIncomingDataStreamManagerDelegate + : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeIncomingDataStreamManagerDelegate INSTANCE = + new FfiConverterTypeIncomingDataStreamManagerDelegate(); + + static FfiConverterTypeIncomingDataStreamManagerDelegate() + { + UniffiCallbackInterfaceIncomingDataStreamManagerDelegate.Register(); + } + + public override ulong Lower(IncomingDataStreamManagerDelegate value) + { + if (value is IncomingDataStreamManagerDelegateImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override IncomingDataStreamManagerDelegate Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new IncomingDataStreamManagerDelegateImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override IncomingDataStreamManagerDelegate Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(IncomingDataStreamManagerDelegate value) + { + return 8; + } + + public override void Write(IncomingDataStreamManagerDelegate value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Buffers opened and closed streams so they can be pulled instead of pushed. + /// + /// Implements [`IncomingDataStreamManagerDelegate`] in Rust; see the module docs. + /// + public interface IIncomingStreamQueue + { + /// + /// Wakes a pending [`Self::next_opened_stream`] or [`Self::next_closed_stream`] with `None`. + /// See [`OutgoingPacketQueue::close`]. + /// + void Close(); + + /// + /// Awaits the next stream-closed notification. + /// + /// Pulled independently of [`Self::next_opened_stream`], so ordering across the two queues is + /// not guaranteed — correlate by `stream_id` (a close always follows its open on the push + /// side). Returns `None` once the manager has shut down. + /// + Task NextClosedStream(); + + /// + /// Awaits the next stream opened by a remote participant. + /// + /// Returns `None` once the manager has shut down, which ends the caller's drain loop. Unlike + /// [`OutgoingPacketQueue::next_packets`] this yields one at a time: each carries a reader the + /// caller has to route to a handler, so batching would only defer that work. + /// + Task NextOpenedStream(); + } + + /// + /// Buffers opened and closed streams so they can be pulled instead of pushed. + /// + /// Implements [`IncomingDataStreamManagerDelegate`] in Rust; see the module docs. + /// + public class IncomingStreamQueue : IIncomingStreamQueue, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public IncomingStreamQueue(ulong pointer) + { + this.pointer = pointer; + } + + ~IncomingStreamQueue() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_incomingstreamqueue( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_incomingstreamqueue( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Wakes a pending [`Self::next_opened_stream`] or [`Self::next_closed_stream`] with `None`. + /// See [`OutgoingPacketQueue::close`]. + /// + public void Close() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingstreamqueue_close( + thisPtr, + ref _status + ) + ) + ); + } + + /// + /// Awaits the next stream-closed notification. + /// + /// Pulled independently of [`Self::next_opened_stream`], so ordering across the two queues is + /// not guaranteed — correlate by `stream_id` (a close always follows its open on the push + /// side). Returns `None` once the manager has shut down. + /// + public async Task NextClosedStream() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingstreamqueue_next_closed_stream( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalTypeClosedStream.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + + /// + /// Awaits the next stream opened by a remote participant. + /// + /// Returns `None` once the manager has shut down, which ends the caller's drain loop. Unlike + /// [`OutgoingPacketQueue::next_packets`] this yields one at a time: each carries a reader the + /// caller has to route to a handler, so batching would only defer that work. + /// + public async Task NextOpenedStream() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_incomingstreamqueue_next_opened_stream( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalTypeOpenedStream.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeIncomingStreamQueue : FfiConverter + { + public static FfiConverterTypeIncomingStreamQueue INSTANCE = + new FfiConverterTypeIncomingStreamQueue(); + + public override ulong Lower(IncomingStreamQueue value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override IncomingStreamQueue Lift(ulong value) + { + return new IncomingStreamQueue(value); + } + + public override IncomingStreamQueue Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(IncomingStreamQueue value) + { + return 8; + } + + public override void Write(IncomingStreamQueue value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Data track published by the local participant. + /// + public interface ILocalDataTrack + { + /// + /// Information about the data track. + /// + DataTrackInfo Info(); + + /// + /// Whether or not the track is currently published. + /// + bool IsPublished(); + + /// + /// Try pushing a frame to subscribers of the track. + /// + /// + void TryPush(DataTrackFrame @frame); + + /// + /// Unpublishes the track. + /// + void Unpublish(); + + /// + /// Waits asynchronously until the track is unpublished. + /// + /// Use this to trigger follow-up work once the track is no longer published. + /// If the track is already unpublished, this method returns immediately. + /// + Task WaitForUnpublish(); + } + + /// + /// Data track published by the local participant. + /// + public class LocalDataTrack : ILocalDataTrack, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public LocalDataTrack(ulong pointer) + { + this.pointer = pointer; + } + + ~LocalDataTrack() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_localdatatrack(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_localdatatrack( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Information about the data track. + /// + public DataTrackInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeDataTrackInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrack_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Whether or not the track is currently published. + /// + public bool IsPublished() + { + return CallWithPointer(thisPtr => + FfiConverterBoolean.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrack_is_published( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Try pushing a frame to subscribers of the track. + /// + /// + public void TryPush(DataTrackFrame @frame) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypePushFrameErrorReason.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrack_try_push( + thisPtr, + FfiConverterTypeDataTrackFrame.INSTANCE.Lower(@frame), + ref _status + ) + ) + ); + } + + /// + /// Unpublishes the track. + /// + public void Unpublish() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrack_unpublish( + thisPtr, + ref _status + ) + ) + ); + } + + /// + /// Waits asynchronously until the track is unpublished. + /// + /// Use this to trigger follow-up work once the track is no longer published. + /// If the track is already unpublished, this method returns immediately. + /// + public async Task WaitForUnpublish() + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrack_wait_for_unpublish( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeLocalDataTrack : FfiConverter + { + public static FfiConverterTypeLocalDataTrack INSTANCE = new FfiConverterTypeLocalDataTrack(); + + public override ulong Lower(LocalDataTrack value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override LocalDataTrack Lift(ulong value) + { + return new LocalDataTrack(value); + } + + public override LocalDataTrack Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(LocalDataTrack value) + { + return 8; + } + + public override void Write(LocalDataTrack value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// System for managing data track publications. + /// + public interface ILocalDataTrackManager + { + /// + /// Handles a serialized `PublishDataTrackResponse` signal response from the SFU. + /// + /// + void HandleSfuPublishResponse(byte[] @res); + + /// + /// Handles a serialized `RequestResponse` signal response from the SFU. + /// + /// + void HandleSfuRequestResponse(byte[] @res); + + /// + /// Get serialized publish responses for all currently published tracks to support sync state. + /// + /// Returns a vector of serialized [`proto::PublishDataTrackResponse`]. + /// + Task PublishResponsesForSyncState(); + + /// + /// Publishes a data track with given options. + /// + /// + Task PublishTrack(DataTrackOptions @options); + + /// + /// Republish all tracks. + /// + /// This must be invoked after a full reconnect in order for existing publications + /// to be recognized by the SFU. Each republished track will be assigned a new SID. + /// + void RepublishTracks(); + } + + /// + /// System for managing data track publications. + /// + public class LocalDataTrackManager : ILocalDataTrackManager, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public LocalDataTrackManager(ulong pointer) + { + this.pointer = pointer; + } + + ~LocalDataTrackManager() + { + Destroy(); + } + + public LocalDataTrackManager( + LocalDataTrackManagerDelegate @delegate, + EncryptionProvider? @encryptionProvider + ) + : this( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_constructor_localdatatrackmanager_new( + FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.Lower(@delegate), + FfiConverterOptionalTypeEncryptionProvider.INSTANCE.Lower( + @encryptionProvider + ), + ref _status + ) + ) + ) { } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_localdatatrackmanager( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_localdatatrackmanager( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Handles a serialized `PublishDataTrackResponse` signal response from the SFU. + /// + /// + public void HandleSfuPublishResponse(byte[] @res) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypeHandleSignalResponseError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanager_handle_sfu_publish_response( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@res), + ref _status + ) + ) + ); + } + + /// + /// Handles a serialized `RequestResponse` signal response from the SFU. + /// + /// + public void HandleSfuRequestResponse(byte[] @res) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypeHandleSignalResponseError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanager_handle_sfu_request_response( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@res), + ref _status + ) + ) + ); + } + + /// + /// Get serialized publish responses for all currently published tracks to support sync state. + /// + /// Returns a vector of serialized [`proto::PublishDataTrackResponse`]. + /// + public async Task PublishResponsesForSyncState() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanager_publish_responses_for_sync_state( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterSequenceTypeBytes.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + + /// + /// Publishes a data track with given options. + /// + /// + public async Task PublishTrack(DataTrackOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanager_publish_track( + thisPtr, + FfiConverterTypeDataTrackOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterTypeLocalDataTrack.INSTANCE.Lift(result), + // Error + FfiConverterTypePublishError.INSTANCE + ); + } + + /// + /// Republish all tracks. + /// + /// This must be invoked after a full reconnect in order for existing publications + /// to be recognized by the SFU. Each republished track will be assigned a new SID. + /// + public void RepublishTracks() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanager_republish_tracks( + thisPtr, + ref _status + ) + ) + ); + } + } + + class FfiConverterTypeLocalDataTrackManager : FfiConverter + { + public static FfiConverterTypeLocalDataTrackManager INSTANCE = + new FfiConverterTypeLocalDataTrackManager(); + + public override ulong Lower(LocalDataTrackManager value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override LocalDataTrackManager Lift(ulong value) + { + return new LocalDataTrackManager(value); + } + + public override LocalDataTrackManager Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(LocalDataTrackManager value) + { + return 8; + } + + public override void Write(LocalDataTrackManager value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Delegate for receiving output events from [`LocalDataTrackManager`]. + /// + public interface LocalDataTrackManagerDelegate + { + /// + /// Encoded signal request to be forwarded to the SFU. + /// + void OnSignalRequest(byte[] @request); + + /// + /// Packets available to be sent over the data channel transport. + /// + void OnPacketsAvailable(Bytes[] @packets); + } + + /// + /// Delegate for receiving output events from [`LocalDataTrackManager`]. + /// + public class LocalDataTrackManagerDelegateImpl : LocalDataTrackManagerDelegate, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public LocalDataTrackManagerDelegateImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~LocalDataTrackManagerDelegateImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_localdatatrackmanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_localdatatrackmanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Encoded signal request to be forwarded to the SFU. + /// + public void OnSignalRequest(byte[] @request) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanagerdelegate_on_signal_request( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@request), + ref _status + ) + ) + ); + } + + /// + /// Packets available to be sent over the data channel transport. + /// + public void OnPacketsAvailable(Bytes[] @packets) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_localdatatrackmanagerdelegate_on_packets_available( + thisPtr, + FfiConverterSequenceTypeBytes.INSTANCE.Lower(@packets), + ref _status + ) + ) + ); + } + } + + class UniffiCallbackInterfaceLocalDataTrackManagerDelegate + { + static void OnSignalRequest( + ulong @uniffiHandle, + RustBuffer @request, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnSignalRequest(FfiConverterByteArray.INSTANCE.Lift(@request)); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void OnPacketsAvailable( + ulong @uniffiHandle, + RustBuffer @packets, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnPacketsAvailable(FfiConverterSequenceTypeBytes.INSTANCE.Lift(@packets)); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if ( + !FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + @handle, + out var obj + ) + ) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeLocalDataTrackManagerDelegate.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod0(OnSignalRequest); + static _UniFFILib.UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod1 _m1 = + new _UniFFILib.UniffiCallbackInterfaceLocalDataTrackManagerDelegateMethod1( + OnPacketsAvailable + ); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceLocalDataTrackManagerDelegate _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceLocalDataTrackManagerDelegate + { + @onSignalRequest = Marshal.GetFunctionPointerForDelegate(_m0), + @onPacketsAvailable = Marshal.GetFunctionPointerForDelegate(_m1), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_uniffi_fn_init_callback_vtable_localdatatrackmanagerdelegate( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeLocalDataTrackManagerDelegate + : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeLocalDataTrackManagerDelegate INSTANCE = + new FfiConverterTypeLocalDataTrackManagerDelegate(); + + static FfiConverterTypeLocalDataTrackManagerDelegate() + { + UniffiCallbackInterfaceLocalDataTrackManagerDelegate.Register(); + } + + public override ulong Lower(LocalDataTrackManagerDelegate value) + { + if (value is LocalDataTrackManagerDelegateImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override LocalDataTrackManagerDelegate Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new LocalDataTrackManagerDelegateImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override LocalDataTrackManagerDelegate Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(LocalDataTrackManagerDelegate value) + { + return 8; + } + + public override void Write(LocalDataTrackManagerDelegate value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Sends data streams, choosing v2 single-packet/compression or legacy multi-packet framing based + /// on recipient capabilities. Outbound packets are handed to a foreign delegate for transport. + /// + public interface IOutgoingDataStreamManager + { + /// + /// Sends a complete byte payload, returning info about the created stream. + /// + /// + Task SendBytes(Bytes @data, StreamByteOptions @options); + + /// + /// Streams a file from disk, returning info about the created stream. + /// + /// + Task SendFile(string @path, StreamByteOptions @options); + + /// + /// Sends a complete text payload, returning info about the created stream. + /// + /// + Task SendText(string @text, StreamTextOptions @options); + + /// + /// Opens an incremental byte stream writer (never compressed or inlined). + /// + /// + Task StreamBytes(StreamByteOptions @options); + + /// + /// Opens an incremental text stream writer (never compressed or inlined). + /// + /// + Task StreamText(StreamTextOptions @options); + } + + /// + /// Sends data streams, choosing v2 single-packet/compression or legacy multi-packet framing based + /// on recipient capabilities. Outbound packets are handed to a foreign delegate for transport. + /// + public class OutgoingDataStreamManager : IOutgoingDataStreamManager, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public OutgoingDataStreamManager(ulong pointer) + { + this.pointer = pointer; + } + + ~OutgoingDataStreamManager() + { + Destroy(); + } + + public OutgoingDataStreamManager( + OutgoingDataStreamManagerDelegate @delegate, + RemoteParticipantRegistryDelegate @registry + ) + : this( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_constructor_outgoingdatastreammanager_new( + FfiConverterTypeOutgoingDataStreamManagerDelegate.INSTANCE.Lower(@delegate), + FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.Lower(@registry), + ref _status + ) + ) + ) { } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_outgoingdatastreammanager( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_outgoingdatastreammanager( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Sends a complete byte payload, returning info about the created stream. + /// + /// + public async Task SendBytes(Bytes @data, StreamByteOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_bytes( + thisPtr, + FfiConverterTypeBytes.INSTANCE.Lower(@data), + FfiConverterTypeStreamByteOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeByteStreamInfo.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Streams a file from disk, returning info about the created stream. + /// + /// + public async Task SendFile(string @path, StreamByteOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_file( + thisPtr, + FfiConverterString.INSTANCE.Lower(@path), + FfiConverterTypeStreamByteOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeByteStreamInfo.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Sends a complete text payload, returning info about the created stream. + /// + /// + public async Task SendText(string @text, StreamTextOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_send_text( + thisPtr, + FfiConverterString.INSTANCE.Lower(@text), + FfiConverterTypeStreamTextOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterTypeTextStreamInfo.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Opens an incremental byte stream writer (never compressed or inlined). + /// + /// + public async Task StreamBytes(StreamByteOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_stream_bytes( + thisPtr, + FfiConverterTypeStreamByteOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterTypeByteStreamWriter.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Opens an incremental text stream writer (never compressed or inlined). + /// + /// + public async Task StreamText(StreamTextOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanager_stream_text( + thisPtr, + FfiConverterTypeStreamTextOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterTypeTextStreamWriter.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + } + + class FfiConverterTypeOutgoingDataStreamManager : FfiConverter + { + public static FfiConverterTypeOutgoingDataStreamManager INSTANCE = + new FfiConverterTypeOutgoingDataStreamManager(); + + public override ulong Lower(OutgoingDataStreamManager value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override OutgoingDataStreamManager Lift(ulong value) + { + return new OutgoingDataStreamManager(value); + } + + public override OutgoingDataStreamManager Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(OutgoingDataStreamManager value) + { + return 8; + } + + public override void Write(OutgoingDataStreamManager value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Delegate for receiving outbound packets from [`OutgoingDataStreamManager`]. + /// + public interface OutgoingDataStreamManagerDelegate + { + /// + /// Encoded [`livekit_protocol::DataPacket`]s to be sent over the data channel transport, in + /// order. One-shot sends (`send_text`/`send_bytes`) deliver their entire stream — header, + /// chunks, trailer — in a single call; incremental writers and `send_file` deliver one packet + /// per call. + /// + /// Return only once the packets have been handed to the transport: the originating + /// `send_*`/`write` call stays pending until then, which is what bounds how fast a producer + /// can enqueue. Throwing [`PacketDeliveryError`] fails that call with + /// [`DataStreamError::SendFailed`](super::common::DataStreamError::SendFailed) and closes the + /// affected stream (`is_open` becomes false for writers). + /// + /// + Task OnPacketsAvailable(Bytes[] @packets); + } + + /// + /// Delegate for receiving outbound packets from [`OutgoingDataStreamManager`]. + /// + public class OutgoingDataStreamManagerDelegateImpl : OutgoingDataStreamManagerDelegate, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public OutgoingDataStreamManagerDelegateImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~OutgoingDataStreamManagerDelegateImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_outgoingdatastreammanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_outgoingdatastreammanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Encoded [`livekit_protocol::DataPacket`]s to be sent over the data channel transport, in + /// order. One-shot sends (`send_text`/`send_bytes`) deliver their entire stream — header, + /// chunks, trailer — in a single call; incremental writers and `send_file` deliver one packet + /// per call. + /// + /// Return only once the packets have been handed to the transport: the originating + /// `send_*`/`write` call stays pending until then, which is what bounds how fast a producer + /// can enqueue. Throwing [`PacketDeliveryError`] fails that call with + /// [`DataStreamError::SendFailed`](super::common::DataStreamError::SendFailed) and closes the + /// affected stream (`is_open` becomes false for writers). + /// + /// + public async Task OnPacketsAvailable(Bytes[] @packets) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingdatastreammanagerdelegate_on_packets_available( + thisPtr, + FfiConverterSequenceTypeBytes.INSTANCE.Lower(@packets) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypePacketDeliveryError.INSTANCE + ); + } + } + + class UniffiCallbackInterfaceOutgoingDataStreamManagerDelegate + { + static void OnPacketsAvailable( + ulong @uniffiHandle, + RustBuffer @packets, + IntPtr @uniffiFutureCallback, + ulong @uniffiCallbackData, + IntPtr /*_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*/ + @uniffiOutDroppedCallback + ) + { + var handle = @uniffiHandle; + var futureHandle = new UniffiForeignFutureHandle(); + var foreignHandle = _UniFFIAsync._foreign_futures_map.Insert(futureHandle); + unsafe + { + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).handle = foreignHandle; + ( + *(_UniFFILib.UniffiForeignFutureDroppedCallbackStruct*)@uniffiOutDroppedCallback + ).free = Marshal.GetFunctionPointerForDelegate( + _UniFFIAsync.UniffiForeignFutureDroppedCallbackImpl.callback + ); + } + if ( + !FfiConverterTypeOutgoingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + $"No callback in handlemap '{handle}'" + ); + } + catch { } + var earlyCb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + earlyCb(@uniffiCallbackData, ret); + }); + futureHandle.Dispose(); + return; + } + + // Optimization: skip queuing if already cancelled before Task.Run schedules. + // TryInvokeCallback is the definitive cancellation guard inside the task body. + Task.Run( + async () => + { + var ret = new _UniFFILib.UniffiForeignFutureResultVoid(); + ret.@callStatus = new UniffiRustCallStatus(); + + try + { + try + { + await uniffiObject.OnPacketsAvailable( + FfiConverterSequenceTypeBytes.INSTANCE.Lift(@packets) + ) +#if NET6_0_OR_GREATER + .WaitAsync(futureHandle.Cts.Token) +#endif + ; + + ret.@callStatus.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (PacketDeliveryException e) + { + try + { + ret.@callStatus.code = UniffiCallbackResponseStatus.ERROR; + ret.@callStatus.error_buf = + FfiConverterTypePacketDeliveryError.INSTANCE.Lower(e); + } + catch + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + } + } + catch (OperationCanceledException) + when (futureHandle.Cts.IsCancellationRequested) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + "Future cancelled" + ); + } + catch { } + } + catch (System.Exception e) + { + ret.@callStatus.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + ret.@callStatus.error_buf = FfiConverterString.INSTANCE.Lower( + e.Message + ); + } + catch { } + } + + var cb = + Marshal.GetDelegateForFunctionPointer<_UniFFILib.UniffiForeignFutureCompleteVoid>( + @uniffiFutureCallback + ); + futureHandle.InvokeCallbackOnce(() => + { + cb(@uniffiCallbackData, ret); + }); + } + finally + { + futureHandle.Dispose(); + } + }, + futureHandle.Cts.Token + ); + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeOutgoingDataStreamManagerDelegate.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if ( + !FfiConverterTypeOutgoingDataStreamManagerDelegate.INSTANCE.handleMap.TryGet( + @handle, + out var obj + ) + ) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeOutgoingDataStreamManagerDelegate.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceOutgoingDataStreamManagerDelegateMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceOutgoingDataStreamManagerDelegateMethod0( + OnPacketsAvailable + ); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceOutgoingDataStreamManagerDelegate _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceOutgoingDataStreamManagerDelegate + { + @onPacketsAvailable = Marshal.GetFunctionPointerForDelegate(_m0), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_uniffi_fn_init_callback_vtable_outgoingdatastreammanagerdelegate( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeOutgoingDataStreamManagerDelegate + : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeOutgoingDataStreamManagerDelegate INSTANCE = + new FfiConverterTypeOutgoingDataStreamManagerDelegate(); + + static FfiConverterTypeOutgoingDataStreamManagerDelegate() + { + UniffiCallbackInterfaceOutgoingDataStreamManagerDelegate.Register(); + } + + public override ulong Lower(OutgoingDataStreamManagerDelegate value) + { + if (value is OutgoingDataStreamManagerDelegateImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override OutgoingDataStreamManagerDelegate Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new OutgoingDataStreamManagerDelegateImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override OutgoingDataStreamManagerDelegate Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(OutgoingDataStreamManagerDelegate value) + { + return 8; + } + + public override void Write(OutgoingDataStreamManagerDelegate value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Buffers outbound packets so they can be pulled instead of pushed. + /// + /// Implements [`OutgoingDataStreamManagerDelegate`] in Rust; see the module docs. + /// + public interface IOutgoingPacketQueue + { + /// + /// Wakes a pending [`Self::next_packets`] with `None` so the caller's drain loop can exit. + /// + /// Call this before releasing the queue: a caller blocked in `next_packets` is holding a + /// pointer to it, so freeing it first is a use-after-free. + /// + void Close(); + + /// + /// Awaits the next batch of encoded `livekit.DataPacket`s to put on the wire. + /// + /// Returns `None` once the manager has shut down, which ends the caller's drain loop. + /// Everything already queued is returned together, so a burst costs one FFI crossing rather + /// than one per packet. + /// + Task NextPackets(); + } + + /// + /// Buffers outbound packets so they can be pulled instead of pushed. + /// + /// Implements [`OutgoingDataStreamManagerDelegate`] in Rust; see the module docs. + /// + public class OutgoingPacketQueue : IOutgoingPacketQueue, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public OutgoingPacketQueue(ulong pointer) + { + this.pointer = pointer; + } + + ~OutgoingPacketQueue() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_outgoingpacketqueue( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_outgoingpacketqueue( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Wakes a pending [`Self::next_packets`] with `None` so the caller's drain loop can exit. + /// + /// Call this before releasing the queue: a caller blocked in `next_packets` is holding a + /// pointer to it, so freeing it first is a use-after-free. + /// + public void Close() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingpacketqueue_close( + thisPtr, + ref _status + ) + ) + ); + } + + /// + /// Awaits the next batch of encoded `livekit.DataPacket`s to put on the wire. + /// + /// Returns `None` once the manager has shut down, which ends the caller's drain loop. + /// Everything already queued is returned together, so a burst costs one FFI crossing rather + /// than one per packet. + /// + public async Task NextPackets() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_outgoingpacketqueue_next_packets( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalSequenceTypeBytes.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeOutgoingPacketQueue : FfiConverter + { + public static FfiConverterTypeOutgoingPacketQueue INSTANCE = + new FfiConverterTypeOutgoingPacketQueue(); + + public override ulong Lower(OutgoingPacketQueue value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override OutgoingPacketQueue Lift(ulong value) + { + return new OutgoingPacketQueue(value); + } + + public override OutgoingPacketQueue Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(OutgoingPacketQueue value) + { + return 8; + } + + public override void Write(OutgoingPacketQueue value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Data track published by the remote participant. + /// + public interface IRemoteDataTrack + { + /// + /// Information about the data track. + /// + DataTrackInfo Info(); + + /// + /// Whether or not the track is currently published. + /// + bool IsPublished(); + + /// + /// Identity of the participant who published the track. + /// + string PublisherIdentity(); + + /// + /// Configures options for the pipeline handling incoming packets for this track. + /// + /// These options apply to all current and future subscriptions of this track, and may be + /// set at any time. New options take affect with the next received packet. + /// + void SetPipelineOptions(RemoteDataTrackPipelineOptions @options); + + /// + /// Subscribes to the data track. + /// + /// + Task Subscribe(); + + /// + /// Subscribes to the data track with custom options. + /// + /// + Task SubscribeWithOptions(DataTrackSubscribeOptions @options); + + /// + /// Waits asynchronously until the track is unpublished. + /// + /// Use this to trigger follow-up work once the track is no longer published. + /// If the track is already unpublished, this method returns immediately. + /// + Task WaitForUnpublish(); + } + + /// + /// Data track published by the remote participant. + /// + public class RemoteDataTrack : IRemoteDataTrack, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public RemoteDataTrack(ulong pointer) + { + this.pointer = pointer; + } + + ~RemoteDataTrack() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_remotedatatrack(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_remotedatatrack( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Information about the data track. + /// + public DataTrackInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeDataTrackInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Whether or not the track is currently published. + /// + public bool IsPublished() + { + return CallWithPointer(thisPtr => + FfiConverterBoolean.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_is_published( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Identity of the participant who published the track. + /// + public string PublisherIdentity() + { + return CallWithPointer(thisPtr => + FfiConverterString.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_publisher_identity( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Configures options for the pipeline handling incoming packets for this track. + /// + /// These options apply to all current and future subscriptions of this track, and may be + /// set at any time. New options take affect with the next received packet. + /// + public void SetPipelineOptions(RemoteDataTrackPipelineOptions @options) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_set_pipeline_options( + thisPtr, + FfiConverterTypeRemoteDataTrackPipelineOptions.INSTANCE.Lower(@options), + ref _status + ) + ) + ); + } + + /// + /// Subscribes to the data track. + /// + /// + public async Task Subscribe() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_subscribe( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterTypeDataTrackStream.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataTrackSubscribeError.INSTANCE + ); + } + + /// + /// Subscribes to the data track with custom options. + /// + /// + public async Task SubscribeWithOptions(DataTrackSubscribeOptions @options) + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_subscribe_with_options( + thisPtr, + FfiConverterTypeDataTrackSubscribeOptions.INSTANCE.Lower(@options) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_u64(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_u64(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_u64(future), + // Lift + (result) => FfiConverterTypeDataTrackStream.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataTrackSubscribeError.INSTANCE + ); + } + + /// + /// Waits asynchronously until the track is unpublished. + /// + /// Use this to trigger follow-up work once the track is no longer published. + /// If the track is already unpublished, this method returns immediately. + /// + public async Task WaitForUnpublish() + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrack_wait_for_unpublish( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + } + + class FfiConverterTypeRemoteDataTrack : FfiConverter + { + public static FfiConverterTypeRemoteDataTrack INSTANCE = new FfiConverterTypeRemoteDataTrack(); + + public override ulong Lower(RemoteDataTrack value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override RemoteDataTrack Lift(ulong value) + { + return new RemoteDataTrack(value); + } + + public override RemoteDataTrack Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(RemoteDataTrack value) + { + return 8; + } + + public override void Write(RemoteDataTrack value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// System for managing data track subscriptions. + /// + public interface IRemoteDataTrackManager + { + /// + /// Handles a encoded packet received over the data channel. + /// + void HandlePacketReceived(Bytes @packet); + + /// + /// Handles a serialized `JoinResponse` signal response from the SFU. + /// + /// Invoke once after initial join. This is used to detect pre-existing + /// track publications by remote participants. + /// + /// + void HandleSfuJoinResponse(byte[] @res); + + /// + /// Handles a serialized `ParticipantUpdate` signal response from the SFU. + /// + /// Invoke every time a participant update is received. This is used to detect changes + /// to track publication by remote participants. + /// + /// Note: the local participant identity is required to exclude data tracks published by the + /// local participant from being treated as remote tracks. + /// + /// + void HandleSfuParticipantUpdate(byte[] @res, string @localParticipantIdentity); + + /// + /// Handles a serialized `DataTrackSubscriberHandles` signal response from the SFU. + /// + /// + void HandleSubscriberHandles(byte[] @res); + + /// + /// Resend all subscription updates. + /// + /// This must be sent after a full reconnect to ensure the SFU knows which tracks + /// are subscribed to locally. + /// + void ResendSubscriptionUpdates(); + } + + /// + /// System for managing data track subscriptions. + /// + public class RemoteDataTrackManager : IRemoteDataTrackManager, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public RemoteDataTrackManager(ulong pointer) + { + this.pointer = pointer; + } + + ~RemoteDataTrackManager() + { + Destroy(); + } + + public RemoteDataTrackManager( + RemoteDataTrackManagerDelegate @delegate, + DecryptionProvider? @decryptionProvider + ) + : this( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_constructor_remotedatatrackmanager_new( + FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.Lower(@delegate), + FfiConverterOptionalTypeDecryptionProvider.INSTANCE.Lower( + @decryptionProvider + ), + ref _status + ) + ) + ) { } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_remotedatatrackmanager( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_remotedatatrackmanager( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Handles a encoded packet received over the data channel. + /// + public void HandlePacketReceived(Bytes @packet) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_packet_received( + thisPtr, + FfiConverterTypeBytes.INSTANCE.Lower(@packet), + ref _status + ) + ) + ); + } + + /// + /// Handles a serialized `JoinResponse` signal response from the SFU. + /// + /// Invoke once after initial join. This is used to detect pre-existing + /// track publications by remote participants. + /// + /// + public void HandleSfuJoinResponse(byte[] @res) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypeHandleSignalResponseError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_sfu_join_response( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@res), + ref _status + ) + ) + ); + } + + /// + /// Handles a serialized `ParticipantUpdate` signal response from the SFU. + /// + /// Invoke every time a participant update is received. This is used to detect changes + /// to track publication by remote participants. + /// + /// Note: the local participant identity is required to exclude data tracks published by the + /// local participant from being treated as remote tracks. + /// + /// + public void HandleSfuParticipantUpdate(byte[] @res, string @localParticipantIdentity) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypeHandleSignalResponseError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_sfu_participant_update( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@res), + FfiConverterString.INSTANCE.Lower(@localParticipantIdentity), + ref _status + ) + ) + ); + } + + /// + /// Handles a serialized `DataTrackSubscriberHandles` signal response from the SFU. + /// + /// + public void HandleSubscriberHandles(byte[] @res) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCallWithError( + FfiConverterTypeHandleSignalResponseError.INSTANCE, + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_handle_subscriber_handles( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@res), + ref _status + ) + ) + ); + } + + /// + /// Resend all subscription updates. + /// + /// This must be sent after a full reconnect to ensure the SFU knows which tracks + /// are subscribed to locally. + /// + public void ResendSubscriptionUpdates() + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanager_resend_subscription_updates( + thisPtr, + ref _status + ) + ) + ); + } + } + + class FfiConverterTypeRemoteDataTrackManager : FfiConverter + { + public static FfiConverterTypeRemoteDataTrackManager INSTANCE = + new FfiConverterTypeRemoteDataTrackManager(); + + public override ulong Lower(RemoteDataTrackManager value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override RemoteDataTrackManager Lift(ulong value) + { + return new RemoteDataTrackManager(value); + } + + public override RemoteDataTrackManager Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(RemoteDataTrackManager value) + { + return 8; + } + + public override void Write(RemoteDataTrackManager value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Delegate for receiving output events from [`RemoteDataTrackManager`]. + /// + public interface RemoteDataTrackManagerDelegate + { + /// + /// Encoded signal request to be forwarded to the SFU. + /// + void OnSignalRequest(byte[] @request); + + /// + /// A track has been published by a remote participant and is available to be + /// subscribed to. + /// + /// Emit a public event to deliver the track to the user, allowing them to subscribe + /// with [`RemoteDataTrack::subscribe`] if desired. + /// + void OnTrackPublished(RemoteDataTrack @track); + + /// + /// A track with the given SID has been unpublished by a remote participant. + /// + void OnTrackUnpublished(DataTrackSid @sid); + } + + /// + /// Delegate for receiving output events from [`RemoteDataTrackManager`]. + /// + public class RemoteDataTrackManagerDelegateImpl : RemoteDataTrackManagerDelegate, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public RemoteDataTrackManagerDelegateImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~RemoteDataTrackManagerDelegateImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_remotedatatrackmanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_remotedatatrackmanagerdelegate( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Encoded signal request to be forwarded to the SFU. + /// + public void OnSignalRequest(byte[] @request) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_signal_request( + thisPtr, + FfiConverterByteArray.INSTANCE.Lower(@request), + ref _status + ) + ) + ); + } + + /// + /// A track has been published by a remote participant and is available to be + /// subscribed to. + /// + /// Emit a public event to deliver the track to the user, allowing them to subscribe + /// with [`RemoteDataTrack::subscribe`] if desired. + /// + public void OnTrackPublished(RemoteDataTrack @track) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_track_published( + thisPtr, + FfiConverterTypeRemoteDataTrack.INSTANCE.Lower(@track), + ref _status + ) + ) + ); + } + + /// + /// A track with the given SID has been unpublished by a remote participant. + /// + public void OnTrackUnpublished(DataTrackSid @sid) + { + CallWithPointer(thisPtr => + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remotedatatrackmanagerdelegate_on_track_unpublished( + thisPtr, + FfiConverterTypeDataTrackSid.INSTANCE.Lower(@sid), + ref _status + ) + ) + ); + } + } + + class UniffiCallbackInterfaceRemoteDataTrackManagerDelegate + { + static void OnSignalRequest( + ulong @uniffiHandle, + RustBuffer @request, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnSignalRequest(FfiConverterByteArray.INSTANCE.Lift(@request)); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void OnTrackPublished( + ulong @uniffiHandle, + ulong @track, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnTrackPublished(FfiConverterTypeRemoteDataTrack.INSTANCE.Lift(@track)); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void OnTrackUnpublished( + ulong @uniffiHandle, + RustBuffer @sid, + IntPtr @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + uniffiObject.OnTrackUnpublished(FfiConverterTypeDataTrackSid.INSTANCE.Lift(@sid)); + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if ( + !FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.TryGet( + @handle, + out var obj + ) + ) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeRemoteDataTrackManagerDelegate.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod0( + OnSignalRequest + ); + static _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod1 _m1 = + new _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod1( + OnTrackPublished + ); + static _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod2 _m2 = + new _UniFFILib.UniffiCallbackInterfaceRemoteDataTrackManagerDelegateMethod2( + OnTrackUnpublished + ); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceRemoteDataTrackManagerDelegate _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceRemoteDataTrackManagerDelegate + { + @onSignalRequest = Marshal.GetFunctionPointerForDelegate(_m0), + @onTrackPublished = Marshal.GetFunctionPointerForDelegate(_m1), + @onTrackUnpublished = Marshal.GetFunctionPointerForDelegate(_m2), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_uniffi_fn_init_callback_vtable_remotedatatrackmanagerdelegate( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeRemoteDataTrackManagerDelegate + : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeRemoteDataTrackManagerDelegate INSTANCE = + new FfiConverterTypeRemoteDataTrackManagerDelegate(); + + static FfiConverterTypeRemoteDataTrackManagerDelegate() + { + UniffiCallbackInterfaceRemoteDataTrackManagerDelegate.Register(); + } + + public override ulong Lower(RemoteDataTrackManagerDelegate value) + { + if (value is RemoteDataTrackManagerDelegateImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override RemoteDataTrackManagerDelegate Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new RemoteDataTrackManagerDelegateImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override RemoteDataTrackManagerDelegate Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(RemoteDataTrackManagerDelegate value) + { + return 8; + } + + public override void Write(RemoteDataTrackManagerDelegate value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Read access to remote participants' advertised protocol and capabilities, implemented by the + /// foreign side. Mirrors [`lk_common::RemoteParticipantRegistry`]; used to decide inline/compression + /// eligibility per send. + /// + public interface RemoteParticipantRegistryDelegate + { + /// + /// A remote participant's `client_protocol`, or `0` (`CLIENT_PROTOCOL_DEFAULT`) if unknown. + /// + int RemoteClientProtocol(string @identity); + + /// + /// A remote participant's advertised capabilities, or empty if unknown. + /// + ClientCapability[] RemoteCapabilities(string @identity); + + /// + /// The identities of every remote participant, used to resolve a broadcast send. + /// + string[] RemoteIdentities(); + } + + /// + /// Read access to remote participants' advertised protocol and capabilities, implemented by the + /// foreign side. Mirrors [`lk_common::RemoteParticipantRegistry`]; used to decide inline/compression + /// eligibility per send. + /// + public class RemoteParticipantRegistryDelegateImpl : RemoteParticipantRegistryDelegate, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public RemoteParticipantRegistryDelegateImpl(ulong pointer) + { + this.pointer = pointer; + } + + ~RemoteParticipantRegistryDelegateImpl() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_remoteparticipantregistrydelegate( + this.pointer, + ref status + ); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_remoteparticipantregistrydelegate( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// A remote participant's `client_protocol`, or `0` (`CLIENT_PROTOCOL_DEFAULT`) if unknown. + /// + public int RemoteClientProtocol(string @identity) + { + return CallWithPointer(thisPtr => + FfiConverterInt32.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_client_protocol( + thisPtr, + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ) + ); + } + + /// + /// A remote participant's advertised capabilities, or empty if unknown. + /// + public ClientCapability[] RemoteCapabilities(string @identity) + { + return CallWithPointer(thisPtr => + FfiConverterSequenceTypeClientCapability.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_capabilities( + thisPtr, + FfiConverterString.INSTANCE.Lower(@identity), + ref _status + ) + ) + ) + ); + } + + /// + /// The identities of every remote participant, used to resolve a broadcast send. + /// + public string[] RemoteIdentities() + { + return CallWithPointer(thisPtr => + FfiConverterSequenceString.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_remoteparticipantregistrydelegate_remote_identities( + thisPtr, + ref _status + ) + ) + ) + ); + } + } + + class UniffiCallbackInterfaceRemoteParticipantRegistryDelegate + { + static void RemoteClientProtocol( + ulong @uniffiHandle, + RustBuffer @identity, + IntPtr /*int*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + var result = uniffiObject.RemoteClientProtocol( + FfiConverterString.INSTANCE.Lift(@identity) + ); + unsafe + { + *(int*)uniffiOutReturn = FfiConverterInt32.INSTANCE.Lower(result); + } + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void RemoteCapabilities( + ulong @uniffiHandle, + RustBuffer @identity, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + var result = uniffiObject.RemoteCapabilities( + FfiConverterString.INSTANCE.Lift(@identity) + ); + unsafe + { + *(RustBuffer*)uniffiOutReturn = + FfiConverterSequenceTypeClientCapability.INSTANCE.Lower(result); + } + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void RemoteIdentities( + ulong @uniffiHandle, + IntPtr /*RustBuffer*/ + @uniffiOutReturn, + ref UniffiRustCallStatus _uniffi_out_err + ) + { + var handle = @uniffiHandle; + try + { + if ( + !FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.TryGet( + handle, + out var uniffiObject + ) + ) + { + throw new InternalException($"No callback in handlemap '{handle}'"); + } + var result = uniffiObject.RemoteIdentities(); + unsafe + { + *(RustBuffer*)uniffiOutReturn = FfiConverterSequenceString.INSTANCE.Lower(result); + } + + _uniffi_out_err.code = UniffiCallbackResponseStatus.SUCCESS; + } + catch (System.Exception e) + { + _uniffi_out_err.code = UniffiCallbackResponseStatus.UNEXPECTED_ERROR; + try + { + _uniffi_out_err.error_buf = FfiConverterString.INSTANCE.Lower(e.Message); + } + catch { } + } + } + + static void UniffiFree(ulong @handle) + { + FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.Remove(@handle); + } + + static ulong UniffiClone(ulong @handle) + { + try + { + if ( + !FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.TryGet( + @handle, + out var obj + ) + ) + { + throw new InternalException($"No callback in handlemap '{@handle}'"); + } + return FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.handleMap.Insert(obj); + } + catch (System.Exception) + { + return 0; // 0 is never a valid handle; ConcurrentHandleMap starts at 1 + } + } + + static _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod0 _m0 = + new _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod0( + RemoteClientProtocol + ); + static _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod1 _m1 = + new _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod1( + RemoteCapabilities + ); + static _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod2 _m2 = + new _UniFFILib.UniffiCallbackInterfaceRemoteParticipantRegistryDelegateMethod2( + RemoteIdentities + ); + static _UniFFILib.UniffiCallbackInterfaceFree _callback_interface_free = + new _UniFFILib.UniffiCallbackInterfaceFree(UniffiFree); + static _UniFFILib.UniffiCallbackInterfaceClone _callback_interface_clone = + new _UniFFILib.UniffiCallbackInterfaceClone(UniffiClone); + + private static GCHandle? _vtablePin; + + public static void Register() + { + if (_vtablePin.HasValue) + return; + _UniFFILib.UniffiVTableCallbackInterfaceRemoteParticipantRegistryDelegate _vtable = + new _UniFFILib.UniffiVTableCallbackInterfaceRemoteParticipantRegistryDelegate + { + @remoteClientProtocol = Marshal.GetFunctionPointerForDelegate(_m0), + @remoteCapabilities = Marshal.GetFunctionPointerForDelegate(_m1), + @remoteIdentities = Marshal.GetFunctionPointerForDelegate(_m2), + @uniffiFree = Marshal.GetFunctionPointerForDelegate(_callback_interface_free), + @uniffiClone = Marshal.GetFunctionPointerForDelegate(_callback_interface_clone), + }; + + // Pin the vtable so the GC never moves it. The GCHandle is intentionally never freed — + // this pin must remain valid for the process lifetime. + _vtablePin = GCHandle.Alloc(_vtable, GCHandleType.Pinned); + _UniFFILib.uniffi_livekit_uniffi_fn_init_callback_vtable_remoteparticipantregistrydelegate( + _vtablePin.Value.AddrOfPinnedObject() + ); + } + } + + class FfiConverterTypeRemoteParticipantRegistryDelegate + : FfiConverter + { + public ConcurrentHandleMap handleMap = + new ConcurrentHandleMap(); + + public static FfiConverterTypeRemoteParticipantRegistryDelegate INSTANCE = + new FfiConverterTypeRemoteParticipantRegistryDelegate(); + + static FfiConverterTypeRemoteParticipantRegistryDelegate() + { + UniffiCallbackInterfaceRemoteParticipantRegistryDelegate.Register(); + } + + public override ulong Lower(RemoteParticipantRegistryDelegate value) + { + if (value is RemoteParticipantRegistryDelegateImpl rustObj) + { + // Rust-implemented object. Clone the handle and return it. + return rustObj.CallWithPointer(thisPtr => thisPtr); + } + else + { + // C# object, generate a new handle map entry and return it. + return handleMap.Insert(value); + } + } + + public override RemoteParticipantRegistryDelegate Lift(ulong value) + { + if ((value & 1UL) == 0UL) + { + // Rust-generated handle, construct a new wrapper. + return new RemoteParticipantRegistryDelegateImpl(value); + } + else + { + // C#-generated handle, retrieve and remove from the handle map. + if (handleMap.Remove(value, out var obj)) + { + return obj; + } + else + { + throw new InternalException($"No callback in handlemap '{value}'"); + } + } + } + + public override RemoteParticipantRegistryDelegate Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(RemoteParticipantRegistryDelegate value) + { + return 8; + } + + public override void Write(RemoteParticipantRegistryDelegate value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Reader for an incoming text data stream. + /// + public interface ITextStreamReader + { + /// + /// Information about the underlying stream. + /// + TextStreamInfo Info(); + + /// + /// Returns the next chunk, or `None` once the stream has closed. + /// + /// + Task Next(); + + /// + /// Reads every chunk, concatenating them into a single string returned once the stream closes. + /// + /// + Task ReadAll(); + } + + /// + /// Reader for an incoming text data stream. + /// + public class TextStreamReader : ITextStreamReader, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public TextStreamReader(ulong pointer) + { + this.pointer = pointer; + } + + ~TextStreamReader() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_textstreamreader(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_textstreamreader( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Information about the underlying stream. + /// + public TextStreamInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeTextStreamInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamreader_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Returns the next chunk, or `None` once the stream has closed. + /// + /// + public async Task Next() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamreader_next(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterOptionalString.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Reads every chunk, concatenating them into a single string returned once the stream closes. + /// + /// + public async Task ReadAll() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamreader_read_all( + thisPtr + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_rust_buffer( + future, + continuation, + data + ), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_rust_buffer( + future, + ref status + ); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_rust_buffer(future), + // Lift + (result) => FfiConverterString.INSTANCE.Lift(result), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + } + + class FfiConverterTypeTextStreamReader : FfiConverter + { + public static FfiConverterTypeTextStreamReader INSTANCE = + new FfiConverterTypeTextStreamReader(); + + public override ulong Lower(TextStreamReader value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override TextStreamReader Lift(ulong value) + { + return new TextStreamReader(value); + } + + public override TextStreamReader Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(TextStreamReader value) + { + return 8; + } + + public override void Write(TextStreamReader value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// Writer for an open text data stream. + /// + public interface ITextStreamWriter + { + /// + /// Closes the stream normally. + /// + /// + Task Close(); + + /// + /// Closes the stream abnormally with a reason. + /// + /// + Task CloseWithReason(string @reason); + + /// + /// Information about the underlying stream. + /// + TextStreamInfo Info(); + + /// + /// Whether the stream is still open — false once it has been closed locally or a send has failed. + /// + Task IsOpen(); + + /// + /// Appends text to the stream. + /// + /// + Task Write(string @text); + } + + /// + /// Writer for an open text data stream. + /// + public class TextStreamWriter : ITextStreamWriter, IDisposable + { + protected ulong pointer; + private int _wasDestroyed = 0; + private long _callCounter = 1; + + public TextStreamWriter(ulong pointer) + { + this.pointer = pointer; + } + + ~TextStreamWriter() + { + Destroy(); + } + + protected void FreeRustArcPtr() + { + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + _UniFFILib.uniffi_livekit_uniffi_fn_free_textstreamwriter(this.pointer, ref status); + } + ); + } + + protected ulong CloneRustArcPtr() + { + return _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus status) => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_clone_textstreamwriter( + this.pointer, + ref status + ); + } + ); + } + + public void Destroy() + { + // Only allow a single call to this method. + if (Interlocked.CompareExchange(ref _wasDestroyed, 1, 0) == 0) + { + // This decrement always matches the initial count of 1 given at creation time. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + } + + public void Dispose() + { + Destroy(); + GC.SuppressFinalize(this); // Suppress finalization to avoid unnecessary GC overhead. + } + + private void IncrementCallCounter() + { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + long count; + do + { + count = Interlocked.Read(ref _callCounter); + if (count == 0L) + throw new System.ObjectDisposedException( + String.Format("'{0}' object has already been destroyed", this.GetType().Name) + ); + if (count == long.MaxValue) + throw new System.OverflowException( + String.Format("'{0}' call counter would overflow", this.GetType().Name) + ); + } while (Interlocked.CompareExchange(ref _callCounter, count + 1, count) != count); + } + + private void DecrementCallCounter() + { + // This decrement always matches the increment we performed above. + if (Interlocked.Decrement(ref _callCounter) == 0) + { + FreeRustArcPtr(); + } + } + + internal void CallWithPointer(Action action) + { + IncrementCallCounter(); + try + { + action(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + internal T CallWithPointer(Func func) + { + IncrementCallCounter(); + try + { + return func(CloneRustArcPtr()); + } + finally + { + DecrementCallCounter(); + } + } + + /// + /// Closes the stream normally. + /// + /// + public async Task Close() + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamwriter_close(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Closes the stream abnormally with a reason. + /// + /// + public async Task CloseWithReason(string @reason) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamwriter_close_with_reason( + thisPtr, + FfiConverterString.INSTANCE.Lower(@reason) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + + /// + /// Information about the underlying stream. + /// + public TextStreamInfo Info() + { + return CallWithPointer(thisPtr => + FfiConverterTypeTextStreamInfo.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamwriter_info( + thisPtr, + ref _status + ) + ) + ) + ); + } + + /// + /// Whether the stream is still open — false once it has been closed locally or a send has failed. + /// + public async Task IsOpen() + { + return await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamwriter_is_open(thisPtr); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_i8(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + return _UniFFILib.ffi_livekit_uniffi_rust_future_complete_i8(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_i8(future), + // Lift + (result) => FfiConverterBoolean.INSTANCE.Lift(result), + // Error + NullCallStatusErrorHandler.INSTANCE + ); + } + + /// + /// Appends text to the stream. + /// + /// + public async Task Write(string @text) + { + await _UniFFIAsync.UniffiRustCallAsync( + // Get rust future + CallWithPointer(thisPtr => + { + return _UniFFILib.uniffi_livekit_uniffi_fn_method_textstreamwriter_write( + thisPtr, + FfiConverterString.INSTANCE.Lower(@text) + ); + }), + // Poll + (ulong future, IntPtr continuation, ulong data) => + _UniFFILib.ffi_livekit_uniffi_rust_future_poll_void(future, continuation, data), + // Complete + (ulong future, ref UniffiRustCallStatus status) => + { + _UniFFILib.ffi_livekit_uniffi_rust_future_complete_void(future, ref status); + }, + // Free + (ulong future) => _UniFFILib.ffi_livekit_uniffi_rust_future_free_void(future), + // Error + FfiConverterTypeDataStreamError.INSTANCE + ); + } + } + + class FfiConverterTypeTextStreamWriter : FfiConverter + { + public static FfiConverterTypeTextStreamWriter INSTANCE = + new FfiConverterTypeTextStreamWriter(); + + public override ulong Lower(TextStreamWriter value) + { + return value.CallWithPointer(thisPtr => thisPtr); + } + + public override TextStreamWriter Lift(ulong value) + { + return new TextStreamWriter(value); + } + + public override TextStreamWriter Read(BigEndianStream stream) + { + return Lift(stream.ReadULong()); + } + + public override int AllocationSize(TextStreamWriter value) + { + return 8; + } + + public override void Write(TextStreamWriter value, BigEndianStream stream) + { + stream.WriteULong(Lower(value)); + } + } + + /// + /// API credentials for access token generation and verification. + /// + public record ApiCredentials(string Key, string Secret) { } + + class FfiConverterTypeApiCredentials : FfiConverterRustBuffer + { + public static FfiConverterTypeApiCredentials INSTANCE = new FfiConverterTypeApiCredentials(); + + public override ApiCredentials Read(BigEndianStream stream) + { + return new ApiCredentials( + Key: FfiConverterString.INSTANCE.Read(stream), + Secret: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(ApiCredentials value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Key) + + FfiConverterString.INSTANCE.AllocationSize(value.Secret); + } + + public override void Write(ApiCredentials value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Key, stream); + FfiConverterString.INSTANCE.Write(value.Secret, stream); + } + } + + /// + /// Information about a byte data stream. FFI wrapper around [`ds_api::ByteStreamInfo`]. + /// + /// + /// Unix timestamp in milliseconds. + /// + public record ByteStreamInfo( + string Id, + string Topic, + /// + /// Unix timestamp in milliseconds. + /// + long TimestampMs, + ulong? TotalLength, + Dictionary Attributes, + string MimeType, + string Name, + EncryptionType EncryptionType + ) { } + + class FfiConverterTypeByteStreamInfo : FfiConverterRustBuffer + { + public static FfiConverterTypeByteStreamInfo INSTANCE = new FfiConverterTypeByteStreamInfo(); + + public override ByteStreamInfo Read(BigEndianStream stream) + { + return new ByteStreamInfo( + Id: FfiConverterString.INSTANCE.Read(stream), + Topic: FfiConverterString.INSTANCE.Read(stream), + TimestampMs: FfiConverterInt64.INSTANCE.Read(stream), + TotalLength: FfiConverterOptionalUInt64.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + MimeType: FfiConverterString.INSTANCE.Read(stream), + Name: FfiConverterString.INSTANCE.Read(stream), + EncryptionType: FfiConverterTypeEncryptionType.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(ByteStreamInfo value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Id) + + FfiConverterString.INSTANCE.AllocationSize(value.Topic) + + FfiConverterInt64.INSTANCE.AllocationSize(value.TimestampMs) + + FfiConverterOptionalUInt64.INSTANCE.AllocationSize(value.TotalLength) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterString.INSTANCE.AllocationSize(value.MimeType) + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterTypeEncryptionType.INSTANCE.AllocationSize(value.EncryptionType); + } + + public override void Write(ByteStreamInfo value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Id, stream); + FfiConverterString.INSTANCE.Write(value.Topic, stream); + FfiConverterInt64.INSTANCE.Write(value.TimestampMs, stream); + FfiConverterOptionalUInt64.INSTANCE.Write(value.TotalLength, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterString.INSTANCE.Write(value.MimeType, stream); + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterTypeEncryptionType.INSTANCE.Write(value.EncryptionType, stream); + } + } + + /// + /// Claims decoded from a valid access token. + /// + public record Claims( + ulong Exp, + string Iss, + ulong Nbf, + string Sub, + string Name, + VideoGrants Video, + SipGrants Sip, + string Sha256, + string Metadata, + Dictionary Attributes, + RoomConfiguration? RoomConfiguration + ) { } + + class FfiConverterTypeClaims : FfiConverterRustBuffer + { + public static FfiConverterTypeClaims INSTANCE = new FfiConverterTypeClaims(); + + public override Claims Read(BigEndianStream stream) + { + return new Claims( + Exp: FfiConverterUInt64.INSTANCE.Read(stream), + Iss: FfiConverterString.INSTANCE.Read(stream), + Nbf: FfiConverterUInt64.INSTANCE.Read(stream), + Sub: FfiConverterString.INSTANCE.Read(stream), + Name: FfiConverterString.INSTANCE.Read(stream), + Video: FfiConverterTypeVideoGrants.INSTANCE.Read(stream), + Sip: FfiConverterTypeSIPGrants.INSTANCE.Read(stream), + Sha256: FfiConverterString.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(Claims value) + { + return 0 + + FfiConverterUInt64.INSTANCE.AllocationSize(value.Exp) + + FfiConverterString.INSTANCE.AllocationSize(value.Iss) + + FfiConverterUInt64.INSTANCE.AllocationSize(value.Nbf) + + FfiConverterString.INSTANCE.AllocationSize(value.Sub) + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterTypeVideoGrants.INSTANCE.AllocationSize(value.Video) + + FfiConverterTypeSIPGrants.INSTANCE.AllocationSize(value.Sip) + + FfiConverterString.INSTANCE.AllocationSize(value.Sha256) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( + value.RoomConfiguration + ); + } + + public override void Write(Claims value, BigEndianStream stream) + { + FfiConverterUInt64.INSTANCE.Write(value.Exp, stream); + FfiConverterString.INSTANCE.Write(value.Iss, stream); + FfiConverterUInt64.INSTANCE.Write(value.Nbf, stream); + FfiConverterString.INSTANCE.Write(value.Sub, stream); + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterTypeVideoGrants.INSTANCE.Write(value.Video, stream); + FfiConverterTypeSIPGrants.INSTANCE.Write(value.Sip, stream); + FfiConverterString.INSTANCE.Write(value.Sha256, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + } + } + + /// + /// A stream closed by a remote participant (or terminated by an error/abort); see + /// [`IncomingDataStreamManagerDelegate::on_stream_closed`]. + /// + /// + /// Id of the stream that closed. + /// + /// + /// Identity of the participant that opened the stream. + /// + public record ClosedStream( + /// + /// Id of the stream that closed. + /// + string StreamId, + /// + /// Identity of the participant that opened the stream. + /// + string Identity + ) { } + + class FfiConverterTypeClosedStream : FfiConverterRustBuffer + { + public static FfiConverterTypeClosedStream INSTANCE = new FfiConverterTypeClosedStream(); + + public override ClosedStream Read(BigEndianStream stream) + { + return new ClosedStream( + StreamId: FfiConverterString.INSTANCE.Read(stream), + Identity: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(ClosedStream value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.StreamId) + + FfiConverterString.INSTANCE.AllocationSize(value.Identity); + } + + public override void Write(ClosedStream value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.StreamId, stream); + FfiConverterString.INSTANCE.Write(value.Identity, stream); + } + } + + /// + /// A frame published on a data track. + /// + /// FFI wrapper around [`livekit_datatrack::api::DataTrackFrame`]. The underlying type uses the + /// builder pattern with private fields. + /// + public record DataTrackFrame(Bytes Payload, ulong? UserTimestamp) { } + + class FfiConverterTypeDataTrackFrame : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackFrame INSTANCE = new FfiConverterTypeDataTrackFrame(); + + public override DataTrackFrame Read(BigEndianStream stream) + { + return new DataTrackFrame( + Payload: FfiConverterTypeBytes.INSTANCE.Read(stream), + UserTimestamp: FfiConverterOptionalUInt64.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(DataTrackFrame value) + { + return 0 + + FfiConverterTypeBytes.INSTANCE.AllocationSize(value.Payload) + + FfiConverterOptionalUInt64.INSTANCE.AllocationSize(value.UserTimestamp); + } + + public override void Write(DataTrackFrame value, BigEndianStream stream) + { + FfiConverterTypeBytes.INSTANCE.Write(value.Payload, stream); + FfiConverterOptionalUInt64.INSTANCE.Write(value.UserTimestamp, stream); + } + } + + /// + /// Information about a published data track. + /// + public record DataTrackInfo( + DataTrackSid Sid, + string Name, + bool UsesE2ee, + DataTrackSchemaId? Schema, + DataTrackFrameEncoding? FrameEncoding + ) { } + + class FfiConverterTypeDataTrackInfo : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackInfo INSTANCE = new FfiConverterTypeDataTrackInfo(); + + public override DataTrackInfo Read(BigEndianStream stream) + { + return new DataTrackInfo( + Sid: FfiConverterTypeDataTrackSid.INSTANCE.Read(stream), + Name: FfiConverterString.INSTANCE.Read(stream), + UsesE2ee: FfiConverterBoolean.INSTANCE.Read(stream), + Schema: FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.Read(stream), + FrameEncoding: FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(DataTrackInfo value) + { + return 0 + + FfiConverterTypeDataTrackSid.INSTANCE.AllocationSize(value.Sid) + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.UsesE2ee) + + FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.AllocationSize(value.Schema) + + FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.AllocationSize( + value.FrameEncoding + ); + } + + public override void Write(DataTrackInfo value, BigEndianStream stream) + { + FfiConverterTypeDataTrackSid.INSTANCE.Write(value.Sid, stream); + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterBoolean.INSTANCE.Write(value.UsesE2ee, stream); + FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.Write(value.Schema, stream); + FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.Write(value.FrameEncoding, stream); + } + } + + /// + /// Options for publishing a data track. + /// + /// FFI wrapper around [`livekit_datatrack::api::DataTrackOptions`]. The underlying type uses the + /// builder pattern with private fields. + /// + public record DataTrackOptions( + string Name, + DataTrackSchemaId? Schema = null, + DataTrackFrameEncoding? FrameEncoding = null + ) { } + + class FfiConverterTypeDataTrackOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackOptions INSTANCE = + new FfiConverterTypeDataTrackOptions(); + + public override DataTrackOptions Read(BigEndianStream stream) + { + return new DataTrackOptions( + Name: FfiConverterString.INSTANCE.Read(stream), + Schema: FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.Read(stream), + FrameEncoding: FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(DataTrackOptions value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.AllocationSize(value.Schema) + + FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.AllocationSize( + value.FrameEncoding + ); + } + + public override void Write(DataTrackOptions value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterOptionalTypeDataTrackSchemaId.INSTANCE.Write(value.Schema, stream); + FfiConverterOptionalTypeDataTrackFrameEncoding.INSTANCE.Write(value.FrameEncoding, stream); + } + } + + /// + /// Identifier for a data track schema. + /// + /// A compound identifier with two components: name and encoding. + /// + public record DataTrackSchemaId(string Name, DataTrackSchemaEncoding Encoding) { } + + class FfiConverterTypeDataTrackSchemaId : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackSchemaId INSTANCE = + new FfiConverterTypeDataTrackSchemaId(); + + public override DataTrackSchemaId Read(BigEndianStream stream) + { + return new DataTrackSchemaId( + Name: FfiConverterString.INSTANCE.Read(stream), + Encoding: FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(DataTrackSchemaId value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.AllocationSize(value.Encoding); + } + + public override void Write(DataTrackSchemaId value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.Write(value.Encoding, stream); + } + } + + /// + /// Maximum number of received frames buffered internally. Zero is clamped to one. + /// + public record DataTrackSubscribeOptions( + /// + /// Maximum number of received frames buffered internally. Zero is clamped to one. + /// + uint BufferSize = 16u + ) { } + + class FfiConverterTypeDataTrackSubscribeOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackSubscribeOptions INSTANCE = + new FfiConverterTypeDataTrackSubscribeOptions(); + + public override DataTrackSubscribeOptions Read(BigEndianStream stream) + { + return new DataTrackSubscribeOptions(BufferSize: FfiConverterUInt32.INSTANCE.Read(stream)); + } + + public override int AllocationSize(DataTrackSubscribeOptions value) + { + return 0 + FfiConverterUInt32.INSTANCE.AllocationSize(value.BufferSize); + } + + public override void Write(DataTrackSubscribeOptions value, BigEndianStream stream) + { + FfiConverterUInt32.INSTANCE.Write(value.BufferSize, stream); + } + } + + public record LogForwardEntry( + LogForwardLevel Level, + string Target, + string? File, + uint? Line, + string Message + ) { } + + class FfiConverterTypeLogForwardEntry : FfiConverterRustBuffer + { + public static FfiConverterTypeLogForwardEntry INSTANCE = new FfiConverterTypeLogForwardEntry(); + + public override LogForwardEntry Read(BigEndianStream stream) + { + return new LogForwardEntry( + Level: FfiConverterTypeLogForwardLevel.INSTANCE.Read(stream), + Target: FfiConverterString.INSTANCE.Read(stream), + File: FfiConverterOptionalString.INSTANCE.Read(stream), + Line: FfiConverterOptionalUInt32.INSTANCE.Read(stream), + Message: FfiConverterString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(LogForwardEntry value) + { + return 0 + + FfiConverterTypeLogForwardLevel.INSTANCE.AllocationSize(value.Level) + + FfiConverterString.INSTANCE.AllocationSize(value.Target) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.File) + + FfiConverterOptionalUInt32.INSTANCE.AllocationSize(value.Line) + + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(LogForwardEntry value, BigEndianStream stream) + { + FfiConverterTypeLogForwardLevel.INSTANCE.Write(value.Level, stream); + FfiConverterString.INSTANCE.Write(value.Target, stream); + FfiConverterOptionalString.INSTANCE.Write(value.File, stream); + FfiConverterOptionalUInt32.INSTANCE.Write(value.Line, stream); + FfiConverterString.INSTANCE.Write(value.Message, stream); + } + } + + /// + /// A stream opened by a remote participant. + /// + /// Exactly one of the two readers is set; which one tells you the stream's kind. Two `Option`s + /// rather than an enum keeps the shape trivial in every binding. + /// + /// + /// Identity of the participant that opened the stream. + /// + /// + /// Set when the stream carries bytes. + /// + /// + /// Set when the stream carries text. + /// + public record OpenedStream( + /// + /// Identity of the participant that opened the stream. + /// + string Identity, + /// + /// Set when the stream carries bytes. + /// + ByteStreamReader? ByteReader, + /// + /// Set when the stream carries text. + /// + TextStreamReader? TextReader + ) : IDisposable + { + public void Dispose() + { + FFIObjectUtil.DisposeAll(this.Identity, this.ByteReader, this.TextReader); + } + } + + class FfiConverterTypeOpenedStream : FfiConverterRustBuffer + { + public static FfiConverterTypeOpenedStream INSTANCE = new FfiConverterTypeOpenedStream(); + + public override OpenedStream Read(BigEndianStream stream) + { + return new OpenedStream( + Identity: FfiConverterString.INSTANCE.Read(stream), + ByteReader: FfiConverterOptionalTypeByteStreamReader.INSTANCE.Read(stream), + TextReader: FfiConverterOptionalTypeTextStreamReader.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(OpenedStream value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Identity) + + FfiConverterOptionalTypeByteStreamReader.INSTANCE.AllocationSize(value.ByteReader) + + FfiConverterOptionalTypeTextStreamReader.INSTANCE.AllocationSize(value.TextReader); + } + + public override void Write(OpenedStream value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Identity, stream); + FfiConverterOptionalTypeByteStreamReader.INSTANCE.Write(value.ByteReader, stream); + FfiConverterOptionalTypeTextStreamReader.INSTANCE.Write(value.TextReader, stream); + } + } + + /// + /// An [`IncomingDataStreamManager`] and the queue draining it, already connected. + /// + public record PolledIncomingDataStreamManager( + IncomingDataStreamManager Manager, + IncomingStreamQueue Streams + ) : IDisposable + { + public void Dispose() + { + FFIObjectUtil.DisposeAll(this.Manager, this.Streams); + } + } + + class FfiConverterTypePolledIncomingDataStreamManager + : FfiConverterRustBuffer + { + public static FfiConverterTypePolledIncomingDataStreamManager INSTANCE = + new FfiConverterTypePolledIncomingDataStreamManager(); + + public override PolledIncomingDataStreamManager Read(BigEndianStream stream) + { + return new PolledIncomingDataStreamManager( + Manager: FfiConverterTypeIncomingDataStreamManager.INSTANCE.Read(stream), + Streams: FfiConverterTypeIncomingStreamQueue.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(PolledIncomingDataStreamManager value) + { + return 0 + + FfiConverterTypeIncomingDataStreamManager.INSTANCE.AllocationSize(value.Manager) + + FfiConverterTypeIncomingStreamQueue.INSTANCE.AllocationSize(value.Streams); + } + + public override void Write(PolledIncomingDataStreamManager value, BigEndianStream stream) + { + FfiConverterTypeIncomingDataStreamManager.INSTANCE.Write(value.Manager, stream); + FfiConverterTypeIncomingStreamQueue.INSTANCE.Write(value.Streams, stream); + } + } + + /// + /// An [`OutgoingDataStreamManager`] and the queue draining it, already connected. + /// + public record PolledOutgoingDataStreamManager( + OutgoingDataStreamManager Manager, + OutgoingPacketQueue Packets + ) : IDisposable + { + public void Dispose() + { + FFIObjectUtil.DisposeAll(this.Manager, this.Packets); + } + } + + class FfiConverterTypePolledOutgoingDataStreamManager + : FfiConverterRustBuffer + { + public static FfiConverterTypePolledOutgoingDataStreamManager INSTANCE = + new FfiConverterTypePolledOutgoingDataStreamManager(); + + public override PolledOutgoingDataStreamManager Read(BigEndianStream stream) + { + return new PolledOutgoingDataStreamManager( + Manager: FfiConverterTypeOutgoingDataStreamManager.INSTANCE.Read(stream), + Packets: FfiConverterTypeOutgoingPacketQueue.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(PolledOutgoingDataStreamManager value) + { + return 0 + + FfiConverterTypeOutgoingDataStreamManager.INSTANCE.AllocationSize(value.Manager) + + FfiConverterTypeOutgoingPacketQueue.INSTANCE.AllocationSize(value.Packets); + } + + public override void Write(PolledOutgoingDataStreamManager value, BigEndianStream stream) + { + FfiConverterTypeOutgoingDataStreamManager.INSTANCE.Write(value.Manager, stream); + FfiConverterTypeOutgoingPacketQueue.INSTANCE.Write(value.Packets, stream); + } + } + + /// + /// FFI wrapper around [`livekit_datatrack::api::RemoteDataTrackPipelineOptions`]. The underlying + /// type uses the builder pattern with private fields. + /// + /// + /// Maximum number of partial frames the depacketizer will track concurrently for this track. + /// + /// Higher values give more out-of-order tolerance for high-frequency senders at the cost of + /// additional buffering. Zero is clamped to one. + /// + public record RemoteDataTrackPipelineOptions( + /// + /// Maximum number of partial frames the depacketizer will track concurrently for this track. + /// + /// Higher values give more out-of-order tolerance for high-frequency senders at the cost of + /// additional buffering. Zero is clamped to one. + /// + uint MaxPartialFrames = 1u + ) { } + + class FfiConverterTypeRemoteDataTrackPipelineOptions + : FfiConverterRustBuffer + { + public static FfiConverterTypeRemoteDataTrackPipelineOptions INSTANCE = + new FfiConverterTypeRemoteDataTrackPipelineOptions(); + + public override RemoteDataTrackPipelineOptions Read(BigEndianStream stream) + { + return new RemoteDataTrackPipelineOptions( + MaxPartialFrames: FfiConverterUInt32.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(RemoteDataTrackPipelineOptions value) + { + return 0 + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxPartialFrames); + } + + public override void Write(RemoteDataTrackPipelineOptions value, BigEndianStream stream) + { + FfiConverterUInt32.INSTANCE.Write(value.MaxPartialFrames, stream); + } + } + + /// + /// Agent dispatch configuration + /// + /// Defines which agents should be dispatched to a room. + /// + public record RoomAgentDispatch( + string AgentName, + string Metadata, + int RestartPolicy, + string Deployment, + Dictionary Attributes + ) { } + + class FfiConverterTypeRoomAgentDispatch : FfiConverterRustBuffer + { + public static FfiConverterTypeRoomAgentDispatch INSTANCE = + new FfiConverterTypeRoomAgentDispatch(); + + public override RoomAgentDispatch Read(BigEndianStream stream) + { + return new RoomAgentDispatch( + AgentName: FfiConverterString.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + RestartPolicy: FfiConverterInt32.INSTANCE.Read(stream), + Deployment: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(RoomAgentDispatch value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.AgentName) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterInt32.INSTANCE.AllocationSize(value.RestartPolicy) + + FfiConverterString.INSTANCE.AllocationSize(value.Deployment) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes); + } + + public override void Write(RoomAgentDispatch value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.AgentName, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterInt32.INSTANCE.Write(value.RestartPolicy, stream); + FfiConverterString.INSTANCE.Write(value.Deployment, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + } + } + + /// + /// Room configuration + /// + /// Configuration options for a room. + /// + public record RoomConfiguration( + string Name, + uint EmptyTimeout, + uint DepartureTimeout, + uint MaxParticipants, + string Metadata, + uint MinPlayoutDelay, + uint MaxPlayoutDelay, + bool SyncStreams, + RoomAgentDispatch[] Agents + ) { } + + class FfiConverterTypeRoomConfiguration : FfiConverterRustBuffer + { + public static FfiConverterTypeRoomConfiguration INSTANCE = + new FfiConverterTypeRoomConfiguration(); + + public override RoomConfiguration Read(BigEndianStream stream) + { + return new RoomConfiguration( + Name: FfiConverterString.INSTANCE.Read(stream), + EmptyTimeout: FfiConverterUInt32.INSTANCE.Read(stream), + DepartureTimeout: FfiConverterUInt32.INSTANCE.Read(stream), + MaxParticipants: FfiConverterUInt32.INSTANCE.Read(stream), + Metadata: FfiConverterString.INSTANCE.Read(stream), + MinPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), + MaxPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), + SyncStreams: FfiConverterBoolean.INSTANCE.Read(stream), + Agents: FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(RoomConfiguration value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Name) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.EmptyTimeout) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.DepartureTimeout) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxParticipants) + + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MinPlayoutDelay) + + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxPlayoutDelay) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.SyncStreams) + + FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.AllocationSize(value.Agents); + } + + public override void Write(RoomConfiguration value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Name, stream); + FfiConverterUInt32.INSTANCE.Write(value.EmptyTimeout, stream); + FfiConverterUInt32.INSTANCE.Write(value.DepartureTimeout, stream); + FfiConverterUInt32.INSTANCE.Write(value.MaxParticipants, stream); + FfiConverterString.INSTANCE.Write(value.Metadata, stream); + FfiConverterUInt32.INSTANCE.Write(value.MinPlayoutDelay, stream); + FfiConverterUInt32.INSTANCE.Write(value.MaxPlayoutDelay, stream); + FfiConverterBoolean.INSTANCE.Write(value.SyncStreams, stream); + FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Write(value.Agents, stream); + } + } + + /// + /// SIP grants + /// + /// Maps to the JWT's `sip` field. + /// + public record SipGrants(bool Admin, bool Call) { } + + class FfiConverterTypeSIPGrants : FfiConverterRustBuffer + { + public static FfiConverterTypeSIPGrants INSTANCE = new FfiConverterTypeSIPGrants(); + + public override SipGrants Read(BigEndianStream stream) + { + return new SipGrants( + Admin: FfiConverterBoolean.INSTANCE.Read(stream), + Call: FfiConverterBoolean.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(SipGrants value) + { + return 0 + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Admin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Call); + } + + public override void Write(SipGrants value, BigEndianStream stream) + { + FfiConverterBoolean.INSTANCE.Write(value.Admin, stream); + FfiConverterBoolean.INSTANCE.Write(value.Call, stream); + } + } + + /// + /// Options for sending a byte stream. FFI wrapper around [`ds_api::StreamByteOptions`]. + /// + public record StreamByteOptions( + string Topic, + Dictionary Attributes, + string[] DestinationIdentities = null, + string? Id = null, + string? MimeType = null, + string? Name = null, + ulong? TotalLength = null, + bool? Compress = null, + string? SenderIdentity = null + ) { } + + class FfiConverterTypeStreamByteOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeStreamByteOptions INSTANCE = + new FfiConverterTypeStreamByteOptions(); + + public override StreamByteOptions Read(BigEndianStream stream) + { + return new StreamByteOptions( + Topic: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + DestinationIdentities: FfiConverterSequenceString.INSTANCE.Read(stream), + Id: FfiConverterOptionalString.INSTANCE.Read(stream), + MimeType: FfiConverterOptionalString.INSTANCE.Read(stream), + Name: FfiConverterOptionalString.INSTANCE.Read(stream), + TotalLength: FfiConverterOptionalUInt64.INSTANCE.Read(stream), + Compress: FfiConverterOptionalBoolean.INSTANCE.Read(stream), + SenderIdentity: FfiConverterOptionalString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(StreamByteOptions value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Topic) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.DestinationIdentities) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Id) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.MimeType) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Name) + + FfiConverterOptionalUInt64.INSTANCE.AllocationSize(value.TotalLength) + + FfiConverterOptionalBoolean.INSTANCE.AllocationSize(value.Compress) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.SenderIdentity); + } + + public override void Write(StreamByteOptions value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Topic, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterSequenceString.INSTANCE.Write(value.DestinationIdentities, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Id, stream); + FfiConverterOptionalString.INSTANCE.Write(value.MimeType, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Name, stream); + FfiConverterOptionalUInt64.INSTANCE.Write(value.TotalLength, stream); + FfiConverterOptionalBoolean.INSTANCE.Write(value.Compress, stream); + FfiConverterOptionalString.INSTANCE.Write(value.SenderIdentity, stream); + } + } + + /// + /// Options for sending a text stream. FFI wrapper around [`ds_api::StreamTextOptions`]. + /// + public record StreamTextOptions( + string Topic, + Dictionary Attributes, + string[] DestinationIdentities = null, + string? Id = null, + OperationType? OperationType = null, + int? Version = null, + string? ReplyToStreamId = null, + string[] AttachedStreamIds = null, + bool? Generated = null, + bool? Compress = null, + string? SenderIdentity = null + ) { } + + class FfiConverterTypeStreamTextOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeStreamTextOptions INSTANCE = + new FfiConverterTypeStreamTextOptions(); + + public override StreamTextOptions Read(BigEndianStream stream) + { + return new StreamTextOptions( + Topic: FfiConverterString.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + DestinationIdentities: FfiConverterSequenceString.INSTANCE.Read(stream), + Id: FfiConverterOptionalString.INSTANCE.Read(stream), + OperationType: FfiConverterOptionalTypeOperationType.INSTANCE.Read(stream), + Version: FfiConverterOptionalInt32.INSTANCE.Read(stream), + ReplyToStreamId: FfiConverterOptionalString.INSTANCE.Read(stream), + AttachedStreamIds: FfiConverterSequenceString.INSTANCE.Read(stream), + Generated: FfiConverterOptionalBoolean.INSTANCE.Read(stream), + Compress: FfiConverterOptionalBoolean.INSTANCE.Read(stream), + SenderIdentity: FfiConverterOptionalString.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(StreamTextOptions value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Topic) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.DestinationIdentities) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Id) + + FfiConverterOptionalTypeOperationType.INSTANCE.AllocationSize(value.OperationType) + + FfiConverterOptionalInt32.INSTANCE.AllocationSize(value.Version) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.ReplyToStreamId) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.AttachedStreamIds) + + FfiConverterOptionalBoolean.INSTANCE.AllocationSize(value.Generated) + + FfiConverterOptionalBoolean.INSTANCE.AllocationSize(value.Compress) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.SenderIdentity); + } + + public override void Write(StreamTextOptions value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Topic, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterSequenceString.INSTANCE.Write(value.DestinationIdentities, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Id, stream); + FfiConverterOptionalTypeOperationType.INSTANCE.Write(value.OperationType, stream); + FfiConverterOptionalInt32.INSTANCE.Write(value.Version, stream); + FfiConverterOptionalString.INSTANCE.Write(value.ReplyToStreamId, stream); + FfiConverterSequenceString.INSTANCE.Write(value.AttachedStreamIds, stream); + FfiConverterOptionalBoolean.INSTANCE.Write(value.Generated, stream); + FfiConverterOptionalBoolean.INSTANCE.Write(value.Compress, stream); + FfiConverterOptionalString.INSTANCE.Write(value.SenderIdentity, stream); + } + } + + /// + /// Information about a text data stream. FFI wrapper around [`ds_api::TextStreamInfo`]. + /// + /// + /// Unix timestamp in milliseconds. + /// + public record TextStreamInfo( + string Id, + string Topic, + /// + /// Unix timestamp in milliseconds. + /// + long TimestampMs, + ulong? TotalLength, + Dictionary Attributes, + string MimeType, + OperationType OperationType, + int Version, + string? ReplyToStreamId, + string[] AttachedStreamIds, + bool Generated, + EncryptionType EncryptionType + ) { } + + class FfiConverterTypeTextStreamInfo : FfiConverterRustBuffer + { + public static FfiConverterTypeTextStreamInfo INSTANCE = new FfiConverterTypeTextStreamInfo(); + + public override TextStreamInfo Read(BigEndianStream stream) + { + return new TextStreamInfo( + Id: FfiConverterString.INSTANCE.Read(stream), + Topic: FfiConverterString.INSTANCE.Read(stream), + TimestampMs: FfiConverterInt64.INSTANCE.Read(stream), + TotalLength: FfiConverterOptionalUInt64.INSTANCE.Read(stream), + Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), + MimeType: FfiConverterString.INSTANCE.Read(stream), + OperationType: FfiConverterTypeOperationType.INSTANCE.Read(stream), + Version: FfiConverterInt32.INSTANCE.Read(stream), + ReplyToStreamId: FfiConverterOptionalString.INSTANCE.Read(stream), + AttachedStreamIds: FfiConverterSequenceString.INSTANCE.Read(stream), + Generated: FfiConverterBoolean.INSTANCE.Read(stream), + EncryptionType: FfiConverterTypeEncryptionType.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(TextStreamInfo value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.Id) + + FfiConverterString.INSTANCE.AllocationSize(value.Topic) + + FfiConverterInt64.INSTANCE.AllocationSize(value.TimestampMs) + + FfiConverterOptionalUInt64.INSTANCE.AllocationSize(value.TotalLength) + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterString.INSTANCE.AllocationSize(value.MimeType) + + FfiConverterTypeOperationType.INSTANCE.AllocationSize(value.OperationType) + + FfiConverterInt32.INSTANCE.AllocationSize(value.Version) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.ReplyToStreamId) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.AttachedStreamIds) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Generated) + + FfiConverterTypeEncryptionType.INSTANCE.AllocationSize(value.EncryptionType); + } + + public override void Write(TextStreamInfo value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.Id, stream); + FfiConverterString.INSTANCE.Write(value.Topic, stream); + FfiConverterInt64.INSTANCE.Write(value.TimestampMs, stream); + FfiConverterOptionalUInt64.INSTANCE.Write(value.TotalLength, stream); + FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterString.INSTANCE.Write(value.MimeType, stream); + FfiConverterTypeOperationType.INSTANCE.Write(value.OperationType, stream); + FfiConverterInt32.INSTANCE.Write(value.Version, stream); + FfiConverterOptionalString.INSTANCE.Write(value.ReplyToStreamId, stream); + FfiConverterSequenceString.INSTANCE.Write(value.AttachedStreamIds, stream); + FfiConverterBoolean.INSTANCE.Write(value.Generated, stream); + FfiConverterTypeEncryptionType.INSTANCE.Write(value.EncryptionType, stream); + } + } + + /// + /// Options used for generating an access token. + /// + /// Any fields left empty will use the token generator's defaults. + /// + public record TokenOptions( + TimeSpan? Ttl = null, + VideoGrants? VideoGrants = null, + SipGrants? SipGrants = null, + string? Identity = null, + string? Name = null, + string? Metadata = null, + Dictionary? Attributes = null, + string? Sha256 = null, + RoomConfiguration? RoomConfiguration = null + ) { } + + class FfiConverterTypeTokenOptions : FfiConverterRustBuffer + { + public static FfiConverterTypeTokenOptions INSTANCE = new FfiConverterTypeTokenOptions(); + + public override TokenOptions Read(BigEndianStream stream) + { + return new TokenOptions( + Ttl: FfiConverterOptionalDuration.INSTANCE.Read(stream), + VideoGrants: FfiConverterOptionalTypeVideoGrants.INSTANCE.Read(stream), + SipGrants: FfiConverterOptionalTypeSIPGrants.INSTANCE.Read(stream), + Identity: FfiConverterOptionalString.INSTANCE.Read(stream), + Name: FfiConverterOptionalString.INSTANCE.Read(stream), + Metadata: FfiConverterOptionalString.INSTANCE.Read(stream), + Attributes: FfiConverterOptionalDictionaryStringString.INSTANCE.Read(stream), + Sha256: FfiConverterOptionalString.INSTANCE.Read(stream), + RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) + ); + } - static void uniffiCheckContractApiVersion() + public override int AllocationSize(TokenOptions value) { - var scaffolding_contract_version = _UniFFILib.ffi_livekit_uniffi_uniffi_contract_version(); - if (30 != scaffolding_contract_version) - { - throw new UniffiContractVersionException( - $"LiveKit.Uniffi: uniffi bindings expected version `30`, library returned `{scaffolding_contract_version}`" + return 0 + + FfiConverterOptionalDuration.INSTANCE.AllocationSize(value.Ttl) + + FfiConverterOptionalTypeVideoGrants.INSTANCE.AllocationSize(value.VideoGrants) + + FfiConverterOptionalTypeSIPGrants.INSTANCE.AllocationSize(value.SipGrants) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Identity) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Name) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Metadata) + + FfiConverterOptionalDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) + + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Sha256) + + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( + value.RoomConfiguration ); - } } - static void uniffiCheckApiChecksums() + public override void Write(TokenOptions value, BigEndianStream stream) + { + FfiConverterOptionalDuration.INSTANCE.Write(value.Ttl, stream); + FfiConverterOptionalTypeVideoGrants.INSTANCE.Write(value.VideoGrants, stream); + FfiConverterOptionalTypeSIPGrants.INSTANCE.Write(value.SipGrants, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Identity, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Name, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Metadata, stream); + FfiConverterOptionalDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + FfiConverterOptionalString.INSTANCE.Write(value.Sha256, stream); + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + } + } + + /// + /// Room permissions + /// + /// Maps to the JWT's `video` field. + /// + public record VideoGrants( + bool RoomCreate, + bool RoomList, + bool RoomRecord, + bool RoomAdmin, + bool RoomJoin, + string Room, + string DestinationRoom, + bool CanPublish, + bool CanSubscribe, + bool CanPublishData, + string[] CanPublishSources, + bool CanUpdateOwnMetadata, + bool IngressAdmin, + bool Hidden, + bool Recorder + ) { } + + class FfiConverterTypeVideoGrants : FfiConverterRustBuffer + { + public static FfiConverterTypeVideoGrants INSTANCE = new FfiConverterTypeVideoGrants(); + + public override VideoGrants Read(BigEndianStream stream) + { + return new VideoGrants( + RoomCreate: FfiConverterBoolean.INSTANCE.Read(stream), + RoomList: FfiConverterBoolean.INSTANCE.Read(stream), + RoomRecord: FfiConverterBoolean.INSTANCE.Read(stream), + RoomAdmin: FfiConverterBoolean.INSTANCE.Read(stream), + RoomJoin: FfiConverterBoolean.INSTANCE.Read(stream), + Room: FfiConverterString.INSTANCE.Read(stream), + DestinationRoom: FfiConverterString.INSTANCE.Read(stream), + CanPublish: FfiConverterBoolean.INSTANCE.Read(stream), + CanSubscribe: FfiConverterBoolean.INSTANCE.Read(stream), + CanPublishData: FfiConverterBoolean.INSTANCE.Read(stream), + CanPublishSources: FfiConverterSequenceString.INSTANCE.Read(stream), + CanUpdateOwnMetadata: FfiConverterBoolean.INSTANCE.Read(stream), + IngressAdmin: FfiConverterBoolean.INSTANCE.Read(stream), + Hidden: FfiConverterBoolean.INSTANCE.Read(stream), + Recorder: FfiConverterBoolean.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(VideoGrants value) + { + return 0 + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomCreate) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomList) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomRecord) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomAdmin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomJoin) + + FfiConverterString.INSTANCE.AllocationSize(value.Room) + + FfiConverterString.INSTANCE.AllocationSize(value.DestinationRoom) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublish) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanSubscribe) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublishData) + + FfiConverterSequenceString.INSTANCE.AllocationSize(value.CanPublishSources) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanUpdateOwnMetadata) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.IngressAdmin) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Hidden) + + FfiConverterBoolean.INSTANCE.AllocationSize(value.Recorder); + } + + public override void Write(VideoGrants value, BigEndianStream stream) + { + FfiConverterBoolean.INSTANCE.Write(value.RoomCreate, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomList, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomRecord, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomAdmin, stream); + FfiConverterBoolean.INSTANCE.Write(value.RoomJoin, stream); + FfiConverterString.INSTANCE.Write(value.Room, stream); + FfiConverterString.INSTANCE.Write(value.DestinationRoom, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanPublish, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanSubscribe, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanPublishData, stream); + FfiConverterSequenceString.INSTANCE.Write(value.CanPublishSources, stream); + FfiConverterBoolean.INSTANCE.Write(value.CanUpdateOwnMetadata, stream); + FfiConverterBoolean.INSTANCE.Write(value.IngressAdmin, stream); + FfiConverterBoolean.INSTANCE.Write(value.Hidden, stream); + FfiConverterBoolean.INSTANCE.Write(value.Recorder, stream); + } + } + + /// + /// An error that can occur during token generation or verification. + /// + public class AccessTokenException : UniffiException + { + AccessTokenException(string message) + : base(message) { } + + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class InvalidKeys : AccessTokenException + { + public InvalidKeys(string message) + : base(message) { } + } + + public class InvalidEnv : AccessTokenException + { + public InvalidEnv(string message) + : base(message) { } + } + + public class InvalidClaims : AccessTokenException + { + public InvalidClaims(string message) + : base(message) { } + } + + public class Encoding : AccessTokenException + { + public Encoding(string message) + : base(message) { } + } + } + + class FfiConverterTypeAccessTokenError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeAccessTokenError INSTANCE = + new FfiConverterTypeAccessTokenError(); + + public override AccessTokenException Read(BigEndianStream stream) { + var value = stream.ReadInt(); + switch (value) { - var checksum = - _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified(); - if (checksum != 50037) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_claims_from_unverified` checksum `50037`, library returned `{checksum}`" + case 1: + return new AccessTokenException.InvalidKeys( + FfiConverterString.INSTANCE.Read(stream) ); - } - } - { - var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_generate(); - if (checksum != 61939) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_generate` checksum `61939`, library returned `{checksum}`" + case 2: + return new AccessTokenException.InvalidEnv( + FfiConverterString.INSTANCE.Read(stream) ); - } - } - { - var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_token_verify(); - if (checksum != 22102) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_token_verify` checksum `22102`, library returned `{checksum}`" + case 3: + return new AccessTokenException.InvalidClaims( + FfiConverterString.INSTANCE.Read(stream) + ); + case 4: + return new AccessTokenException.Encoding(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Read()", + value + ) ); - } } + } + + public override int AllocationSize(AccessTokenException value) + { + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + } + + public override void Write(AccessTokenException value, BigEndianStream stream) + { + switch (value) { - var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_build_version(); - if (checksum != 21480) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_build_version` checksum `21480`, library returned `{checksum}`" + case AccessTokenException.InvalidKeys: + stream.WriteInt(1); + break; + case AccessTokenException.InvalidEnv: + stream.WriteInt(2); + break; + case AccessTokenException.InvalidClaims: + stream.WriteInt(3); + break; + case AccessTokenException.Encoding: + stream.WriteInt(4); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Write()", + value + ) ); - } } + } + } + + /// + /// A capability a remote participant's client advertises, mirroring [`common::ClientCapability`]. + /// + public enum ClientCapability : int + { + Unused, + PacketTrailer, + CompressionDeflateRaw, + } + + class FfiConverterTypeClientCapability : FfiConverterRustBuffer + { + public static FfiConverterTypeClientCapability INSTANCE = + new FfiConverterTypeClientCapability(); + + public override ClientCapability Read(BigEndianStream stream) + { + var value = stream.ReadInt(); + switch (value) { - var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap(); - if (checksum != 25458) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_bootstrap` checksum `25458`, library returned `{checksum}`" + case 1: + return ClientCapability.Unused; + case 2: + return ClientCapability.PacketTrailer; + case 3: + return ClientCapability.CompressionDeflateRaw; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeClientCapability.Read()", + value + ) ); - } } + } + + public override int AllocationSize(ClientCapability value) + { + return 4; + } + + public override void Write(ClientCapability value, BigEndianStream stream) + { + switch (value) { - var checksum = _UniFFILib.uniffi_livekit_uniffi_checksum_func_log_forward_receive(); - if (checksum != 58503) - { - throw new UniffiContractChecksumException( - $"LiveKit.Uniffi: uniffi bindings expected function `uniffi_livekit_uniffi_checksum_func_log_forward_receive` checksum `58503`, library returned `{checksum}`" + case ClientCapability.Unused: + stream.WriteInt(1); + break; + case ClientCapability.PacketTrailer: + stream.WriteInt(2); + break; + case ClientCapability.CompressionDeflateRaw: + stream.WriteInt(3); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeClientCapability.Write()", + value + ) ); - } } } } - // Public interface members begin here. + /// + /// A data stream operation failed. Structured mirror of [`ds_api::StreamError`] so foreign callers + /// can map each case to their own error type; variants carrying a message forward it as `message`. + /// + public class DataStreamException : UniffiException + { + DataStreamException() + : base() { } -#pragma warning disable 8625 + DataStreamException(String @Message) + : base(@Message) { } - class FfiConverterUInt32 : FfiConverter - { - public static FfiConverterUInt32 INSTANCE = new FfiConverterUInt32(); + // Each variant is a nested class - public override uint Lift(uint value) + public class AlreadyClosed : DataStreamException { - return value; + public AlreadyClosed() + : base() { } } - public override uint Read(BigEndianStream stream) + public class AbnormalEnd : DataStreamException { - return stream.ReadUInt(); + // Members + public string @reason; + + // Constructor + public AbnormalEnd(string @reason) + : base("@reason" + "=" + @reason) + { + this.@reason = @reason; + } } - public override uint Lower(uint value) + public class Utf8 : DataStreamException { - return value; + // Members + public string @reason; + + // Constructor + public Utf8(string @reason) + : base("@reason" + "=" + @reason) + { + this.@reason = @reason; + } } - public override int AllocationSize(uint value) + public class InvalidHeader : DataStreamException { - return 4; + public InvalidHeader() + : base() { } } - public override void Write(uint value, BigEndianStream stream) + public class MissedChunk : DataStreamException { - stream.WriteUInt(value); + public MissedChunk() + : base() { } } - } - - class FfiConverterInt32 : FfiConverter - { - public static FfiConverterInt32 INSTANCE = new FfiConverterInt32(); - public override int Lift(int value) + public class LengthExceeded : DataStreamException { - return value; + public LengthExceeded() + : base() { } } - public override int Read(BigEndianStream stream) + public class Incomplete : DataStreamException { - return stream.ReadInt(); + public Incomplete() + : base() { } } - public override int Lower(int value) + public class SendFailed : DataStreamException { - return value; + public SendFailed() + : base() { } } - public override int AllocationSize(int value) + public class Io : DataStreamException { - return 4; + // Members + public string @reason; + + // Constructor + public Io(string @reason) + : base("@reason" + "=" + @reason) + { + this.@reason = @reason; + } } - public override void Write(int value, BigEndianStream stream) + public class Internal : DataStreamException { - stream.WriteInt(value); + public Internal() + : base() { } } - } - - class FfiConverterUInt64 : FfiConverter - { - public static FfiConverterUInt64 INSTANCE = new FfiConverterUInt64(); - public override ulong Lift(ulong value) + public class EncryptionTypeMismatch : DataStreamException { - return value; + // Members + public EncryptionType @expected; + public EncryptionType @received; + + // Constructor + public EncryptionTypeMismatch(EncryptionType @expected, EncryptionType @received) + : base("@expected" + "=" + @expected + ", " + "@received" + "=" + @received) + { + this.@expected = @expected; + + this.@received = @received; + } } - public override ulong Read(BigEndianStream stream) + public class HeaderTooLarge : DataStreamException { - return stream.ReadULong(); + public HeaderTooLarge() + : base() { } } - public override ulong Lower(ulong value) + public class PayloadTooLarge : DataStreamException { - return value; + public PayloadTooLarge() + : base() { } } - public override int AllocationSize(ulong value) + public class Decompression : DataStreamException { - return 8; + public Decompression() + : base() { } } - public override void Write(ulong value, BigEndianStream stream) + public class InvalidFileName : DataStreamException { - stream.WriteULong(value); + public InvalidFileName() + : base() { } } } - class FfiConverterBoolean : FfiConverter + class FfiConverterTypeDataStreamError + : FfiConverterRustBuffer, + CallStatusErrorHandler { - public static FfiConverterBoolean INSTANCE = new FfiConverterBoolean(); + public static FfiConverterTypeDataStreamError INSTANCE = new FfiConverterTypeDataStreamError(); - public override bool Lift(sbyte value) + public override DataStreamException Read(BigEndianStream stream) { - return value != 0; + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new DataStreamException.AlreadyClosed(); + case 2: + return new DataStreamException.AbnormalEnd( + FfiConverterString.INSTANCE.Read(stream) + ); + case 3: + return new DataStreamException.Utf8(FfiConverterString.INSTANCE.Read(stream)); + case 4: + return new DataStreamException.InvalidHeader(); + case 5: + return new DataStreamException.MissedChunk(); + case 6: + return new DataStreamException.LengthExceeded(); + case 7: + return new DataStreamException.Incomplete(); + case 8: + return new DataStreamException.SendFailed(); + case 9: + return new DataStreamException.Io(FfiConverterString.INSTANCE.Read(stream)); + case 10: + return new DataStreamException.Internal(); + case 11: + return new DataStreamException.EncryptionTypeMismatch( + FfiConverterTypeEncryptionType.INSTANCE.Read(stream), + FfiConverterTypeEncryptionType.INSTANCE.Read(stream) + ); + case 12: + return new DataStreamException.HeaderTooLarge(); + case 13: + return new DataStreamException.PayloadTooLarge(); + case 14: + return new DataStreamException.Decompression(); + case 15: + return new DataStreamException.InvalidFileName(); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDataStreamError.Read()", + value + ) + ); + } } - public override bool Read(BigEndianStream stream) + public override int AllocationSize(DataStreamException value) { - return Lift(stream.ReadSByte()); - } + switch (value) + { + case DataStreamException.AlreadyClosed variant_value: + return 4; - public override sbyte Lower(bool value) - { - return value ? (sbyte)1 : (sbyte)0; - } + case DataStreamException.AbnormalEnd variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@reason); - public override int AllocationSize(bool value) - { - return (sbyte)1; + case DataStreamException.Utf8 variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@reason); + + case DataStreamException.InvalidHeader variant_value: + return 4; + + case DataStreamException.MissedChunk variant_value: + return 4; + + case DataStreamException.LengthExceeded variant_value: + return 4; + + case DataStreamException.Incomplete variant_value: + return 4; + + case DataStreamException.SendFailed variant_value: + return 4; + + case DataStreamException.Io variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@reason); + + case DataStreamException.Internal variant_value: + return 4; + + case DataStreamException.EncryptionTypeMismatch variant_value: + return 4 + + FfiConverterTypeEncryptionType.INSTANCE.AllocationSize( + variant_value.@expected + ) + + FfiConverterTypeEncryptionType.INSTANCE.AllocationSize( + variant_value.@received + ); + + case DataStreamException.HeaderTooLarge variant_value: + return 4; + + case DataStreamException.PayloadTooLarge variant_value: + return 4; + + case DataStreamException.Decompression variant_value: + return 4; + + case DataStreamException.InvalidFileName variant_value: + return 4; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDataStreamError.AllocationSize()", + value + ) + ); + } } - public override void Write(bool value, BigEndianStream stream) + public override void Write(DataStreamException value, BigEndianStream stream) { - stream.WriteSByte(Lower(value)); + switch (value) + { + case DataStreamException.AlreadyClosed variant_value: + stream.WriteInt(1); + break; + case DataStreamException.AbnormalEnd variant_value: + stream.WriteInt(2); + FfiConverterString.INSTANCE.Write(variant_value.@reason, stream); + break; + case DataStreamException.Utf8 variant_value: + stream.WriteInt(3); + FfiConverterString.INSTANCE.Write(variant_value.@reason, stream); + break; + case DataStreamException.InvalidHeader variant_value: + stream.WriteInt(4); + break; + case DataStreamException.MissedChunk variant_value: + stream.WriteInt(5); + break; + case DataStreamException.LengthExceeded variant_value: + stream.WriteInt(6); + break; + case DataStreamException.Incomplete variant_value: + stream.WriteInt(7); + break; + case DataStreamException.SendFailed variant_value: + stream.WriteInt(8); + break; + case DataStreamException.Io variant_value: + stream.WriteInt(9); + FfiConverterString.INSTANCE.Write(variant_value.@reason, stream); + break; + case DataStreamException.Internal variant_value: + stream.WriteInt(10); + break; + case DataStreamException.EncryptionTypeMismatch variant_value: + stream.WriteInt(11); + FfiConverterTypeEncryptionType.INSTANCE.Write(variant_value.@expected, stream); + FfiConverterTypeEncryptionType.INSTANCE.Write(variant_value.@received, stream); + break; + case DataStreamException.HeaderTooLarge variant_value: + stream.WriteInt(12); + break; + case DataStreamException.PayloadTooLarge variant_value: + stream.WriteInt(13); + break; + case DataStreamException.Decompression variant_value: + stream.WriteInt(14); + break; + case DataStreamException.InvalidFileName variant_value: + stream.WriteInt(15); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeDataStreamError.Write()", + value + ) + ); + } } } - class FfiConverterString : FfiConverter + /// + /// Encryption applied to a data stream, mirroring [`common::EncryptionType`]. + /// + public enum EncryptionType : int { - public static FfiConverterString INSTANCE = new FfiConverterString(); + None, + Gcm, + Custom, + } - // Note: we don't inherit from FfiConverterRustBuffer, because we use a - // special encoding when lowering/lifting. We can use `RustBuffer.len` to - // store our length and avoid writing it out to the buffer. - public override string Lift(RustBuffer value) + class FfiConverterTypeEncryptionType : FfiConverterRustBuffer + { + public static FfiConverterTypeEncryptionType INSTANCE = new FfiConverterTypeEncryptionType(); + + public override EncryptionType Read(BigEndianStream stream) { - try + var value = stream.ReadInt(); + switch (value) { - var bytes = value.AsStream().ReadBytes(Convert.ToInt32(value.len)); - return System.Text.Encoding.UTF8.GetString(bytes); + case 1: + return EncryptionType.None; + case 2: + return EncryptionType.Gcm; + case 3: + return EncryptionType.Custom; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeEncryptionType.Read()", + value + ) + ); } - finally + } + + public override int AllocationSize(EncryptionType value) + { + return 4; + } + + public override void Write(EncryptionType value, BigEndianStream stream) + { + switch (value) { - RustBuffer.Free(value); + case EncryptionType.None: + stream.WriteInt(1); + break; + case EncryptionType.Gcm: + stream.WriteInt(2); + break; + case EncryptionType.Custom: + stream.WriteInt(3); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeEncryptionType.Write()", + value + ) + ); } } + } + + /// + /// Signal response crossing the FFI boundary could not be processed. + /// + public class HandleSignalResponseException : UniffiException + { + HandleSignalResponseException(string message) + : base(message) { } - public override string Read(BigEndianStream stream) + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + + public class Decode : HandleSignalResponseException { - var length = stream.ReadInt(); - var bytes = stream.ReadBytes(length); - return System.Text.Encoding.UTF8.GetString(bytes); + public Decode(string message) + : base(message) { } } - public override RustBuffer Lower(string value) + public class EmptyMessage : HandleSignalResponseException { - var bytes = System.Text.Encoding.UTF8.GetBytes(value); - var rbuf = RustBuffer.Alloc(bytes.Length); - rbuf.AsWriteableStream().WriteBytes(bytes); - return rbuf; + public EmptyMessage(string message) + : base(message) { } } - // TODO(CS) - // We aren't sure exactly how many bytes our string will be once it's UTF-8 - // encoded. Allocate 3 bytes per unicode codepoint which will always be - // enough. - public override int AllocationSize(string value) + public class UnsupportedType : HandleSignalResponseException { - const int sizeForLength = 4; - var sizeForString = System.Text.Encoding.UTF8.GetByteCount(value); - return sizeForLength + sizeForString; + public UnsupportedType(string message) + : base(message) { } } - public override void Write(string value, BigEndianStream stream) + public class Internal : HandleSignalResponseException { - var bytes = System.Text.Encoding.UTF8.GetBytes(value); - stream.WriteInt(bytes.Length); - stream.WriteBytes(bytes); + public Internal(string message) + : base(message) { } } } - class FfiConverterDuration : FfiConverterRustBuffer + class FfiConverterTypeHandleSignalResponseError + : FfiConverterRustBuffer, + CallStatusErrorHandler { - public static FfiConverterDuration INSTANCE = new FfiConverterDuration(); - - // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs - private const uint NanosecondsPerTick = 100; + public static FfiConverterTypeHandleSignalResponseError INSTANCE = + new FfiConverterTypeHandleSignalResponseError(); - public override TimeSpan Read(BigEndianStream stream) + public override HandleSignalResponseException Read(BigEndianStream stream) { - var seconds = stream.ReadULong(); - var nanoseconds = stream.ReadUInt(); - var ticks = seconds * TimeSpan.TicksPerSecond; - ticks += nanoseconds / NanosecondsPerTick; - return new TimeSpan(Convert.ToInt64(ticks)); + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new HandleSignalResponseException.Decode( + FfiConverterString.INSTANCE.Read(stream) + ); + case 2: + return new HandleSignalResponseException.EmptyMessage( + FfiConverterString.INSTANCE.Read(stream) + ); + case 3: + return new HandleSignalResponseException.UnsupportedType( + FfiConverterString.INSTANCE.Read(stream) + ); + case 4: + return new HandleSignalResponseException.Internal( + FfiConverterString.INSTANCE.Read(stream) + ); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeHandleSignalResponseError.Read()", + value + ) + ); + } } - public override int AllocationSize(TimeSpan value) + public override int AllocationSize(HandleSignalResponseException value) { - // 8 bytes for seconds, 4 bytes for nanoseconds - return 12; + return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); } - public override void Write(TimeSpan value, BigEndianStream stream) + public override void Write(HandleSignalResponseException value, BigEndianStream stream) { - stream.WriteULong(Convert.ToUInt64(value.Ticks / TimeSpan.TicksPerSecond)); - stream.WriteUInt( - Convert.ToUInt32(value.Ticks % TimeSpan.TicksPerSecond * NanosecondsPerTick) - ); + switch (value) + { + case HandleSignalResponseException.Decode: + stream.WriteInt(1); + break; + case HandleSignalResponseException.EmptyMessage: + stream.WriteInt(2); + break; + case HandleSignalResponseException.UnsupportedType: + stream.WriteInt(3); + break; + case HandleSignalResponseException.Internal: + stream.WriteInt(4); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypeHandleSignalResponseError.Write()", + value + ) + ); + } } } - /// - /// API credentials for access token generation and verification. - /// - public record ApiCredentials(string Key, string Secret) { } + public enum LogForwardFilter : int + { + Off, + Error, + Warn, + Info, + Debug, + Trace, + } - class FfiConverterTypeApiCredentials : FfiConverterRustBuffer + class FfiConverterTypeLogForwardFilter : FfiConverterRustBuffer { - public static FfiConverterTypeApiCredentials INSTANCE = new FfiConverterTypeApiCredentials(); + public static FfiConverterTypeLogForwardFilter INSTANCE = + new FfiConverterTypeLogForwardFilter(); - public override ApiCredentials Read(BigEndianStream stream) + public override LogForwardFilter Read(BigEndianStream stream) { - return new ApiCredentials( - Key: FfiConverterString.INSTANCE.Read(stream), - Secret: FfiConverterString.INSTANCE.Read(stream) - ); + var value = stream.ReadInt(); + switch (value) + { + case 1: + return LogForwardFilter.Off; + case 2: + return LogForwardFilter.Error; + case 3: + return LogForwardFilter.Warn; + case 4: + return LogForwardFilter.Info; + case 5: + return LogForwardFilter.Debug; + case 6: + return LogForwardFilter.Trace; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Read()", + value + ) + ); + } } - public override int AllocationSize(ApiCredentials value) + public override int AllocationSize(LogForwardFilter value) { - return 0 - + FfiConverterString.INSTANCE.AllocationSize(value.Key) - + FfiConverterString.INSTANCE.AllocationSize(value.Secret); + return 4; } - public override void Write(ApiCredentials value, BigEndianStream stream) + public override void Write(LogForwardFilter value, BigEndianStream stream) { - FfiConverterString.INSTANCE.Write(value.Key, stream); - FfiConverterString.INSTANCE.Write(value.Secret, stream); + switch (value) + { + case LogForwardFilter.Off: + stream.WriteInt(1); + break; + case LogForwardFilter.Error: + stream.WriteInt(2); + break; + case LogForwardFilter.Warn: + stream.WriteInt(3); + break; + case LogForwardFilter.Info: + stream.WriteInt(4); + break; + case LogForwardFilter.Debug: + stream.WriteInt(5); + break; + case LogForwardFilter.Trace: + stream.WriteInt(6); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Write()", + value + ) + ); + } } } - /// - /// Claims decoded from a valid access token. - /// - public record Claims( - ulong Exp, - string Iss, - ulong Nbf, - string Sub, - string Name, - VideoGrants Video, - SipGrants Sip, - string Sha256, - string Metadata, - Dictionary Attributes, - RoomConfiguration? RoomConfiguration - ) { } + public enum LogForwardLevel : int + { + Error, + Warn, + Info, + Debug, + Trace, + } - class FfiConverterTypeClaims : FfiConverterRustBuffer + class FfiConverterTypeLogForwardLevel : FfiConverterRustBuffer { - public static FfiConverterTypeClaims INSTANCE = new FfiConverterTypeClaims(); + public static FfiConverterTypeLogForwardLevel INSTANCE = new FfiConverterTypeLogForwardLevel(); - public override Claims Read(BigEndianStream stream) + public override LogForwardLevel Read(BigEndianStream stream) { - return new Claims( - Exp: FfiConverterUInt64.INSTANCE.Read(stream), - Iss: FfiConverterString.INSTANCE.Read(stream), - Nbf: FfiConverterUInt64.INSTANCE.Read(stream), - Sub: FfiConverterString.INSTANCE.Read(stream), - Name: FfiConverterString.INSTANCE.Read(stream), - Video: FfiConverterTypeVideoGrants.INSTANCE.Read(stream), - Sip: FfiConverterTypeSIPGrants.INSTANCE.Read(stream), - Sha256: FfiConverterString.INSTANCE.Read(stream), - Metadata: FfiConverterString.INSTANCE.Read(stream), - Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream), - RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) - ); + var value = stream.ReadInt(); + switch (value) + { + case 1: + return LogForwardLevel.Error; + case 2: + return LogForwardLevel.Warn; + case 3: + return LogForwardLevel.Info; + case 4: + return LogForwardLevel.Debug; + case 5: + return LogForwardLevel.Trace; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Read()", + value + ) + ); + } } - public override int AllocationSize(Claims value) - { - return 0 - + FfiConverterUInt64.INSTANCE.AllocationSize(value.Exp) - + FfiConverterString.INSTANCE.AllocationSize(value.Iss) - + FfiConverterUInt64.INSTANCE.AllocationSize(value.Nbf) - + FfiConverterString.INSTANCE.AllocationSize(value.Sub) - + FfiConverterString.INSTANCE.AllocationSize(value.Name) - + FfiConverterTypeVideoGrants.INSTANCE.AllocationSize(value.Video) - + FfiConverterTypeSIPGrants.INSTANCE.AllocationSize(value.Sip) - + FfiConverterString.INSTANCE.AllocationSize(value.Sha256) - + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) - + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) - + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( - value.RoomConfiguration - ); + public override int AllocationSize(LogForwardLevel value) + { + return 4; } - public override void Write(Claims value, BigEndianStream stream) + public override void Write(LogForwardLevel value, BigEndianStream stream) { - FfiConverterUInt64.INSTANCE.Write(value.Exp, stream); - FfiConverterString.INSTANCE.Write(value.Iss, stream); - FfiConverterUInt64.INSTANCE.Write(value.Nbf, stream); - FfiConverterString.INSTANCE.Write(value.Sub, stream); - FfiConverterString.INSTANCE.Write(value.Name, stream); - FfiConverterTypeVideoGrants.INSTANCE.Write(value.Video, stream); - FfiConverterTypeSIPGrants.INSTANCE.Write(value.Sip, stream); - FfiConverterString.INSTANCE.Write(value.Sha256, stream); - FfiConverterString.INSTANCE.Write(value.Metadata, stream); - FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); - FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + switch (value) + { + case LogForwardLevel.Error: + stream.WriteInt(1); + break; + case LogForwardLevel.Warn: + stream.WriteInt(2); + break; + case LogForwardLevel.Info: + stream.WriteInt(3); + break; + case LogForwardLevel.Debug: + stream.WriteInt(4); + break; + case LogForwardLevel.Trace: + stream.WriteInt(5); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Write()", + value + ) + ); + } } } - public record LogForwardEntry( - LogForwardLevel Level, - string Target, - string? File, - uint? Line, - string Message - ) { } + /// + /// Operation type for text streams, mirroring [`ds_api::OperationType`]. + /// + public enum OperationType : int + { + Create, + Update, + Delete, + Reaction, + } - class FfiConverterTypeLogForwardEntry : FfiConverterRustBuffer + class FfiConverterTypeOperationType : FfiConverterRustBuffer { - public static FfiConverterTypeLogForwardEntry INSTANCE = new FfiConverterTypeLogForwardEntry(); + public static FfiConverterTypeOperationType INSTANCE = new FfiConverterTypeOperationType(); - public override LogForwardEntry Read(BigEndianStream stream) + public override OperationType Read(BigEndianStream stream) { - return new LogForwardEntry( - Level: FfiConverterTypeLogForwardLevel.INSTANCE.Read(stream), - Target: FfiConverterString.INSTANCE.Read(stream), - File: FfiConverterOptionalString.INSTANCE.Read(stream), - Line: FfiConverterOptionalUInt32.INSTANCE.Read(stream), - Message: FfiConverterString.INSTANCE.Read(stream) - ); + var value = stream.ReadInt(); + switch (value) + { + case 1: + return OperationType.Create; + case 2: + return OperationType.Update; + case 3: + return OperationType.Delete; + case 4: + return OperationType.Reaction; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeOperationType.Read()", + value + ) + ); + } } - public override int AllocationSize(LogForwardEntry value) + public override int AllocationSize(OperationType value) { - return 0 - + FfiConverterTypeLogForwardLevel.INSTANCE.AllocationSize(value.Level) - + FfiConverterString.INSTANCE.AllocationSize(value.Target) - + FfiConverterOptionalString.INSTANCE.AllocationSize(value.File) - + FfiConverterOptionalUInt32.INSTANCE.AllocationSize(value.Line) - + FfiConverterString.INSTANCE.AllocationSize(value.Message); + return 4; } - public override void Write(LogForwardEntry value, BigEndianStream stream) + public override void Write(OperationType value, BigEndianStream stream) { - FfiConverterTypeLogForwardLevel.INSTANCE.Write(value.Level, stream); - FfiConverterString.INSTANCE.Write(value.Target, stream); - FfiConverterOptionalString.INSTANCE.Write(value.File, stream); - FfiConverterOptionalUInt32.INSTANCE.Write(value.Line, stream); - FfiConverterString.INSTANCE.Write(value.Message, stream); + switch (value) + { + case OperationType.Create: + stream.WriteInt(1); + break; + case OperationType.Update: + stream.WriteInt(2); + break; + case OperationType.Delete: + stream.WriteInt(3); + break; + case OperationType.Reaction: + stream.WriteInt(4); + break; + default: + throw new InternalException( + String.Format( + "invalid enum value '{0}' in FfiConverterTypeOperationType.Write()", + value + ) + ); + } } } /// - /// Agent dispatch configuration + /// A foreign transport failed to deliver outbound packets; thrown by hosts from + /// [`OutgoingDataStreamManagerDelegate::on_packets_available`](super::outgoing::OutgoingDataStreamManagerDelegate::on_packets_available). /// - /// Defines which agents should be dispatched to a room. + /// Morally `struct PacketDeliveryError(String)`, but uniffi error types must be enums, so the + /// string travels as the single variant's `reason` (free-form host context: logged, not parsed). /// - public record RoomAgentDispatch( - string AgentName, - string Metadata, - int RestartPolicy, - string Deployment, - Dictionary Attributes - ) { } + public class PacketDeliveryException : UniffiException + { + PacketDeliveryException() + : base() { } - class FfiConverterTypeRoomAgentDispatch : FfiConverterRustBuffer + PacketDeliveryException(String @Message) + : base(@Message) { } + + // Each variant is a nested class + + public class Failed : PacketDeliveryException + { + // Members + public string @reason; + + // Constructor + public Failed(string @reason) + : base("@reason" + "=" + @reason) + { + this.@reason = @reason; + } + } + } + + class FfiConverterTypePacketDeliveryError + : FfiConverterRustBuffer, + CallStatusErrorHandler { - public static FfiConverterTypeRoomAgentDispatch INSTANCE = - new FfiConverterTypeRoomAgentDispatch(); + public static FfiConverterTypePacketDeliveryError INSTANCE = + new FfiConverterTypePacketDeliveryError(); - public override RoomAgentDispatch Read(BigEndianStream stream) + public override PacketDeliveryException Read(BigEndianStream stream) { - return new RoomAgentDispatch( - AgentName: FfiConverterString.INSTANCE.Read(stream), - Metadata: FfiConverterString.INSTANCE.Read(stream), - RestartPolicy: FfiConverterInt32.INSTANCE.Read(stream), - Deployment: FfiConverterString.INSTANCE.Read(stream), - Attributes: FfiConverterDictionaryStringString.INSTANCE.Read(stream) - ); + var value = stream.ReadInt(); + switch (value) + { + case 1: + return new PacketDeliveryException.Failed(FfiConverterString.INSTANCE.Read(stream)); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePacketDeliveryError.Read()", + value + ) + ); + } } - public override int AllocationSize(RoomAgentDispatch value) + public override int AllocationSize(PacketDeliveryException value) { - return 0 - + FfiConverterString.INSTANCE.AllocationSize(value.AgentName) - + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) - + FfiConverterInt32.INSTANCE.AllocationSize(value.RestartPolicy) - + FfiConverterString.INSTANCE.AllocationSize(value.Deployment) - + FfiConverterDictionaryStringString.INSTANCE.AllocationSize(value.Attributes); + switch (value) + { + case PacketDeliveryException.Failed variant_value: + return 4 + FfiConverterString.INSTANCE.AllocationSize(variant_value.@reason); + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePacketDeliveryError.AllocationSize()", + value + ) + ); + } } - public override void Write(RoomAgentDispatch value, BigEndianStream stream) + public override void Write(PacketDeliveryException value, BigEndianStream stream) { - FfiConverterString.INSTANCE.Write(value.AgentName, stream); - FfiConverterString.INSTANCE.Write(value.Metadata, stream); - FfiConverterInt32.INSTANCE.Write(value.RestartPolicy, stream); - FfiConverterString.INSTANCE.Write(value.Deployment, stream); - FfiConverterDictionaryStringString.INSTANCE.Write(value.Attributes, stream); + switch (value) + { + case PacketDeliveryException.Failed variant_value: + stream.WriteInt(1); + FfiConverterString.INSTANCE.Write(variant_value.@reason, stream); + break; + default: + throw new InternalException( + String.Format( + "invalid error value '{0}' in FfiConverterTypePacketDeliveryError.Write()", + value + ) + ); + } } } - /// - /// Room configuration - /// - /// Configuration options for a room. - /// - public record RoomConfiguration( - string Name, - uint EmptyTimeout, - uint DepartureTimeout, - uint MaxParticipants, - string Metadata, - uint MinPlayoutDelay, - uint MaxPlayoutDelay, - bool SyncStreams, - RoomAgentDispatch[] Agents - ) { } - - class FfiConverterTypeRoomConfiguration : FfiConverterRustBuffer + class FfiConverterOptionalUInt32 : FfiConverterRustBuffer { - public static FfiConverterTypeRoomConfiguration INSTANCE = - new FfiConverterTypeRoomConfiguration(); + public static FfiConverterOptionalUInt32 INSTANCE = new FfiConverterOptionalUInt32(); - public override RoomConfiguration Read(BigEndianStream stream) + public override uint? Read(BigEndianStream stream) { - return new RoomConfiguration( - Name: FfiConverterString.INSTANCE.Read(stream), - EmptyTimeout: FfiConverterUInt32.INSTANCE.Read(stream), - DepartureTimeout: FfiConverterUInt32.INSTANCE.Read(stream), - MaxParticipants: FfiConverterUInt32.INSTANCE.Read(stream), - Metadata: FfiConverterString.INSTANCE.Read(stream), - MinPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), - MaxPlayoutDelay: FfiConverterUInt32.INSTANCE.Read(stream), - SyncStreams: FfiConverterBoolean.INSTANCE.Read(stream), - Agents: FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Read(stream) - ); + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterUInt32.INSTANCE.Read(stream); } - public override int AllocationSize(RoomConfiguration value) + public override int AllocationSize(uint? value) { - return 0 - + FfiConverterString.INSTANCE.AllocationSize(value.Name) - + FfiConverterUInt32.INSTANCE.AllocationSize(value.EmptyTimeout) - + FfiConverterUInt32.INSTANCE.AllocationSize(value.DepartureTimeout) - + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxParticipants) - + FfiConverterString.INSTANCE.AllocationSize(value.Metadata) - + FfiConverterUInt32.INSTANCE.AllocationSize(value.MinPlayoutDelay) - + FfiConverterUInt32.INSTANCE.AllocationSize(value.MaxPlayoutDelay) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.SyncStreams) - + FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.AllocationSize(value.Agents); + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterUInt32.INSTANCE.AllocationSize((uint)value); + } } - public override void Write(RoomConfiguration value, BigEndianStream stream) + public override void Write(uint? value, BigEndianStream stream) { - FfiConverterString.INSTANCE.Write(value.Name, stream); - FfiConverterUInt32.INSTANCE.Write(value.EmptyTimeout, stream); - FfiConverterUInt32.INSTANCE.Write(value.DepartureTimeout, stream); - FfiConverterUInt32.INSTANCE.Write(value.MaxParticipants, stream); - FfiConverterString.INSTANCE.Write(value.Metadata, stream); - FfiConverterUInt32.INSTANCE.Write(value.MinPlayoutDelay, stream); - FfiConverterUInt32.INSTANCE.Write(value.MaxPlayoutDelay, stream); - FfiConverterBoolean.INSTANCE.Write(value.SyncStreams, stream); - FfiConverterSequenceTypeRoomAgentDispatch.INSTANCE.Write(value.Agents, stream); + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterUInt32.INSTANCE.Write((uint)value, stream); + } } } - /// - /// SIP grants - /// - /// Maps to the JWT's `sip` field. - /// - public record SipGrants(bool Admin, bool Call) { } - - class FfiConverterTypeSIPGrants : FfiConverterRustBuffer + class FfiConverterOptionalInt32 : FfiConverterRustBuffer { - public static FfiConverterTypeSIPGrants INSTANCE = new FfiConverterTypeSIPGrants(); + public static FfiConverterOptionalInt32 INSTANCE = new FfiConverterOptionalInt32(); - public override SipGrants Read(BigEndianStream stream) + public override int? Read(BigEndianStream stream) { - return new SipGrants( - Admin: FfiConverterBoolean.INSTANCE.Read(stream), - Call: FfiConverterBoolean.INSTANCE.Read(stream) - ); + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterInt32.INSTANCE.Read(stream); } - public override int AllocationSize(SipGrants value) + public override int AllocationSize(int? value) { - return 0 - + FfiConverterBoolean.INSTANCE.AllocationSize(value.Admin) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.Call); + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterInt32.INSTANCE.AllocationSize((int)value); + } } - public override void Write(SipGrants value, BigEndianStream stream) + public override void Write(int? value, BigEndianStream stream) { - FfiConverterBoolean.INSTANCE.Write(value.Admin, stream); - FfiConverterBoolean.INSTANCE.Write(value.Call, stream); + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterInt32.INSTANCE.Write((int)value, stream); + } } } - /// - /// Options used for generating an access token. - /// - /// Any fields left empty will use the token generator's defaults. - /// - public record TokenOptions( - TimeSpan? Ttl = null, - VideoGrants? VideoGrants = null, - SipGrants? SipGrants = null, - string? Identity = null, - string? Name = null, - string? Metadata = null, - Dictionary? Attributes = null, - string? Sha256 = null, - RoomConfiguration? RoomConfiguration = null - ) { } - - class FfiConverterTypeTokenOptions : FfiConverterRustBuffer + class FfiConverterOptionalUInt64 : FfiConverterRustBuffer { - public static FfiConverterTypeTokenOptions INSTANCE = new FfiConverterTypeTokenOptions(); + public static FfiConverterOptionalUInt64 INSTANCE = new FfiConverterOptionalUInt64(); - public override TokenOptions Read(BigEndianStream stream) + public override ulong? Read(BigEndianStream stream) { - return new TokenOptions( - Ttl: FfiConverterOptionalDuration.INSTANCE.Read(stream), - VideoGrants: FfiConverterOptionalTypeVideoGrants.INSTANCE.Read(stream), - SipGrants: FfiConverterOptionalTypeSIPGrants.INSTANCE.Read(stream), - Identity: FfiConverterOptionalString.INSTANCE.Read(stream), - Name: FfiConverterOptionalString.INSTANCE.Read(stream), - Metadata: FfiConverterOptionalString.INSTANCE.Read(stream), - Attributes: FfiConverterOptionalDictionaryStringString.INSTANCE.Read(stream), - Sha256: FfiConverterOptionalString.INSTANCE.Read(stream), - RoomConfiguration: FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Read(stream) - ); + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterUInt64.INSTANCE.Read(stream); } - public override int AllocationSize(TokenOptions value) + public override int AllocationSize(ulong? value) { - return 0 - + FfiConverterOptionalDuration.INSTANCE.AllocationSize(value.Ttl) - + FfiConverterOptionalTypeVideoGrants.INSTANCE.AllocationSize(value.VideoGrants) - + FfiConverterOptionalTypeSIPGrants.INSTANCE.AllocationSize(value.SipGrants) - + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Identity) - + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Name) - + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Metadata) - + FfiConverterOptionalDictionaryStringString.INSTANCE.AllocationSize(value.Attributes) - + FfiConverterOptionalString.INSTANCE.AllocationSize(value.Sha256) - + FfiConverterOptionalTypeRoomConfiguration.INSTANCE.AllocationSize( - value.RoomConfiguration - ); + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterUInt64.INSTANCE.AllocationSize((ulong)value); + } } - public override void Write(TokenOptions value, BigEndianStream stream) + public override void Write(ulong? value, BigEndianStream stream) { - FfiConverterOptionalDuration.INSTANCE.Write(value.Ttl, stream); - FfiConverterOptionalTypeVideoGrants.INSTANCE.Write(value.VideoGrants, stream); - FfiConverterOptionalTypeSIPGrants.INSTANCE.Write(value.SipGrants, stream); - FfiConverterOptionalString.INSTANCE.Write(value.Identity, stream); - FfiConverterOptionalString.INSTANCE.Write(value.Name, stream); - FfiConverterOptionalString.INSTANCE.Write(value.Metadata, stream); - FfiConverterOptionalDictionaryStringString.INSTANCE.Write(value.Attributes, stream); - FfiConverterOptionalString.INSTANCE.Write(value.Sha256, stream); - FfiConverterOptionalTypeRoomConfiguration.INSTANCE.Write(value.RoomConfiguration, stream); + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterUInt64.INSTANCE.Write((ulong)value, stream); + } } } - /// - /// Room permissions - /// - /// Maps to the JWT's `video` field. - /// - public record VideoGrants( - bool RoomCreate, - bool RoomList, - bool RoomRecord, - bool RoomAdmin, - bool RoomJoin, - string Room, - string DestinationRoom, - bool CanPublish, - bool CanSubscribe, - bool CanPublishData, - string[] CanPublishSources, - bool CanUpdateOwnMetadata, - bool IngressAdmin, - bool Hidden, - bool Recorder - ) { } - - class FfiConverterTypeVideoGrants : FfiConverterRustBuffer + class FfiConverterOptionalBoolean : FfiConverterRustBuffer { - public static FfiConverterTypeVideoGrants INSTANCE = new FfiConverterTypeVideoGrants(); + public static FfiConverterOptionalBoolean INSTANCE = new FfiConverterOptionalBoolean(); - public override VideoGrants Read(BigEndianStream stream) + public override bool? Read(BigEndianStream stream) { - return new VideoGrants( - RoomCreate: FfiConverterBoolean.INSTANCE.Read(stream), - RoomList: FfiConverterBoolean.INSTANCE.Read(stream), - RoomRecord: FfiConverterBoolean.INSTANCE.Read(stream), - RoomAdmin: FfiConverterBoolean.INSTANCE.Read(stream), - RoomJoin: FfiConverterBoolean.INSTANCE.Read(stream), - Room: FfiConverterString.INSTANCE.Read(stream), - DestinationRoom: FfiConverterString.INSTANCE.Read(stream), - CanPublish: FfiConverterBoolean.INSTANCE.Read(stream), - CanSubscribe: FfiConverterBoolean.INSTANCE.Read(stream), - CanPublishData: FfiConverterBoolean.INSTANCE.Read(stream), - CanPublishSources: FfiConverterSequenceString.INSTANCE.Read(stream), - CanUpdateOwnMetadata: FfiConverterBoolean.INSTANCE.Read(stream), - IngressAdmin: FfiConverterBoolean.INSTANCE.Read(stream), - Hidden: FfiConverterBoolean.INSTANCE.Read(stream), - Recorder: FfiConverterBoolean.INSTANCE.Read(stream) - ); + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterBoolean.INSTANCE.Read(stream); } - public override int AllocationSize(VideoGrants value) + public override int AllocationSize(bool? value) { - return 0 - + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomCreate) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomList) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomRecord) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomAdmin) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.RoomJoin) - + FfiConverterString.INSTANCE.AllocationSize(value.Room) - + FfiConverterString.INSTANCE.AllocationSize(value.DestinationRoom) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublish) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanSubscribe) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanPublishData) - + FfiConverterSequenceString.INSTANCE.AllocationSize(value.CanPublishSources) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.CanUpdateOwnMetadata) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.IngressAdmin) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.Hidden) - + FfiConverterBoolean.INSTANCE.AllocationSize(value.Recorder); + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterBoolean.INSTANCE.AllocationSize((bool)value); + } } - public override void Write(VideoGrants value, BigEndianStream stream) + public override void Write(bool? value, BigEndianStream stream) { - FfiConverterBoolean.INSTANCE.Write(value.RoomCreate, stream); - FfiConverterBoolean.INSTANCE.Write(value.RoomList, stream); - FfiConverterBoolean.INSTANCE.Write(value.RoomRecord, stream); - FfiConverterBoolean.INSTANCE.Write(value.RoomAdmin, stream); - FfiConverterBoolean.INSTANCE.Write(value.RoomJoin, stream); - FfiConverterString.INSTANCE.Write(value.Room, stream); - FfiConverterString.INSTANCE.Write(value.DestinationRoom, stream); - FfiConverterBoolean.INSTANCE.Write(value.CanPublish, stream); - FfiConverterBoolean.INSTANCE.Write(value.CanSubscribe, stream); - FfiConverterBoolean.INSTANCE.Write(value.CanPublishData, stream); - FfiConverterSequenceString.INSTANCE.Write(value.CanPublishSources, stream); - FfiConverterBoolean.INSTANCE.Write(value.CanUpdateOwnMetadata, stream); - FfiConverterBoolean.INSTANCE.Write(value.IngressAdmin, stream); - FfiConverterBoolean.INSTANCE.Write(value.Hidden, stream); - FfiConverterBoolean.INSTANCE.Write(value.Recorder, stream); + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterBoolean.INSTANCE.Write((bool)value, stream); + } } } - /// - /// An error that can occur during token generation or verification. - /// - public class AccessTokenException : UniffiException + class FfiConverterOptionalString : FfiConverterRustBuffer { - AccessTokenException(string message) - : base(message) { } + public static FfiConverterOptionalString INSTANCE = new FfiConverterOptionalString(); - // Each variant is a nested class - // Flat enums carries a string error message, so no special implementation is necessary. + public override string? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterString.INSTANCE.Read(stream); + } - public class InvalidKeys : AccessTokenException + public override int AllocationSize(string? value) { - public InvalidKeys(string message) - : base(message) { } + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterString.INSTANCE.AllocationSize((string)value); + } + } + + public override void Write(string? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterString.INSTANCE.Write((string)value, stream); + } } + } - public class InvalidEnv : AccessTokenException + class FfiConverterOptionalDuration : FfiConverterRustBuffer + { + public static FfiConverterOptionalDuration INSTANCE = new FfiConverterOptionalDuration(); + + public override TimeSpan? Read(BigEndianStream stream) { - public InvalidEnv(string message) - : base(message) { } + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterDuration.INSTANCE.Read(stream); } - public class InvalidClaims : AccessTokenException + public override int AllocationSize(TimeSpan? value) { - public InvalidClaims(string message) - : base(message) { } + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterDuration.INSTANCE.AllocationSize((TimeSpan)value); + } } - public class Encoding : AccessTokenException + public override void Write(TimeSpan? value, BigEndianStream stream) { - public Encoding(string message) - : base(message) { } + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterDuration.INSTANCE.Write((TimeSpan)value, stream); + } } } - class FfiConverterTypeAccessTokenError - : FfiConverterRustBuffer, - CallStatusErrorHandler + class FfiConverterOptionalTypeDecryptionProvider : FfiConverterRustBuffer { - public static FfiConverterTypeAccessTokenError INSTANCE = - new FfiConverterTypeAccessTokenError(); + public static FfiConverterOptionalTypeDecryptionProvider INSTANCE = + new FfiConverterOptionalTypeDecryptionProvider(); - public override AccessTokenException Read(BigEndianStream stream) + public override DecryptionProvider? Read(BigEndianStream stream) { - var value = stream.ReadInt(); - switch (value) + if (stream.ReadByte() == 0) { - case 1: - return new AccessTokenException.InvalidKeys( - FfiConverterString.INSTANCE.Read(stream) - ); - case 2: - return new AccessTokenException.InvalidEnv( - FfiConverterString.INSTANCE.Read(stream) - ); - case 3: - return new AccessTokenException.InvalidClaims( - FfiConverterString.INSTANCE.Read(stream) - ); - case 4: - return new AccessTokenException.Encoding(FfiConverterString.INSTANCE.Read(stream)); - default: - throw new InternalException( - String.Format( - "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Read()", - value - ) - ); + return null; } + return FfiConverterTypeDecryptionProvider.INSTANCE.Read(stream); } - public override int AllocationSize(AccessTokenException value) + public override int AllocationSize(DecryptionProvider? value) { - return 4 + FfiConverterString.INSTANCE.AllocationSize(value.Message); + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeDecryptionProvider.INSTANCE.AllocationSize( + (DecryptionProvider)value + ); + } } - public override void Write(AccessTokenException value, BigEndianStream stream) + public override void Write(DecryptionProvider? value, BigEndianStream stream) { - switch (value) + if (value == null) { - case AccessTokenException.InvalidKeys: - stream.WriteInt(1); - break; - case AccessTokenException.InvalidEnv: - stream.WriteInt(2); - break; - case AccessTokenException.InvalidClaims: - stream.WriteInt(3); - break; - case AccessTokenException.Encoding: - stream.WriteInt(4); - break; - default: - throw new InternalException( - String.Format( - "invalid error value '{0}' in FfiConverterTypeAccessTokenError.Write()", - value - ) - ); + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeDecryptionProvider.INSTANCE.Write((DecryptionProvider)value, stream); } } } - public enum LogForwardFilter : int + class FfiConverterOptionalTypeEncryptionProvider : FfiConverterRustBuffer { - Off, - Error, - Warn, - Info, - Debug, - Trace, + public static FfiConverterOptionalTypeEncryptionProvider INSTANCE = + new FfiConverterOptionalTypeEncryptionProvider(); + + public override EncryptionProvider? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeEncryptionProvider.INSTANCE.Read(stream); + } + + public override int AllocationSize(EncryptionProvider? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeEncryptionProvider.INSTANCE.AllocationSize( + (EncryptionProvider)value + ); + } + } + + public override void Write(EncryptionProvider? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeEncryptionProvider.INSTANCE.Write((EncryptionProvider)value, stream); + } + } } - class FfiConverterTypeLogForwardFilter : FfiConverterRustBuffer + class FfiConverterOptionalTypeByteStreamReader : FfiConverterRustBuffer { - public static FfiConverterTypeLogForwardFilter INSTANCE = - new FfiConverterTypeLogForwardFilter(); + public static FfiConverterOptionalTypeByteStreamReader INSTANCE = + new FfiConverterOptionalTypeByteStreamReader(); - public override LogForwardFilter Read(BigEndianStream stream) + public override ByteStreamReader? Read(BigEndianStream stream) { - var value = stream.ReadInt(); - switch (value) + if (stream.ReadByte() == 0) { - case 1: - return LogForwardFilter.Off; - case 2: - return LogForwardFilter.Error; - case 3: - return LogForwardFilter.Warn; - case 4: - return LogForwardFilter.Info; - case 5: - return LogForwardFilter.Debug; - case 6: - return LogForwardFilter.Trace; - default: - throw new InternalException( - String.Format( - "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Read()", - value - ) - ); + return null; } + return FfiConverterTypeByteStreamReader.INSTANCE.Read(stream); } - public override int AllocationSize(LogForwardFilter value) + public override int AllocationSize(ByteStreamReader? value) { - return 4; + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeByteStreamReader.INSTANCE.AllocationSize((ByteStreamReader)value); + } } - public override void Write(LogForwardFilter value, BigEndianStream stream) + public override void Write(ByteStreamReader? value, BigEndianStream stream) { - switch (value) + if (value == null) { - case LogForwardFilter.Off: - stream.WriteInt(1); - break; - case LogForwardFilter.Error: - stream.WriteInt(2); - break; - case LogForwardFilter.Warn: - stream.WriteInt(3); - break; - case LogForwardFilter.Info: - stream.WriteInt(4); - break; - case LogForwardFilter.Debug: - stream.WriteInt(5); - break; - case LogForwardFilter.Trace: - stream.WriteInt(6); - break; - default: - throw new InternalException( - String.Format( - "invalid enum value '{0}' in FfiConverterTypeLogForwardFilter.Write()", - value - ) - ); + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeByteStreamReader.INSTANCE.Write((ByteStreamReader)value, stream); } } } - public enum LogForwardLevel : int + class FfiConverterOptionalTypeTextStreamReader : FfiConverterRustBuffer { - Error, - Warn, - Info, - Debug, - Trace, - } + public static FfiConverterOptionalTypeTextStreamReader INSTANCE = + new FfiConverterOptionalTypeTextStreamReader(); - class FfiConverterTypeLogForwardLevel : FfiConverterRustBuffer - { - public static FfiConverterTypeLogForwardLevel INSTANCE = new FfiConverterTypeLogForwardLevel(); + public override TextStreamReader? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeTextStreamReader.INSTANCE.Read(stream); + } - public override LogForwardLevel Read(BigEndianStream stream) + public override int AllocationSize(TextStreamReader? value) { - var value = stream.ReadInt(); - switch (value) + if (value == null) { - case 1: - return LogForwardLevel.Error; - case 2: - return LogForwardLevel.Warn; - case 3: - return LogForwardLevel.Info; - case 4: - return LogForwardLevel.Debug; - case 5: - return LogForwardLevel.Trace; - default: - throw new InternalException( - String.Format( - "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Read()", - value - ) - ); + return 1; + } + else + { + return 1 + + FfiConverterTypeTextStreamReader.INSTANCE.AllocationSize((TextStreamReader)value); } } - public override int AllocationSize(LogForwardLevel value) - { - return 4; - } - - public override void Write(LogForwardLevel value, BigEndianStream stream) + public override void Write(TextStreamReader? value, BigEndianStream stream) { - switch (value) + if (value == null) { - case LogForwardLevel.Error: - stream.WriteInt(1); - break; - case LogForwardLevel.Warn: - stream.WriteInt(2); - break; - case LogForwardLevel.Info: - stream.WriteInt(3); - break; - case LogForwardLevel.Debug: - stream.WriteInt(4); - break; - case LogForwardLevel.Trace: - stream.WriteInt(5); - break; - default: - throw new InternalException( - String.Format( - "invalid enum value '{0}' in FfiConverterTypeLogForwardLevel.Write()", - value - ) - ); + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeTextStreamReader.INSTANCE.Write((TextStreamReader)value, stream); } } } - class FfiConverterOptionalUInt32 : FfiConverterRustBuffer + class FfiConverterOptionalTypeApiCredentials : FfiConverterRustBuffer { - public static FfiConverterOptionalUInt32 INSTANCE = new FfiConverterOptionalUInt32(); + public static FfiConverterOptionalTypeApiCredentials INSTANCE = + new FfiConverterOptionalTypeApiCredentials(); - public override uint? Read(BigEndianStream stream) + public override ApiCredentials? Read(BigEndianStream stream) { if (stream.ReadByte() == 0) { return null; } - return FfiConverterUInt32.INSTANCE.Read(stream); + return FfiConverterTypeApiCredentials.INSTANCE.Read(stream); } - public override int AllocationSize(uint? value) + public override int AllocationSize(ApiCredentials? value) { if (value == null) { @@ -2617,11 +13591,12 @@ public override int AllocationSize(uint? value) } else { - return 1 + FfiConverterUInt32.INSTANCE.AllocationSize((uint)value); + return 1 + + FfiConverterTypeApiCredentials.INSTANCE.AllocationSize((ApiCredentials)value); } } - public override void Write(uint? value, BigEndianStream stream) + public override void Write(ApiCredentials? value, BigEndianStream stream) { if (value == null) { @@ -2630,25 +13605,26 @@ public override void Write(uint? value, BigEndianStream stream) else { stream.WriteByte(1); - FfiConverterUInt32.INSTANCE.Write((uint)value, stream); + FfiConverterTypeApiCredentials.INSTANCE.Write((ApiCredentials)value, stream); } } } - class FfiConverterOptionalString : FfiConverterRustBuffer + class FfiConverterOptionalTypeClosedStream : FfiConverterRustBuffer { - public static FfiConverterOptionalString INSTANCE = new FfiConverterOptionalString(); + public static FfiConverterOptionalTypeClosedStream INSTANCE = + new FfiConverterOptionalTypeClosedStream(); - public override string? Read(BigEndianStream stream) + public override ClosedStream? Read(BigEndianStream stream) { if (stream.ReadByte() == 0) { return null; } - return FfiConverterString.INSTANCE.Read(stream); + return FfiConverterTypeClosedStream.INSTANCE.Read(stream); } - public override int AllocationSize(string? value) + public override int AllocationSize(ClosedStream? value) { if (value == null) { @@ -2656,11 +13632,11 @@ public override int AllocationSize(string? value) } else { - return 1 + FfiConverterString.INSTANCE.AllocationSize((string)value); + return 1 + FfiConverterTypeClosedStream.INSTANCE.AllocationSize((ClosedStream)value); } } - public override void Write(string? value, BigEndianStream stream) + public override void Write(ClosedStream? value, BigEndianStream stream) { if (value == null) { @@ -2669,25 +13645,26 @@ public override void Write(string? value, BigEndianStream stream) else { stream.WriteByte(1); - FfiConverterString.INSTANCE.Write((string)value, stream); + FfiConverterTypeClosedStream.INSTANCE.Write((ClosedStream)value, stream); } } } - class FfiConverterOptionalDuration : FfiConverterRustBuffer + class FfiConverterOptionalTypeDataTrackFrame : FfiConverterRustBuffer { - public static FfiConverterOptionalDuration INSTANCE = new FfiConverterOptionalDuration(); + public static FfiConverterOptionalTypeDataTrackFrame INSTANCE = + new FfiConverterOptionalTypeDataTrackFrame(); - public override TimeSpan? Read(BigEndianStream stream) + public override DataTrackFrame? Read(BigEndianStream stream) { if (stream.ReadByte() == 0) { return null; } - return FfiConverterDuration.INSTANCE.Read(stream); + return FfiConverterTypeDataTrackFrame.INSTANCE.Read(stream); } - public override int AllocationSize(TimeSpan? value) + public override int AllocationSize(DataTrackFrame? value) { if (value == null) { @@ -2695,11 +13672,12 @@ public override int AllocationSize(TimeSpan? value) } else { - return 1 + FfiConverterDuration.INSTANCE.AllocationSize((TimeSpan)value); + return 1 + + FfiConverterTypeDataTrackFrame.INSTANCE.AllocationSize((DataTrackFrame)value); } } - public override void Write(TimeSpan? value, BigEndianStream stream) + public override void Write(DataTrackFrame? value, BigEndianStream stream) { if (value == null) { @@ -2708,26 +13686,26 @@ public override void Write(TimeSpan? value, BigEndianStream stream) else { stream.WriteByte(1); - FfiConverterDuration.INSTANCE.Write((TimeSpan)value, stream); + FfiConverterTypeDataTrackFrame.INSTANCE.Write((DataTrackFrame)value, stream); } } } - class FfiConverterOptionalTypeApiCredentials : FfiConverterRustBuffer + class FfiConverterOptionalTypeDataTrackSchemaId : FfiConverterRustBuffer { - public static FfiConverterOptionalTypeApiCredentials INSTANCE = - new FfiConverterOptionalTypeApiCredentials(); + public static FfiConverterOptionalTypeDataTrackSchemaId INSTANCE = + new FfiConverterOptionalTypeDataTrackSchemaId(); - public override ApiCredentials? Read(BigEndianStream stream) + public override DataTrackSchemaId? Read(BigEndianStream stream) { if (stream.ReadByte() == 0) { return null; } - return FfiConverterTypeApiCredentials.INSTANCE.Read(stream); + return FfiConverterTypeDataTrackSchemaId.INSTANCE.Read(stream); } - public override int AllocationSize(ApiCredentials? value) + public override int AllocationSize(DataTrackSchemaId? value) { if (value == null) { @@ -2736,11 +13714,13 @@ public override int AllocationSize(ApiCredentials? value) else { return 1 - + FfiConverterTypeApiCredentials.INSTANCE.AllocationSize((ApiCredentials)value); + + FfiConverterTypeDataTrackSchemaId.INSTANCE.AllocationSize( + (DataTrackSchemaId)value + ); } } - public override void Write(ApiCredentials? value, BigEndianStream stream) + public override void Write(DataTrackSchemaId? value, BigEndianStream stream) { if (value == null) { @@ -2749,7 +13729,7 @@ public override void Write(ApiCredentials? value, BigEndianStream stream) else { stream.WriteByte(1); - FfiConverterTypeApiCredentials.INSTANCE.Write((ApiCredentials)value, stream); + FfiConverterTypeDataTrackSchemaId.INSTANCE.Write((DataTrackSchemaId)value, stream); } } } @@ -2795,6 +13775,46 @@ public override void Write(LogForwardEntry? value, BigEndianStream stream) } } + class FfiConverterOptionalTypeOpenedStream : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeOpenedStream INSTANCE = + new FfiConverterOptionalTypeOpenedStream(); + + public override OpenedStream? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeOpenedStream.INSTANCE.Read(stream); + } + + public override int AllocationSize(OpenedStream? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterTypeOpenedStream.INSTANCE.AllocationSize((OpenedStream)value); + } + } + + public override void Write(OpenedStream? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeOpenedStream.INSTANCE.Write((OpenedStream)value, stream); + } + } + } + class FfiConverterOptionalTypeRoomConfiguration : FfiConverterRustBuffer { public static FfiConverterOptionalTypeRoomConfiguration INSTANCE = @@ -2918,22 +13938,194 @@ public override void Write(VideoGrants? value, BigEndianStream stream) } } - class FfiConverterOptionalDictionaryStringString - : FfiConverterRustBuffer?> + class FfiConverterOptionalTypeDataTrackFrameEncoding + : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeDataTrackFrameEncoding INSTANCE = + new FfiConverterOptionalTypeDataTrackFrameEncoding(); + + public override DataTrackFrameEncoding? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeDataTrackFrameEncoding.INSTANCE.Read(stream); + } + + public override int AllocationSize(DataTrackFrameEncoding? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterTypeDataTrackFrameEncoding.INSTANCE.AllocationSize( + (DataTrackFrameEncoding)value + ); + } + } + + public override void Write(DataTrackFrameEncoding? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeDataTrackFrameEncoding.INSTANCE.Write( + (DataTrackFrameEncoding)value, + stream + ); + } + } + } + + class FfiConverterOptionalTypeOperationType : FfiConverterRustBuffer + { + public static FfiConverterOptionalTypeOperationType INSTANCE = + new FfiConverterOptionalTypeOperationType(); + + public override OperationType? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterTypeOperationType.INSTANCE.Read(stream); + } + + public override int AllocationSize(OperationType? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterTypeOperationType.INSTANCE.AllocationSize((OperationType)value); + } + } + + public override void Write(OperationType? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterTypeOperationType.INSTANCE.Write((OperationType)value, stream); + } + } + } + + class FfiConverterOptionalSequenceTypeBytes : FfiConverterRustBuffer + { + public static FfiConverterOptionalSequenceTypeBytes INSTANCE = + new FfiConverterOptionalSequenceTypeBytes(); + + public override Bytes[]? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterSequenceTypeBytes.INSTANCE.Read(stream); + } + + public override int AllocationSize(Bytes[]? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + FfiConverterSequenceTypeBytes.INSTANCE.AllocationSize((Bytes[])value); + } + } + + public override void Write(Bytes[]? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterSequenceTypeBytes.INSTANCE.Write((Bytes[])value, stream); + } + } + } + + class FfiConverterOptionalDictionaryStringString + : FfiConverterRustBuffer?> + { + public static FfiConverterOptionalDictionaryStringString INSTANCE = + new FfiConverterOptionalDictionaryStringString(); + + public override Dictionary? Read(BigEndianStream stream) + { + if (stream.ReadByte() == 0) + { + return null; + } + return FfiConverterDictionaryStringString.INSTANCE.Read(stream); + } + + public override int AllocationSize(Dictionary? value) + { + if (value == null) + { + return 1; + } + else + { + return 1 + + FfiConverterDictionaryStringString.INSTANCE.AllocationSize( + (Dictionary)value + ); + } + } + + public override void Write(Dictionary? value, BigEndianStream stream) + { + if (value == null) + { + stream.WriteByte(0); + } + else + { + stream.WriteByte(1); + FfiConverterDictionaryStringString.INSTANCE.Write( + (Dictionary)value, + stream + ); + } + } + } + + class FfiConverterOptionalTypeBytes : FfiConverterRustBuffer { - public static FfiConverterOptionalDictionaryStringString INSTANCE = - new FfiConverterOptionalDictionaryStringString(); + public static FfiConverterOptionalTypeBytes INSTANCE = new FfiConverterOptionalTypeBytes(); - public override Dictionary? Read(BigEndianStream stream) + public override Bytes? Read(BigEndianStream stream) { if (stream.ReadByte() == 0) { return null; } - return FfiConverterDictionaryStringString.INSTANCE.Read(stream); + return FfiConverterTypeBytes.INSTANCE.Read(stream); } - public override int AllocationSize(Dictionary? value) + public override int AllocationSize(Bytes? value) { if (value == null) { @@ -2941,14 +14133,11 @@ public override int AllocationSize(Dictionary? value) } else { - return 1 - + FfiConverterDictionaryStringString.INSTANCE.AllocationSize( - (Dictionary)value - ); + return 1 + FfiConverterTypeBytes.INSTANCE.AllocationSize((Bytes)value); } } - public override void Write(Dictionary? value, BigEndianStream stream) + public override void Write(Bytes? value, BigEndianStream stream) { if (value == null) { @@ -2957,10 +14146,7 @@ public override void Write(Dictionary? value, BigEndianStream st else { stream.WriteByte(1); - FfiConverterDictionaryStringString.INSTANCE.Write( - (Dictionary)value, - stream - ); + FfiConverterTypeBytes.INSTANCE.Write((Bytes)value, stream); } } } @@ -3062,6 +14248,103 @@ public override void Write(RoomAgentDispatch[] value, BigEndianStream stream) } } + class FfiConverterSequenceTypeClientCapability : FfiConverterRustBuffer + { + public static FfiConverterSequenceTypeClientCapability INSTANCE = + new FfiConverterSequenceTypeClientCapability(); + + public override ClientCapability[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + if (length == 0) + { + return System.Array.Empty(); + } + + var result = new ClientCapability[length]; + for (int i = 0; i < length; i++) + { + result[i] = FfiConverterTypeClientCapability.INSTANCE.Read(stream); + } + return result; + } + + public override int AllocationSize(ClientCapability[] value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => FfiConverterTypeClientCapability.INSTANCE.AllocationSize(item)); + return sizeForLength + sizeForItems; + } + + public override void Write(ClientCapability[] value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Length); + value.ForEach(item => FfiConverterTypeClientCapability.INSTANCE.Write(item, stream)); + } + } + + class FfiConverterSequenceTypeBytes : FfiConverterRustBuffer + { + public static FfiConverterSequenceTypeBytes INSTANCE = new FfiConverterSequenceTypeBytes(); + + public override Bytes[] Read(BigEndianStream stream) + { + var length = stream.ReadInt(); + if (length == 0) + { + return System.Array.Empty(); + } + + var result = new Bytes[length]; + for (int i = 0; i < length; i++) + { + result[i] = FfiConverterTypeBytes.INSTANCE.Read(stream); + } + return result; + } + + public override int AllocationSize(Bytes[] value) + { + var sizeForLength = 4; + + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + return sizeForLength; + } + + var sizeForItems = value.Sum(item => FfiConverterTypeBytes.INSTANCE.AllocationSize(item)); + return sizeForLength + sizeForItems; + } + + public override void Write(Bytes[] value, BigEndianStream stream) + { + // details/1-empty-list-as-default-method-parameter.md + if (value == null) + { + stream.WriteInt(0); + return; + } + + stream.WriteInt(value.Length); + value.ForEach(item => FfiConverterTypeBytes.INSTANCE.Write(item, stream)); + } + } + class FfiConverterDictionaryStringString : FfiConverterRustBuffer> { public static FfiConverterDictionaryStringString INSTANCE = @@ -3115,53 +14398,177 @@ public override void Write(Dictionary value, BigEndianStream str } } - class ConcurrentHandleMap - where T : notnull + /** + * Typealias from the type name used in the UDL file to the builtin type. This + * is needed because the UDL type name is used in function/method signatures. + * It's also what we have an external type that references a custom type. + */ + + class FfiConverterTypeDataTrackFrameEncoding : FfiConverterRustBuffer { - readonly ConcurrentDictionary _map = new(); + public static FfiConverterTypeDataTrackFrameEncoding INSTANCE = + new FfiConverterTypeDataTrackFrameEncoding(); - // Handles are odd numbers (1, 3, 5, ...) — the lowest bit must always be set. - // Rust uses (handle & 1) to distinguish foreign-language handles from Rust Arc - // pointers, which are always even due to memory alignment. See uniffi_core/src/ffi/handle.rs. - const long HANDLE_INITIAL = 1; - const long HANDLE_DELTA = 2; - long _currentHandle = HANDLE_INITIAL - HANDLE_DELTA; + public override DataTrackFrameEncoding Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackFrameEncoding.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } - public ulong Insert(T obj) + public override int AllocationSize(DataTrackFrameEncoding value) { - var handle = (ulong)Interlocked.Add(ref _currentHandle, HANDLE_DELTA); - if (!_map.TryAdd(handle, obj)) - { - throw new InternalException("ConcurrentHandleMap: Duplicate handle"); - } - return handle; + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackFrameEncoding.INSTANCE.AllocationSize( + value + ); } - public bool TryGet(ulong handle, [NotNullWhen(true)] out T? result) + public override void Write(DataTrackFrameEncoding value, BigEndianStream stream) { - return _map.TryGetValue(handle, out result); + uniffi.livekit_datatrack.FfiConverterTypeDataTrackFrameEncoding.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); } + } - public T Get(ulong handle) + class FfiConverterTypeDataTrackSchemaEncoding : FfiConverterRustBuffer + { + public static FfiConverterTypeDataTrackSchemaEncoding INSTANCE = + new FfiConverterTypeDataTrackSchemaEncoding(); + + public override DataTrackSchemaEncoding Read(BigEndianStream stream) { - if (_map.TryGetValue(handle, out var result)) - { - return result; - } - else - { - throw new InternalException("ConcurrentHandleMap: Invalid handle"); - } + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); } - public bool Remove(ulong handle) + public override int AllocationSize(DataTrackSchemaEncoding value) { - return _map.TryRemove(handle, out _); + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.AllocationSize( + value + ); } - public bool Remove(ulong handle, [NotNullWhen(true)] out T? result) + public override void Write(DataTrackSchemaEncoding value, BigEndianStream stream) { - return _map.TryRemove(handle, out result); + uniffi.livekit_datatrack.FfiConverterTypeDataTrackSchemaEncoding.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + + class FfiConverterTypeDataTrackSubscribeError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypeDataTrackSubscribeError INSTANCE = + new FfiConverterTypeDataTrackSubscribeError(); + + public override DataTrackSubscribeException Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackSubscribeError.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(DataTrackSubscribeException value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDataTrackSubscribeError.INSTANCE.AllocationSize( + value + ); + } + + public override void Write(DataTrackSubscribeException value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypeDataTrackSubscribeError.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + + class FfiConverterTypePublishError + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypePublishError INSTANCE = new FfiConverterTypePublishError(); + + public override PublishException Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypePublishError.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(PublishException value) + { + return uniffi.livekit_datatrack.FfiConverterTypePublishError.INSTANCE.AllocationSize(value); + } + + public override void Write(PublishException value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypePublishError.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + + class FfiConverterTypePushFrameErrorReason + : FfiConverterRustBuffer, + CallStatusErrorHandler + { + public static FfiConverterTypePushFrameErrorReason INSTANCE = + new FfiConverterTypePushFrameErrorReason(); + + public override PushFrameErrorReason Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypePushFrameErrorReason.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(PushFrameErrorReason value) + { + return uniffi.livekit_datatrack.FfiConverterTypePushFrameErrorReason.INSTANCE.AllocationSize( + value + ); + } + + public override void Write(PushFrameErrorReason value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypePushFrameErrorReason.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + + class FfiConverterTypeBytes : FfiConverterRustBuffer + { + public static FfiConverterTypeBytes INSTANCE = new FfiConverterTypeBytes(); + + public override Bytes Read(BigEndianStream stream) + { + return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Read( + new uniffi.livekit_common.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(Bytes value) + { + return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.AllocationSize(value); + } + + public override void Write(Bytes value, BigEndianStream stream) + { + uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Write( + value, + new uniffi.livekit_common.BigEndianStream(stream.InnerStream) + ); } } @@ -3404,6 +14811,50 @@ public static string BuildVersion() ); } + /// + /// Builds an incoming manager whose opened streams are pulled rather than pushed. + /// + /// `max_payload_byte_length` is fixed for the lifetime of the manager; see + /// [`IncomingDataStreamManager::new`]. Hosts sourcing it from per-connection options should build + /// a fresh manager per session rather than memoizing one. + /// + public static PolledIncomingDataStreamManager PolledIncomingDataStreamManager( + ulong? @maxPayloadByteLength + ) + { + return FfiConverterTypePolledIncomingDataStreamManager.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_polled_incoming_data_stream_manager( + FfiConverterOptionalUInt64.INSTANCE.Lower(@maxPayloadByteLength), + ref _status + ) + ) + ); + } + + /// + /// Builds an outgoing manager whose packets are pulled rather than pushed. + /// + /// The two halves are wired together here rather than by the caller: passing a Rust object where + /// `Arc` is expected is awkward-to-impossible from some + /// bindings, and unnecessary — it's ordinary Rust on this side. + /// + public static PolledOutgoingDataStreamManager PolledOutgoingDataStreamManager( + RemoteParticipantRegistryDelegate @registry + ) + { + return FfiConverterTypePolledOutgoingDataStreamManager.INSTANCE.Lift( + _UniffiHelpers.RustCall( + (ref UniffiRustCallStatus _status) => + _UniFFILib.uniffi_livekit_uniffi_fn_func_polled_outgoing_data_stream_manager( + FfiConverterTypeRemoteParticipantRegistryDelegate.INSTANCE.Lower(@registry), + ref _status + ) + ) + ); + } + /// /// Bootstraps log forwarding. /// @@ -3457,4 +14908,72 @@ ref status ); } } + + // WORKAROUND(uniffi-bindgen-cs): `FfiConverterTypeDecryptionProvider` belongs to uniffi.livekit_datatrack. uniffi-bindgen-cs aliases that + // crate's converter directly, whose Read/Write take that crate's BigEndianStream instead of + // this file's. This forwarding converter re-wraps the stream the way the generator already + // does for external records and enums. See Scripts~/uniffi/UPSTREAM-external-object-converter-stream.md + class FfiConverterTypeDecryptionProvider : FfiConverter + { + public static FfiConverterTypeDecryptionProvider INSTANCE = new FfiConverterTypeDecryptionProvider(); + + public override uniffi.livekit_datatrack.DecryptionProvider Lift(ulong value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lift(value); + } + + public override ulong Lower(uniffi.livekit_datatrack.DecryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lower(value); + } + + public override uniffi.livekit_datatrack.DecryptionProvider Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Read(new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); + } + + public override int AllocationSize(uniffi.livekit_datatrack.DecryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.AllocationSize(value); + } + + public override void Write(uniffi.livekit_datatrack.DecryptionProvider value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Write(value, new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); + } + } + + // WORKAROUND(uniffi-bindgen-cs): `FfiConverterTypeEncryptionProvider` belongs to uniffi.livekit_datatrack. uniffi-bindgen-cs aliases that + // crate's converter directly, whose Read/Write take that crate's BigEndianStream instead of + // this file's. This forwarding converter re-wraps the stream the way the generator already + // does for external records and enums. See Scripts~/uniffi/UPSTREAM-external-object-converter-stream.md + class FfiConverterTypeEncryptionProvider : FfiConverter + { + public static FfiConverterTypeEncryptionProvider INSTANCE = new FfiConverterTypeEncryptionProvider(); + + public override uniffi.livekit_datatrack.EncryptionProvider Lift(ulong value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lift(value); + } + + public override ulong Lower(uniffi.livekit_datatrack.EncryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lower(value); + } + + public override uniffi.livekit_datatrack.EncryptionProvider Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Read(new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); + } + + public override int AllocationSize(uniffi.livekit_datatrack.EncryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.AllocationSize(value); + } + + public override void Write(uniffi.livekit_datatrack.EncryptionProvider value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Write(value, new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); + } + } } diff --git a/Runtime/Scripts/UniFFI/livekit_uniffi.cs.meta b/Runtime/Scripts/UniFFI/livekit_uniffi.cs.meta new file mode 100644 index 00000000..4327f50e --- /dev/null +++ b/Runtime/Scripts/UniFFI/livekit_uniffi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2f5f4c511b174a859cc3454e5b93524 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Meet/Assets/Runtime/MeetManager.cs b/Samples~/Meet/Assets/Runtime/MeetManager.cs index eba391c2..6f5712d5 100644 --- a/Samples~/Meet/Assets/Runtime/MeetManager.cs +++ b/Samples~/Meet/Assets/Runtime/MeetManager.cs @@ -5,7 +5,7 @@ using LiveKit; using LiveKit.Proto; using RoomOptions = LiveKit.RoomOptions; -using LiveKit.Uniffi; +using uniffi.livekit_uniffi; /// /// Manages a LiveKit room connection with local/remote audio and video tracks. diff --git a/Scripts~/build_uniffi_locally.sh b/Scripts~/build_uniffi_locally.sh index 1453679c..0bcdede4 100755 --- a/Scripts~/build_uniffi_locally.sh +++ b/Scripts~/build_uniffi_locally.sh @@ -1,8 +1,8 @@ #!/bin/bash -# Builds the experimental livekit-uniffi crate (client-sdk-rust~/livekit-uniffi), installs the -# resulting liblivekit_uniffi.dylib next to liblivekit_ffi.dylib in Runtime/Plugins and -# regenerates the UniFFI C# bindings from it with generate_uniffi_bindings.sh. +# Builds the experimental livekit-uniffi crate (client-sdk-rust~/livekit-uniffi), regenerates the +# UniFFI C# bindings from the resulting liblivekit_uniffi.dylib with generate_uniffi_bindings.sh +# and, only if that succeeded, installs the dylib next to liblivekit_ffi.dylib in Runtime/Plugins. # # The build is the plain-cargo equivalent of `cargo make build` in the crate's Makefile.toml, # with a selectable build type instead of the release-only cargo-make task. @@ -36,8 +36,9 @@ usage() { echo " release Optimized release build" echo " debug Debug build" echo "" - echo "After the build, the UniFFI C# bindings in Runtime/Scripts/UniFFI are regenerated" - echo "from the new library with generate_uniffi_bindings.sh (requires uniffi-bindgen-cs)." + echo "The UniFFI C# bindings in Runtime/Scripts/UniFFI are regenerated from the new library" + echo "with generate_uniffi_bindings.sh (requires uniffi-bindgen-cs) before it is installed;" + echo "if that fails, neither the bindings nor the installed library change." exit 1 } @@ -92,6 +93,13 @@ if [ $BUILD_STATUS -ne 0 ]; then exit 1 fi +# Generate the C# bindings from the freshly built dylib BEFORE installing it. If they cannot +# be produced (e.g. the post-processing rejects the output), the previous library stays in +# place, so Runtime/Plugins never gets ahead of Runtime/Scripts/UniFFI: a mismatched pair +# throws in the bindings' contract/checksum check on first use. +echo "" +"$SCRIPT_DIR/generate_uniffi_bindings.sh" "$SRC" || exit 1 + # Copy a built artifact into the package by writing a temp file next to the # destination and renaming it into place, instead of overwriting in place. # macOS caches code-signature state per inode: overwriting a signed dylib that a @@ -117,9 +125,5 @@ else exit 1 fi -# Regenerate the UniFFI C# bindings from the freshly built dylib. -echo "" -"$SCRIPT_DIR/generate_uniffi_bindings.sh" "$SRC" || exit 1 - echo "" echo -e "${YELLOW}WARNING: QUIT UNITY TO LOAD NEW LIB${RESET}" diff --git a/Scripts~/generate_uniffi_bindings.sh b/Scripts~/generate_uniffi_bindings.sh index fc788a3c..8914157b 100755 --- a/Scripts~/generate_uniffi_bindings.sh +++ b/Scripts~/generate_uniffi_bindings.sh @@ -1,7 +1,8 @@ #!/bin/bash # Regenerates the UniFFI C# bindings in Runtime/Scripts/UniFFI from a native library that -# carries UniFFI metadata and post-processes them for C# 9 (Unity). +# carries UniFFI metadata and post-processes them for C# 9 (Unity), including the clearly +# marked workarounds for uniffi-bindgen-cs bugs documented in uniffi/UPSTREAM-*.md. # # Usage: generate_uniffi_bindings.sh [LIBRARY] # LIBRARY dylib to read the UniFFI metadata from. Defaults to the macOS @@ -89,7 +90,10 @@ fi # --no-polyfill: the Runtime assembly already defines the IsExternalInit marker type that # records / init accessors need (Runtime/Scripts/Internal/IsExternalInit.cs). -python3 "$DOWNGRADE" --no-polyfill "$TMP_OUT" || exit 1 +if ! python3 "$DOWNGRADE" --no-polyfill "$TMP_OUT"; then + echo -e "${RED}C# 9 post-processing failed. Bindings in $OUT_DIR left untouched.${RESET}" + exit 1 +fi # Replace the generated files: drop every previously generated .cs, move the new set in and # remove .meta files whose .cs is gone. Regenerated files keep their .meta (and GUID); Unity diff --git a/Scripts~/uniffi/downgrade_uniffi_bindings.py b/Scripts~/uniffi/downgrade_uniffi_bindings.py index 47e63da7..88a9503c 100755 --- a/Scripts~/uniffi/downgrade_uniffi_bindings.py +++ b/Scripts~/uniffi/downgrade_uniffi_bindings.py @@ -19,6 +19,19 @@ C# 12) cannot be expressed in C# 9 and are reported as errors; change the custom type mapping in uniffi.toml instead. +Two uniffi-bindgen-cs bugs in library-mode output that spans several crates +are worked around as well. Everything they insert is preceded by a comment +starting with `WORKAROUND(uniffi-bindgen-cs)` and points to the bug report +next to this script: + + * a custom type of another crate (e.g. `Bytes`) is only aliased in that + crate's file, but C# `using` aliases are per file; the alias is repeated + in every file that uses the type (UPSTREAM-external-custom-type-alias.md) + * the converter of an object / callback interface of another crate is a + plain alias of that crate's converter, whose Read/Write take that crate's + BigEndianStream; a local forwarding converter that re-wraps the stream + replaces the alias (UPSTREAM-external-object-converter-stream.md) + Usage: downgrade_uniffi_bindings.py [--no-polyfill] [...] @@ -68,6 +81,190 @@ ) INIT_ACCESSOR = re.compile(r"\binit\s*[;{]") +# --------------------------------------------------------------------------- +# WORKAROUNDS for uniffi-bindgen-cs (v0.11.0+v0.31.0) library-mode output that +# spans several crates. Both are generator bugs in its external-type templates; +# the bug reports for upstream (NordSecurity/uniffi-bindgen-cs) live next to +# this script: +# UPSTREAM-external-custom-type-alias.md -> import_external_custom_type_aliases() +# UPSTREAM-external-object-converter-stream.md -> wrap_external_object_converters() +# Every line these insert into the bindings is preceded by a comment starting +# with WORKAROUND_TAG. Delete this whole section (and the two calls in +# process_file) once the bindgen release pinned in generate_uniffi_bindings.sh +# contains the fixes. +# --------------------------------------------------------------------------- +WORKAROUND_TAG = "WORKAROUND(uniffi-bindgen-cs)" +# `namespace Foo;` (file-scoped) or `namespace Foo` / `namespace Foo {` (block-scoped) +NAMESPACE_DECLARATION = re.compile(r"^\s*namespace\s+(?P[A-Za-z_][\w.]*)\s*[;{]?\s*$") +ALIAS_DECLARATION = re.compile(r"^\s*using\s+(?P[A-Za-z_@]\w*)\s*=\s*(?P[^;]+);\s*$") +# `ns.FfiConverterTypeX.INSTANCE.Read(` inside the forwarding converters that +# ExternalTypeTemplate.cs generates for every record, enum, error or custom type +# used from another crate +EXTERNAL_CONVERTER_CALL = re.compile( + r"(?P[A-Za-z_][\w.]*)\.FfiConverterType(?P\w+)\.INSTANCE\.Read\(" +) +# `using FfiConverterTypeX = ns.FfiConverterTypeX;` from ExternalObjectTypeTemplate.cs +EXTERNAL_OBJECT_ALIAS = re.compile( + r"^\s*using\s+(?PFfiConverterType\w+)\s*=\s*(?P[A-Za-z_][\w.]*)\.(?P=converter)\s*;\s*$" +) +# `class FfiConverterTypeX : FfiConverter` as declared by the defining crate +CONVERTER_DECLARATION = re.compile( + r"^\s*(?:\w+\s+)*class\s+(?PFfiConverterType\w+)\s*:\s*FfiConverter<(?P[^,<>]+),\s*(?P[^<>]+)>" +) + + +def read_lines(path): + """Returns (lines, bom, newline) of a generated file, without the trailing newline.""" + raw = path.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw[3:].decode("utf-8") if bom else raw.decode("utf-8") + newline = "\r\n" if "\r\n" in text else "\n" + lines = text.replace("\r\n", "\n").split("\n") + if lines and lines[-1] == "": + lines.pop() # trailing newline is re-added on write + return lines, bom, newline + + +class GeneratedFile: + """What the cross-file workarounds need to know about one bindings file.""" + + def __init__(self, path): + self.path = path + lines, _, _ = read_lines(path) + namespaces = [m.group("name") for m in map(NAMESPACE_DECLARATION.match, lines) if m] + self.namespace = namespaces[0] if namespaces else None + self.aliases = { + m.group("name"): m.group("target").strip() for m in map(ALIAS_DECLARATION.match, lines) if m + } + self.converters = { + m.group("converter"): (m.group("cs_type").strip(), m.group("ffi_type").strip()) + for m in map(CONVERTER_DECLARATION.match, lines) + if m + } + + +def namespace_layout(lines): + """Returns (body_start, body_end, indent) of the file's namespace: the line + range that holds its members and the indentation members use.""" + for index, line in enumerate(lines): + if NAMESPACE_DECLARATION.match(line): + break + else: + raise ValueError("no namespace declaration found") + + if line.rstrip().endswith(";"): # file-scoped: the body runs to the end of the file + return index + 1, len(lines), "" + + body_start = index + 1 if line.rstrip().endswith("{") else index + 2 + for body_end in range(len(lines) - 1, index, -1): + if lines[body_end].strip() == "}": + return body_start, body_end, INDENT + raise ValueError("block-scoped namespace without closing brace") + + +def import_external_custom_type_aliases(lines, siblings): + """WORKAROUND: repeat the `using Name = Target;` alias of a custom type that + another crate defines in the file that uses it. uniffi-bindgen-cs emits the + alias only in the defining crate's file, but C# `using` aliases are per file + (UPSTREAM-external-custom-type-alias.md). Returns (lines, changed).""" + by_namespace = {sibling.namespace: sibling for sibling in siblings if sibling.namespace} + declared = {m.group("name") for m in map(ALIAS_DECLARATION.match, lines) if m} + missing = {} + for match in EXTERNAL_CONVERTER_CALL.finditer("\n".join(lines)): + namespace, name = match.group("namespace"), match.group("name") + sibling = by_namespace.get(namespace) + if sibling is None or name in declared: + continue + target = sibling.aliases.get(name) + if target is not None: # only custom types are aliases; records and enums resolve via `using ns;` + missing[name] = (namespace, target) + if not missing: + return lines, False + + body_start, _, indent = namespace_layout(lines) + inserted = [] + for name, (namespace, target) in sorted(missing.items()): + inserted += [ + f"{indent}// {WORKAROUND_TAG}: `{name}` is a custom type of {namespace}, the only file uniffi-bindgen-cs", + f"{indent}// aliases it in. See Scripts~/uniffi/UPSTREAM-external-custom-type-alias.md", + f"{indent}using {name} = {target};", + ] + return lines[:body_start] + inserted + lines[body_start:], True + + +def external_object_converter(converter, namespace, cs_type, ffi_type): + """A local converter that forwards to `namespace.converter` and re-wraps the + stream, like ExternalTypeTemplate.cs already does for records and enums.""" + remote = f"{namespace}.{converter}.INSTANCE" + if "." not in cs_type: + cs_type = f"{namespace}.{cs_type}" + return [ + f"// {WORKAROUND_TAG}: `{converter}` belongs to {namespace}. uniffi-bindgen-cs aliases that", + "// crate's converter directly, whose Read/Write take that crate's BigEndianStream instead of", + "// this file's. This forwarding converter re-wraps the stream the way the generator already", + "// does for external records and enums. See Scripts~/uniffi/UPSTREAM-external-object-converter-stream.md", + f"class {converter} : FfiConverter<{cs_type}, {ffi_type}>", + "{", + f"{INDENT}public static {converter} INSTANCE = new {converter}();", + "", + f"{INDENT}public override {cs_type} Lift({ffi_type} value)", + f"{INDENT}{{", + f"{INDENT}{INDENT}return {remote}.Lift(value);", + f"{INDENT}}}", + "", + f"{INDENT}public override {ffi_type} Lower({cs_type} value)", + f"{INDENT}{{", + f"{INDENT}{INDENT}return {remote}.Lower(value);", + f"{INDENT}}}", + "", + f"{INDENT}public override {cs_type} Read(BigEndianStream stream)", + f"{INDENT}{{", + f"{INDENT}{INDENT}return {remote}.Read(new {namespace}.BigEndianStream(stream.InnerStream));", + f"{INDENT}}}", + "", + f"{INDENT}public override int AllocationSize({cs_type} value)", + f"{INDENT}{{", + f"{INDENT}{INDENT}return {remote}.AllocationSize(value);", + f"{INDENT}}}", + "", + f"{INDENT}public override void Write({cs_type} value, BigEndianStream stream)", + f"{INDENT}{{", + f"{INDENT}{INDENT}{remote}.Write(value, new {namespace}.BigEndianStream(stream.InnerStream));", + f"{INDENT}}}", + "}", + ] + + +def wrap_external_object_converters(lines, siblings): + """WORKAROUND: replace `using FfiConverterTypeX = ns.FfiConverterTypeX;` for + an object or callback interface of another crate with a local forwarding + converter (UPSTREAM-external-object-converter-stream.md). Returns (lines, changed).""" + by_namespace = {sibling.namespace: sibling for sibling in siblings if sibling.namespace} + kept, converters = [], [] + for number, line in enumerate(lines, start=1): + alias = EXTERNAL_OBJECT_ALIAS.match(line) + if not alias: + kept.append(line) + continue + converter, namespace = alias.group("converter"), alias.group("namespace") + sibling = by_namespace.get(namespace) + declaration = sibling.converters.get(converter) if sibling else None + if declaration is None: + raise ValueError( + f"line {number}: `{line.strip()}` refers to a converter of {namespace}, but no generated file " + f"in this directory declares `class {converter} : FfiConverter<...>`; run this script on the " + "directory that holds all bindings of the library" + ) + converters.append(external_object_converter(converter, namespace, *declaration)) + if not converters: + return lines, False + + _, body_end, indent = namespace_layout(kept) + body = [] + for converter in converters: + body += [""] + [indent + line if line else "" for line in converter] + return kept[:body_end] + body + kept[body_end:], True + def block_scope_namespace(lines): """Wrap everything after a file-scoped namespace declaration into a @@ -179,21 +376,22 @@ def needs_is_external_init(lines): return any(RECORD_DECLARATION.match(line) or INIT_ACCESSOR.search(line) for line in lines) -def process_file(path): - """Returns (changed, needs_polyfill).""" - raw = path.read_bytes() - bom = raw.startswith(b"\xef\xbb\xbf") - text = raw[3:].decode("utf-8") if bom else raw.decode("utf-8") - newline = "\r\n" if "\r\n" in text else "\n" - lines = text.replace("\r\n", "\n").split("\n") - if lines and lines[-1] == "": - lines.pop() # trailing newline is re-added on write +def process_file(path, siblings=()): + """Returns (changed, needs_polyfill). `siblings` are the other generated + files of the same library (GeneratedFile), needed by the uniffi-bindgen-cs + workarounds.""" + lines, bom, newline = read_lines(path) + + # uniffi-bindgen-cs workarounds first; what they insert is treated like + # generated code by the C# 9 steps below. + lines, aliases_added = import_external_custom_type_aliases(lines, siblings) + lines, converters_wrapped = wrap_external_object_converters(lines, siblings) check_type_aliases(lines) lines, namespace_changed = block_scope_namespace(lines) lines, returns_changed = replace_empty_collection_returns(lines) lines, method_groups_changed = inline_method_groups(lines) - changed = namespace_changed or returns_changed or method_groups_changed + changed = aliases_added or converters_wrapped or namespace_changed or returns_changed or method_groups_changed if changed: lines = add_marker(lines) @@ -240,10 +438,14 @@ def main(argv): status = 1 continue + # All bindings files of the library, for the cross-file workarounds. + generated = [GeneratedFile(p) for p in sorted(directory.glob("*.cs")) if p.name != POLYFILL_NAME] + polyfill_needed = False for path in files: + siblings = [g for g in generated if g.path.resolve() != path.resolve()] try: - changed, needs_polyfill = process_file(path) + changed, needs_polyfill = process_file(path, siblings) except (ValueError, UnicodeDecodeError) as exc: print(f"error: {path}: {exc}", file=sys.stderr) status = 1 diff --git a/Scripts~/uniffi/uniffi.toml b/Scripts~/uniffi/uniffi.toml index a905383e..b702b9b0 100644 --- a/Scripts~/uniffi/uniffi.toml +++ b/Scripts~/uniffi/uniffi.toml @@ -1,7 +1,27 @@ # uniffi-bindgen-cs configuration for the C# bindings in Runtime/Scripts/UniFFI. -# Passed via --config by build_ffi_locally.sh; keeps the Unity-specific choices out of -# the Rust crate. Options: https://github.com/NordSecurity/uniffi-bindgen-cs/blob/main/docs/CONFIGURATION.md +# Passed via --config by generate_uniffi_bindings.sh; keeps the Unity-specific choices out of +# the Rust crates. Options: https://github.com/NordSecurity/uniffi-bindgen-cs/blob/main/docs/CONFIGURATION.md +# +# In library mode this file applies to EVERY crate compiled into liblivekit_uniffi (livekit-uniffi, +# livekit-common, livekit-net, livekit-datatrack), so it must not set `namespace`: a shared namespace +# makes the per-crate copies of the UniFFI runtime (RustBuffer, FfiConverter, BigEndianStream, ...) +# collide. Each crate keeps its default `uniffi.` namespace, e.g. uniffi.livekit_uniffi. [bindings.csharp] -namespace = "LiveKit.Uniffi" # Generated API types (methods class, records, enums, errors, objects) are internal by default. access_modifier = "public" + +# Custom types backed by Vec. Without a mapping uniffi-bindgen-cs emits `using Bytes = byte[];`, +# an alias of an array type that needs C# 12 while Unity compiles C# 9 (downgrade_uniffi_bindings.py +# rejects it). Map them to a named type instead; byte[] converts to ReadOnlyMemory implicitly. + +# livekit-common: uniffi::custom_type!(Bytes, Vec) +[bindings.csharp.custom_types.Bytes] +type_name = "System.ReadOnlyMemory" +into_custom = "new System.ReadOnlyMemory({})" +from_custom = "{}.ToArray()" + +# livekit-datatrack: uniffi::custom_type!(InitializationVector, Vec), a 12-byte AES-GCM IV +[bindings.csharp.custom_types.InitializationVector] +type_name = "System.ReadOnlyMemory" +into_custom = "new System.ReadOnlyMemory({})" +from_custom = "{}.ToArray()" diff --git a/client-sdk-rust~ b/client-sdk-rust~ index 267ebb84..4dfca31a 160000 --- a/client-sdk-rust~ +++ b/client-sdk-rust~ @@ -1 +1 @@ -Subproject commit 267ebb84a0c2e1b3f68f5af8a27f0f7d92b6a7ff +Subproject commit 4dfca31ae81939e40e8d15fe4dca0922ed5e687a From 6afa624cc7bf83a7377274d0fcabdb1a0dc21860 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:07:08 +0200 Subject: [PATCH 5/7] TEMP - disable workarounds --- Scripts~/uniffi/downgrade_uniffi_bindings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Scripts~/uniffi/downgrade_uniffi_bindings.py b/Scripts~/uniffi/downgrade_uniffi_bindings.py index 88a9503c..7cf75458 100755 --- a/Scripts~/uniffi/downgrade_uniffi_bindings.py +++ b/Scripts~/uniffi/downgrade_uniffi_bindings.py @@ -384,14 +384,15 @@ def process_file(path, siblings=()): # uniffi-bindgen-cs workarounds first; what they insert is treated like # generated code by the C# 9 steps below. - lines, aliases_added = import_external_custom_type_aliases(lines, siblings) - lines, converters_wrapped = wrap_external_object_converters(lines, siblings) + # lines, aliases_added = import_external_custom_type_aliases(lines, siblings) + # lines, converters_wrapped = wrap_external_object_converters(lines, siblings) check_type_aliases(lines) lines, namespace_changed = block_scope_namespace(lines) lines, returns_changed = replace_empty_collection_returns(lines) lines, method_groups_changed = inline_method_groups(lines) - changed = aliases_added or converters_wrapped or namespace_changed or returns_changed or method_groups_changed + # changed = aliases_added or converters_wrapped or namespace_changed or returns_changed or method_groups_changed + changed = namespace_changed or returns_changed or method_groups_changed if changed: lines = add_marker(lines) From 900be9d8b3e615aa4c96c6c4da229f88a2fa1611 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:55:59 +0200 Subject: [PATCH 6/7] TEMP - Using local uniffi build with both issues fixed --- Runtime/Scripts/UniFFI/livekit_datatrack.cs | 38 ++-- Runtime/Scripts/UniFFI/livekit_uniffi.cs | 188 +++++++++++--------- Scripts~/generate_uniffi_bindings.sh | 2 +- 3 files changed, 133 insertions(+), 95 deletions(-) diff --git a/Runtime/Scripts/UniFFI/livekit_datatrack.cs b/Runtime/Scripts/UniFFI/livekit_datatrack.cs index 34970a04..03a2fda5 100644 --- a/Runtime/Scripts/UniFFI/livekit_datatrack.cs +++ b/Runtime/Scripts/UniFFI/livekit_datatrack.cs @@ -15,14 +15,10 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; -using uniffi.livekit_common; namespace uniffi.livekit_datatrack { - // WORKAROUND(uniffi-bindgen-cs): `Bytes` is a custom type of uniffi.livekit_common, the only file uniffi-bindgen-cs - // aliases it in. See Scripts~/uniffi/UPSTREAM-external-custom-type-alias.md using Bytes = System.ReadOnlyMemory; - using InitializationVector = System.ReadOnlyMemory; // This is a helper for safely working with byte buffers returned from the Rust code. @@ -3436,28 +3432,44 @@ public override void Write(InitializationVector value, BigEndianStream stream) } } - class FfiConverterTypeBytes : FfiConverterRustBuffer + /** + * Typealias from the type name used in the UDL file to the custom type. This + * is needed because the UDL type name is used in function/method signatures. + * It's also what we have an external type that references a custom type. + */ + + class FfiConverterTypeBytes : FfiConverter { public static FfiConverterTypeBytes INSTANCE = new FfiConverterTypeBytes(); + public override Bytes Lift(RustBuffer value) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Lift(value); + return new System.ReadOnlyMemory(builtinValue); + } + + public override RustBuffer Lower(Bytes value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.Lower(builtinValue); + } + public override Bytes Read(BigEndianStream stream) { - return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Read( - new uniffi.livekit_common.BigEndianStream(stream.InnerStream) - ); + var builtinValue = FfiConverterByteArray.INSTANCE.Read(stream); + return new System.ReadOnlyMemory(builtinValue); } public override int AllocationSize(Bytes value) { - return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.AllocationSize(value); + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.AllocationSize(builtinValue); } public override void Write(Bytes value, BigEndianStream stream) { - uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Write( - value, - new uniffi.livekit_common.BigEndianStream(stream.InnerStream) - ); + var builtinValue = value.ToArray(); + FfiConverterByteArray.INSTANCE.Write(builtinValue, stream); } } #pragma warning restore 8625 diff --git a/Runtime/Scripts/UniFFI/livekit_uniffi.cs b/Runtime/Scripts/UniFFI/livekit_uniffi.cs index 7709e2a9..d14ccb94 100644 --- a/Runtime/Scripts/UniFFI/livekit_uniffi.cs +++ b/Runtime/Scripts/UniFFI/livekit_uniffi.cs @@ -16,15 +16,11 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using uniffi.livekit_common; using uniffi.livekit_datatrack; namespace uniffi.livekit_uniffi { - // WORKAROUND(uniffi-bindgen-cs): `Bytes` is a custom type of uniffi.livekit_common, the only file uniffi-bindgen-cs - // aliases it in. See Scripts~/uniffi/UPSTREAM-external-custom-type-alias.md using Bytes = System.ReadOnlyMemory; - using DataTrackSid = String; using FfiConverterTypeDataTrackSid = FfiConverterString; @@ -14404,6 +14400,88 @@ public override void Write(Dictionary value, BigEndianStream str * It's also what we have an external type that references a custom type. */ + // Forwards to the converter generated in `uniffi.livekit_datatrack`. Every generated file has its own + // `BigEndianStream` class, so `Read` and `Write` re-wrap the stream before handing it over. This is + // what lets the external object sit inside an optional, a sequence, a map or a record of this file. + class FfiConverterTypeDecryptionProvider : FfiConverter + { + public static FfiConverterTypeDecryptionProvider INSTANCE = + new FfiConverterTypeDecryptionProvider(); + + public override DecryptionProvider Lift(ulong value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lift(value); + } + + public override ulong Lower(DecryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lower(value); + } + + public override DecryptionProvider Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(DecryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.AllocationSize( + value + ); + } + + public override void Write(DecryptionProvider value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + + // Forwards to the converter generated in `uniffi.livekit_datatrack`. Every generated file has its own + // `BigEndianStream` class, so `Read` and `Write` re-wrap the stream before handing it over. This is + // what lets the external object sit inside an optional, a sequence, a map or a record of this file. + class FfiConverterTypeEncryptionProvider : FfiConverter + { + public static FfiConverterTypeEncryptionProvider INSTANCE = + new FfiConverterTypeEncryptionProvider(); + + public override EncryptionProvider Lift(ulong value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lift(value); + } + + public override ulong Lower(EncryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lower(value); + } + + public override EncryptionProvider Read(BigEndianStream stream) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Read( + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + + public override int AllocationSize(EncryptionProvider value) + { + return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.AllocationSize( + value + ); + } + + public override void Write(EncryptionProvider value, BigEndianStream stream) + { + uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Write( + value, + new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream) + ); + } + } + class FfiConverterTypeDataTrackFrameEncoding : FfiConverterRustBuffer { public static FfiConverterTypeDataTrackFrameEncoding INSTANCE = @@ -14547,28 +14625,44 @@ public override void Write(PushFrameErrorReason value, BigEndianStream stream) } } - class FfiConverterTypeBytes : FfiConverterRustBuffer + /** + * Typealias from the type name used in the UDL file to the custom type. This + * is needed because the UDL type name is used in function/method signatures. + * It's also what we have an external type that references a custom type. + */ + + class FfiConverterTypeBytes : FfiConverter { public static FfiConverterTypeBytes INSTANCE = new FfiConverterTypeBytes(); + public override Bytes Lift(RustBuffer value) + { + var builtinValue = FfiConverterByteArray.INSTANCE.Lift(value); + return new System.ReadOnlyMemory(builtinValue); + } + + public override RustBuffer Lower(Bytes value) + { + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.Lower(builtinValue); + } + public override Bytes Read(BigEndianStream stream) { - return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Read( - new uniffi.livekit_common.BigEndianStream(stream.InnerStream) - ); + var builtinValue = FfiConverterByteArray.INSTANCE.Read(stream); + return new System.ReadOnlyMemory(builtinValue); } public override int AllocationSize(Bytes value) { - return uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.AllocationSize(value); + var builtinValue = value.ToArray(); + return FfiConverterByteArray.INSTANCE.AllocationSize(builtinValue); } public override void Write(Bytes value, BigEndianStream stream) { - uniffi.livekit_common.FfiConverterTypeBytes.INSTANCE.Write( - value, - new uniffi.livekit_common.BigEndianStream(stream.InnerStream) - ); + var builtinValue = value.ToArray(); + FfiConverterByteArray.INSTANCE.Write(builtinValue, stream); } } @@ -14908,72 +15002,4 @@ ref status ); } } - - // WORKAROUND(uniffi-bindgen-cs): `FfiConverterTypeDecryptionProvider` belongs to uniffi.livekit_datatrack. uniffi-bindgen-cs aliases that - // crate's converter directly, whose Read/Write take that crate's BigEndianStream instead of - // this file's. This forwarding converter re-wraps the stream the way the generator already - // does for external records and enums. See Scripts~/uniffi/UPSTREAM-external-object-converter-stream.md - class FfiConverterTypeDecryptionProvider : FfiConverter - { - public static FfiConverterTypeDecryptionProvider INSTANCE = new FfiConverterTypeDecryptionProvider(); - - public override uniffi.livekit_datatrack.DecryptionProvider Lift(ulong value) - { - return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lift(value); - } - - public override ulong Lower(uniffi.livekit_datatrack.DecryptionProvider value) - { - return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Lower(value); - } - - public override uniffi.livekit_datatrack.DecryptionProvider Read(BigEndianStream stream) - { - return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Read(new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); - } - - public override int AllocationSize(uniffi.livekit_datatrack.DecryptionProvider value) - { - return uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.AllocationSize(value); - } - - public override void Write(uniffi.livekit_datatrack.DecryptionProvider value, BigEndianStream stream) - { - uniffi.livekit_datatrack.FfiConverterTypeDecryptionProvider.INSTANCE.Write(value, new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); - } - } - - // WORKAROUND(uniffi-bindgen-cs): `FfiConverterTypeEncryptionProvider` belongs to uniffi.livekit_datatrack. uniffi-bindgen-cs aliases that - // crate's converter directly, whose Read/Write take that crate's BigEndianStream instead of - // this file's. This forwarding converter re-wraps the stream the way the generator already - // does for external records and enums. See Scripts~/uniffi/UPSTREAM-external-object-converter-stream.md - class FfiConverterTypeEncryptionProvider : FfiConverter - { - public static FfiConverterTypeEncryptionProvider INSTANCE = new FfiConverterTypeEncryptionProvider(); - - public override uniffi.livekit_datatrack.EncryptionProvider Lift(ulong value) - { - return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lift(value); - } - - public override ulong Lower(uniffi.livekit_datatrack.EncryptionProvider value) - { - return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Lower(value); - } - - public override uniffi.livekit_datatrack.EncryptionProvider Read(BigEndianStream stream) - { - return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Read(new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); - } - - public override int AllocationSize(uniffi.livekit_datatrack.EncryptionProvider value) - { - return uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.AllocationSize(value); - } - - public override void Write(uniffi.livekit_datatrack.EncryptionProvider value, BigEndianStream stream) - { - uniffi.livekit_datatrack.FfiConverterTypeEncryptionProvider.INSTANCE.Write(value, new uniffi.livekit_datatrack.BigEndianStream(stream.InnerStream)); - } - } } diff --git a/Scripts~/generate_uniffi_bindings.sh b/Scripts~/generate_uniffi_bindings.sh index 8914157b..00a7a631 100755 --- a/Scripts~/generate_uniffi_bindings.sh +++ b/Scripts~/generate_uniffi_bindings.sh @@ -74,7 +74,7 @@ echo "Generating C# bindings from $LIBRARY..." # uniffi-bindgen-cs resolves the crate's own uniffi.toml via `cargo metadata`, so it has to # run from inside the Rust workspace. pushd "$RUST_ROOT" > /dev/null -uniffi-bindgen-cs --library "$LIBRARY" --config "$UNIFFI_CONFIG" --out-dir "$TMP_OUT" +/Users/maxheimbrock/dev/unity/UniFFI/uniffi-bindgen-cs/target/release/uniffi-bindgen-cs --library "$LIBRARY" --config "$UNIFFI_CONFIG" --out-dir "$TMP_OUT" BINDGEN_STATUS=$? popd > /dev/null From 4f9c28c9549b876cc7ba694d96c2c29505594e26 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 11 Sep 2026 13:56:24 +0200 Subject: [PATCH 7/7] Uniffi symmetric data track tests --- Tests/PlayMode/UniFFI.meta | 8 + .../PlayMode/UniFFI/UniffiDataStreamTests.cs | 423 ++++++++++++++++++ .../UniFFI/UniffiDataStreamTests.cs.meta | 11 + Tests/PlayMode/UniFFI/UniffiStreamPeer.cs | 264 +++++++++++ .../PlayMode/UniFFI/UniffiStreamPeer.cs.meta | 11 + 5 files changed, 717 insertions(+) create mode 100644 Tests/PlayMode/UniFFI.meta create mode 100644 Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs create mode 100644 Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs.meta create mode 100644 Tests/PlayMode/UniFFI/UniffiStreamPeer.cs create mode 100644 Tests/PlayMode/UniFFI/UniffiStreamPeer.cs.meta diff --git a/Tests/PlayMode/UniFFI.meta b/Tests/PlayMode/UniFFI.meta new file mode 100644 index 00000000..9eb15452 --- /dev/null +++ b/Tests/PlayMode/UniFFI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17a447105e19a4e6e8ec12e2804d8828 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs b/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs new file mode 100644 index 00000000..9bc13bd4 --- /dev/null +++ b/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using UnityEngine.TestTools; +using LiveKit.PlayModeTests.Utils; +using Uniffi = uniffi.livekit_uniffi; + +namespace LiveKit.PlayModeTests +{ + // Data streams v2 with the UniFFI stream managers on BOTH participants. + // + // Each UniffiStreamPeer borrows a protobuf-FFI Room as a dumb transport (see there), so what + // these tests exercise is the C# UniFFI binding surface end to end over a real SFU round trip: + // records with defaults and nullable members, sequences of the cross-crate `Bytes` custom + // type, Rust async surfaced as Task, a foreign callback interface invoked from Rust threads, + // typed exceptions, and object lifetimes. + // + // The wire framing (legacy header/chunks/trailer vs v2 single inline packet) is chosen by the + // registry the outgoing manager consults, which the tests control through + // FixedParticipantRegistry. + public class UniffiDataStreamTests + { + private const string SenderIdentity = "uniffi-sender"; + private const string ReceiverIdentity = "uniffi-receiver"; + + // livekit-data-stream STREAM_CHUNK_SIZE_BYTES. + private const int ChunkSizeBytes = 15000; + + [SetUp] + public void RequireUniffiLibrary() + { + if (!UniffiAvailability.IsAvailable) Assert.Ignore(UniffiAvailability.IgnoreReason); + } + + private static TestRoomContext NewContext() + { + var sender = TestRoomContext.ConnectionOptions.Default; + sender.Identity = SenderIdentity; + var receiver = TestRoomContext.ConnectionOptions.Default; + receiver.Identity = ReceiverIdentity; + return new TestRoomContext(new[] { sender, receiver }); + } + + // The tunnel bypasses the SFU's identity stamping of the inner packet, so the sender + // identity has to travel in the stream options. + private static Uniffi.StreamTextOptions TextOptions(string topic, Dictionary? attributes = null) => + new Uniffi.StreamTextOptions( + Topic: topic, + Attributes: attributes ?? new Dictionary(), + DestinationIdentities: Array.Empty(), + SenderIdentity: SenderIdentity); + + private static Uniffi.StreamByteOptions ByteOptions(string topic, string? name = null, string? mimeType = null, string[]? destinations = null) => + new Uniffi.StreamByteOptions( + Topic: topic, + Attributes: new Dictionary(), + DestinationIdentities: destinations ?? Array.Empty(), + MimeType: mimeType, + Name: name, + SenderIdentity: SenderIdentity); + + private static void AssertSucceeded(System.Threading.Tasks.Task task, string what) + { + Assert.IsTrue(UniffiTasks.Succeeded(task), $"{what} {UniffiTasks.Describe(task)}"); + } + + private static void AssertHealthy(UniffiStreamPeer sender, UniffiStreamPeer receiver) + { + CollectionAssert.IsEmpty(sender.Errors, "sender transport errors"); + CollectionAssert.IsEmpty(receiver.Errors, "receiver transport errors"); + } + + [UnityTest, Category("E2E")] + public IEnumerator SendText_LegacyFraming_ArrivesWithIdentityTopicAndAttributes() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + var attributes = new Dictionary { ["kind"] = "greeting" }; + var send = sender.Outgoing.SendText("hello from uniffi", TextOptions("chat", attributes)); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendText"); + Assert.AreEqual("chat", send.Result.Topic); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + var stream = opened.Result; + Assert.IsNotNull(stream, "incoming queue closed before a stream was opened"); + Assert.AreEqual(SenderIdentity, stream!.Identity); + Assert.IsNotNull(stream.TextReader, "text stream surfaced without a text reader"); + Assert.IsNull(stream.ByteReader, "text stream surfaced with a byte reader"); + + var info = stream.TextReader!.Info(); + Assert.AreEqual(send.Result.Id, info.Id); + Assert.AreEqual("chat", info.Topic); + Assert.AreEqual("greeting", info.Attributes["kind"]); + Assert.AreEqual(Uniffi.EncryptionType.None, info.EncryptionType); + + var readAll = stream.TextReader.ReadAll(); + yield return UniffiTasks.Await(readAll); + AssertSucceeded(readAll, "ReadAll"); + Assert.AreEqual("hello from uniffi", readAll.Result); + + // Legacy framing: header, one chunk, trailer. + var published = new Expectation(() => sender.PacketsPublished >= 3, 5f); + yield return published.Wait(); + Assert.IsNull(published.Error, published.Error); + yield return null; + Assert.AreEqual(3, sender.PacketsPublished, "legacy send should be header + chunk + trailer"); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator SendText_V2Recipient_InlinedIntoSinglePacket() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.V2(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + // Highly compressible and well under the chunk size, so the packet is inlined and the + // deflate path runs on both ends. + var text = string.Concat(Enumerable.Repeat("the quick brown fox jumps over the lazy dog. ", 200)); + var send = sender.Outgoing.SendText(text, TextOptions("chat")); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendText"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.TextReader, "no text stream opened"); + + var readAll = opened.Result!.TextReader!.ReadAll(); + yield return UniffiTasks.Await(readAll); + AssertSucceeded(readAll, "ReadAll"); + Assert.AreEqual(text, readAll.Result); + Assert.AreEqual((ulong)text.Length, opened.Result.TextReader.Info().TotalLength); + + var published = new Expectation(() => sender.PacketsPublished >= 1, 5f); + yield return published.Wait(); + Assert.IsNull(published.Error, published.Error); + yield return null; + Assert.AreEqual(1, sender.PacketsPublished, "v2 send should be a single inline header packet"); + Assert.AreEqual(1, receiver.PacketsReceived); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator StreamText_IncrementalWriter_ReadAllConcatenatesWrites() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + var open = sender.Outgoing.StreamText(TextOptions("notes")); + yield return UniffiTasks.Await(open); + AssertSucceeded(open, "StreamText"); + using var writer = open.Result; + + foreach (var part in new[] { "Hello, ", "UniFFI ", "world" }) + { + var write = writer.Write(part); + yield return UniffiTasks.Await(write); + AssertSucceeded(write, $"Write(\"{part}\")"); + } + + var isOpen = writer.IsOpen(); + yield return UniffiTasks.Await(isOpen); + AssertSucceeded(isOpen, "IsOpen"); + Assert.IsTrue(isOpen.Result, "writer reported closed before Close"); + + var close = writer.Close(); + yield return UniffiTasks.Await(close); + AssertSucceeded(close, "Close"); + + isOpen = writer.IsOpen(); + yield return UniffiTasks.Await(isOpen); + AssertSucceeded(isOpen, "IsOpen after Close"); + Assert.IsFalse(isOpen.Result, "writer still open after Close"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.TextReader, "no text stream opened"); + Assert.IsNull(opened.Result!.TextReader!.Info().TotalLength, "incremental stream should not declare a total length"); + + var readAll = opened.Result.TextReader.ReadAll(); + yield return UniffiTasks.Await(readAll); + AssertSucceeded(readAll, "ReadAll"); + Assert.AreEqual("Hello, UniFFI world", readAll.Result); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator SendBytes_TargetedAtReceiver_PreservesPayloadNameAndMimeType() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + var payload = new byte[4096]; + new Random(42).NextBytes(payload); + var options = ByteOptions("files", name: "blob.bin", mimeType: "application/x-test", destinations: new[] { ReceiverIdentity }); + var send = sender.Outgoing.SendBytes(new ReadOnlyMemory(payload), options); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendBytes"); + Assert.AreEqual("blob.bin", send.Result.Name); + Assert.AreEqual((ulong)payload.Length, send.Result.TotalLength); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + var stream = opened.Result; + Assert.IsNotNull(stream?.ByteReader, "byte stream surfaced without a byte reader"); + Assert.IsNull(stream!.TextReader, "byte stream surfaced with a text reader"); + Assert.AreEqual(SenderIdentity, stream.Identity); + + var info = stream.ByteReader!.Info(); + Assert.AreEqual(send.Result.Id, info.Id); + Assert.AreEqual("files", info.Topic); + Assert.AreEqual("blob.bin", info.Name); + Assert.AreEqual("application/x-test", info.MimeType); + Assert.AreEqual((ulong)payload.Length, info.TotalLength); + + var readAll = stream.ByteReader.ReadAll(); + yield return UniffiTasks.Await(readAll); + AssertSucceeded(readAll, "ReadAll"); + CollectionAssert.AreEqual(payload, readAll.Result.ToArray()); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator StreamBytes_IncrementalWriter_NextYieldsChunksThenNull() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + var open = sender.Outgoing.StreamBytes(ByteOptions("bytes")); + yield return UniffiTasks.Await(open); + AssertSucceeded(open, "StreamBytes"); + using var writer = open.Result; + + var expected = new List(); + for (int i = 0; i < 3; i++) + { + var chunk = Enumerable.Repeat((byte)(i + 1), 5000).ToArray(); + expected.AddRange(chunk); + var write = writer.Write(new ReadOnlyMemory(chunk)); + yield return UniffiTasks.Await(write); + AssertSucceeded(write, $"Write #{i}"); + } + var close = writer.Close(); + yield return UniffiTasks.Await(close); + AssertSucceeded(close, "Close"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.ByteReader, "no byte stream opened"); + var reader = opened.Result!.ByteReader!; + + var received = new List(); + for (int reads = 0; ; reads++) + { + Assert.Less(reads, 100, "reader never signalled end of stream"); + var next = reader.Next(); + yield return UniffiTasks.Await(next); + AssertSucceeded(next, $"Next #{reads}"); + if (!next.Result.HasValue) break; + received.AddRange(next.Result.Value.ToArray()); + } + CollectionAssert.AreEqual(expected, received); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator SendText_LargerThanChunkSize_SpansMultiplePackets() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + // ASCII only, so byte length equals character count. + var builder = new StringBuilder(40000); + for (int i = 0; i < 40000; i++) builder.Append((char)('a' + i % 26)); + var text = builder.ToString(); + var expectedChunks = (text.Length + ChunkSizeBytes - 1) / ChunkSizeBytes; + + var send = sender.Outgoing.SendText(text, TextOptions("bulk")); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendText"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.TextReader, "no text stream opened"); + + var readAll = opened.Result!.TextReader!.ReadAll(); + yield return UniffiTasks.Await(readAll, 20f); + AssertSucceeded(readAll, "ReadAll"); + Assert.AreEqual(text, readAll.Result); + + var expectedPackets = 1 + expectedChunks + 1; + var published = new Expectation(() => sender.PacketsPublished >= expectedPackets, 5f); + yield return published.Wait(); + Assert.IsNull(published.Error, published.Error); + yield return null; + Assert.AreEqual(expectedPackets, sender.PacketsPublished, "header + chunks + trailer"); + Assert.AreEqual(expectedPackets, receiver.PacketsReceived); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator StreamClosed_IsReportedAndOpenStreamCountReturnsToZero() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity)); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + var send = sender.Outgoing.SendText("bye", TextOptions("chat")); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendText"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.TextReader, "no text stream opened"); + + var readAll = opened.Result!.TextReader!.ReadAll(); + yield return UniffiTasks.Await(readAll); + AssertSucceeded(readAll, "ReadAll"); + + var closed = receiver.NextClosedStream(); + yield return UniffiTasks.Await(closed); + AssertSucceeded(closed, "NextClosedStream"); + Assert.IsNotNull(closed.Result, "closed-stream queue ended without a notification"); + Assert.AreEqual(send.Result.Id, closed.Result!.StreamId); + Assert.AreEqual(SenderIdentity, closed.Result.Identity); + + var openCount = receiver.Incoming.OpenStreamCount(); + yield return UniffiTasks.Await(openCount); + AssertSucceeded(openCount, "OpenStreamCount"); + Assert.AreEqual(0UL, openCount.Result); + AssertHealthy(sender, receiver); + } + + [UnityTest, Category("E2E")] + public IEnumerator Incoming_WithPayloadLimit_ReaderFailsWithPayloadTooLarge() + { + using var context = NewContext(); + yield return context.ConnectAll(); + Assert.IsNull(context.ConnectionError, context.ConnectionError); + + using var sender = new UniffiStreamPeer(context.Rooms[0], SenderIdentity, FixedParticipantRegistry.Legacy(ReceiverIdentity)); + using var receiver = new UniffiStreamPeer(context.Rooms[1], ReceiverIdentity, FixedParticipantRegistry.Legacy(SenderIdentity), maxIncomingPayloadBytes: 512); + yield return sender.WaitUntilReachable(receiver); + Assert.Greater(receiver.PingsReceived, 0, "tunnel warm-up never reached the receiver"); + + // The cap is enforced by the receiver, so the send itself succeeds. + var send = sender.Outgoing.SendText(new string('x', 2000), TextOptions("chat")); + yield return UniffiTasks.Await(send); + AssertSucceeded(send, "SendText"); + + var opened = receiver.NextOpenedStream(); + yield return UniffiTasks.Await(opened); + AssertSucceeded(opened, "NextOpenedStream"); + Assert.IsNotNull(opened.Result?.TextReader, "no text stream opened"); + + var readAll = opened.Result!.TextReader!.ReadAll(); + yield return UniffiTasks.Await(readAll); + Assert.IsTrue(readAll.IsCompleted, "ReadAll did not complete"); + Assert.IsTrue(readAll.IsFaulted, "ReadAll succeeded despite exceeding the payload cap"); + Assert.IsInstanceOf(UniffiTasks.Flatten(readAll.Exception)); + + // The failure still closes the stream exactly once. + var closed = receiver.NextClosedStream(); + yield return UniffiTasks.Await(closed); + AssertSucceeded(closed, "NextClosedStream"); + Assert.AreEqual(send.Result.Id, closed.Result?.StreamId); + AssertHealthy(sender, receiver); + } + } +} diff --git a/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs.meta b/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs.meta new file mode 100644 index 00000000..ef918b79 --- /dev/null +++ b/Tests/PlayMode/UniFFI/UniffiDataStreamTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c039212e083914418aa1376378df391a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs b/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs new file mode 100644 index 00000000..cfc8ca15 --- /dev/null +++ b/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using UnityEngine; +using LiveKit.PlayModeTests.Utils; +using Uniffi = uniffi.livekit_uniffi; + +namespace LiveKit.PlayModeTests +{ + /// + /// A participant whose data streams run entirely through the UniFFI data stream managers. + /// + /// The UniFFI managers are transport-agnostic: the outgoing manager hands out encoded + /// livekit.DataPacket bytes and the incoming manager consumes them. There is no UniFFI + /// room yet, so this peer borrows a protobuf-FFI purely as that transport: + /// every packet the outgoing manager produces is published as an opaque user data packet on + /// , and every user packet received on that topic is handed verbatim to + /// the incoming manager. The FFI room never inspects user payloads, so the stream logic on both + /// ends is exercised in UniFFI only. + /// + /// UniFFI tasks complete on the library's tokio threads. All FFI room calls are kept on the main + /// thread by draining the outgoing queue from a coroutine and polling task completion. + /// + internal sealed class UniffiStreamPeer : IDisposable + { + public const string TunnelTopic = "uniffi-stream-tunnel"; + private const string PingTopic = "uniffi-stream-tunnel-ping"; + + public Room Room { get; } + public string Identity { get; } + public Uniffi.OutgoingDataStreamManager Outgoing => _outgoing.Manager; + public Uniffi.IncomingDataStreamManager Incoming => _incoming.Manager; + + /// Packets the outgoing manager produced and this peer published through the room. + public int PacketsPublished { get; private set; } + + /// Tunnel packets received from the room and handed to the incoming manager. + public int PacketsReceived { get; private set; } + + public int PingsReceived { get; private set; } + + /// Transport failures observed while pumping; empty in a healthy run. + public List Errors { get; } = new List(); + + private readonly Uniffi.PolledOutgoingDataStreamManager _outgoing; + private readonly Uniffi.PolledIncomingDataStreamManager _incoming; + private readonly Room.DataDelegate _onData; + private readonly List _polls = new List(); + private GameObject? _host; + private bool _stopping; + + /// + /// What the outgoing manager believes about remote participants. It decides the framing: + /// see . + /// + /// + /// Per-stream payload cap of the incoming manager; null for the library default. + /// + public UniffiStreamPeer( + Room room, + string identity, + Uniffi.RemoteParticipantRegistryDelegate registry, + ulong? maxIncomingPayloadBytes = null) + { + Room = room; + Identity = identity; + _outgoing = Uniffi.LivekitUniffiMethods.PolledOutgoingDataStreamManager(registry); + _incoming = Uniffi.LivekitUniffiMethods.PolledIncomingDataStreamManager(maxIncomingPayloadBytes); + + _onData = (data, participant, kind, topic) => OnDataReceived(data, topic); + room.DataReceived += _onData; + + // The pump outlives any single test yield, so it needs a MonoBehaviour host. + _host = new GameObject($"uniffi-stream-peer-{identity}"); + _host.AddComponent().StartCoroutine(PumpOutgoing()); + } + + /// + /// Next stream opened by a remote participant. Readers of streams obtained through this method + /// are disposed with the peer. + /// + public Task NextOpenedStream() => Track(_incoming.Streams.NextOpenedStream()); + + public Task NextClosedStream() => Track(_incoming.Streams.NextClosedStream()); + + /// + /// Publishes pings until has received one or the timeout elapses. + /// The SFU data path is not necessarily ready right after connect; check + /// on the target afterwards. + /// + public IEnumerator WaitUntilReachable(UniffiStreamPeer target, float timeoutSeconds = 10f) + { + var start = Time.realtimeSinceStartup; + var retryDelay = new WaitForSeconds(0.2f); + while (target.PingsReceived == 0 && Time.realtimeSinceStartup - start < timeoutSeconds) + { + yield return Room.LocalParticipant.PublishData(new byte[] { 1 }, null, true, PingTopic); + yield return retryDelay; + } + } + + private void OnDataReceived(byte[] data, string topic) + { + if (topic == PingTopic) + { + PingsReceived++; + return; + } + if (topic != TunnelTopic) return; + + PacketsReceived++; + // Cheap synchronous enqueue; decoding and reassembly run on the manager's Rust task. + _incoming.Manager.HandlePacketReceived(new ReadOnlyMemory(data), Uniffi.EncryptionType.None); + } + + // Drains the outgoing packet queue and publishes every packet through the FFI room, in order. + private IEnumerator PumpOutgoing() + { + while (!_stopping) + { + var next = Track(_outgoing.Packets.NextPackets()); + while (!next.IsCompleted) yield return null; + + if (next.Status != TaskStatus.RanToCompletion) + { + Errors.Add($"NextPackets {UniffiTasks.Describe(next)}"); + yield break; + } + + var batch = next.Result; + if (batch == null) yield break; // queue closed + + foreach (var packet in batch) + { + if (_stopping || Room.LocalParticipant == null) yield break; + var publish = Room.LocalParticipant.PublishData(packet.ToArray(), null, true, TunnelTopic); + yield return publish; + PacketsPublished++; + if (publish.IsError) Errors.Add($"PublishData failed: {publish.Error}"); + } + } + } + + private Task Track(Task task) + { + lock (_polls) _polls.Add(task); + return task; + } + + public void Dispose() + { + if (_stopping) return; + _stopping = true; + + Room.DataReceived -= _onData; + if (_host != null) + { + UnityEngine.Object.Destroy(_host); + _host = null; + } + + // A pending poll holds a pointer to its queue, so wake every poll with null and let it + // finish before the Rust objects are released. + _outgoing.Packets.Close(); + _incoming.Streams.Close(); + Task[] pending; + lock (_polls) pending = _polls.ToArray(); + try + { + Task.WaitAll(pending, TimeSpan.FromSeconds(2)); + } + catch (AggregateException) + { + // Faulted polls are reported by the tests that issued them. + } + + foreach (var poll in pending) + { + if (poll is Task opened + && opened.Status == TaskStatus.RanToCompletion + && opened.Result != null) + { + opened.Result.Dispose(); + } + } + + _outgoing.Dispose(); + _incoming.Dispose(); + } + } + + /// + /// Fixed answers for the outgoing manager's remote-participant lookups, standing in for the room + /// state a real host would consult. The answers decide the wire framing of every send. + /// + internal sealed class FixedParticipantRegistry : Uniffi.RemoteParticipantRegistryDelegate + { + private readonly string[] _identities; + private readonly int _protocol; + private readonly Uniffi.ClientCapability[] _capabilities; + + private FixedParticipantRegistry(string[] identities, int protocol, Uniffi.ClientCapability[] capabilities) + { + _identities = identities; + _protocol = protocol; + _capabilities = capabilities; + } + + /// Recipients predating data streams v2: forces header, chunks, trailer framing. + public static FixedParticipantRegistry Legacy(params string[] identities) => + new FixedParticipantRegistry(identities, 0, Array.Empty()); + + /// + /// Recipients on client protocol 2 advertising raw-deflate support: enables single-packet + /// inline sends, compressed when that is smaller. + /// + public static FixedParticipantRegistry V2(params string[] identities) => + new FixedParticipantRegistry(identities, 2, new[] { Uniffi.ClientCapability.CompressionDeflateRaw }); + + public int RemoteClientProtocol(string identity) => _protocol; + public Uniffi.ClientCapability[] RemoteCapabilities(string identity) => _capabilities; + public string[] RemoteIdentities() => _identities; + } + + internal static class UniffiTasks + { + /// + /// Yields until completes or the timeout elapses. Never throws; + /// inspect the task afterwards. + /// + public static IEnumerator Await(Task task, float timeoutSeconds = 10f) + { + var start = Time.realtimeSinceStartup; + while (!task.IsCompleted && Time.realtimeSinceStartup - start < timeoutSeconds) + yield return null; + } + + public static bool Succeeded(Task task) => task.Status == TaskStatus.RanToCompletion; + + public static string Describe(Task task) => task.Status switch + { + TaskStatus.RanToCompletion => "completed", + TaskStatus.Faulted => $"faulted: {Flatten(task.Exception)}", + TaskStatus.Canceled => "canceled", + _ => $"did not complete in time ({task.Status})", + }; + + /// The exception a UniFFI task actually failed with, without the AggregateException wrapper. + public static Exception? Flatten(Exception? exception) => + exception is AggregateException aggregate ? aggregate.Flatten().InnerException ?? aggregate : exception; + } + + internal static class UniffiAvailability + { + public const string IgnoreReason = + "liblivekit_uniffi is only built for macOS arm64 (Runtime/Plugins/ffi-macos-arm64)"; + + public static bool IsAvailable => + (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) + && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + } +} diff --git a/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs.meta b/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs.meta new file mode 100644 index 00000000..0f2d0aec --- /dev/null +++ b/Tests/PlayMode/UniFFI/UniffiStreamPeer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd3a01ffef4c54b86ae88cb1872e3b8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: