From 15e8607e695367524b0693476a2b658790ba93ab Mon Sep 17 00:00:00 2001 From: Thad House Date: Tue, 23 Jan 2024 22:14:47 -0800 Subject: [PATCH] NT Value marshaller --- src/ntcore/Generated/RefNetworkTableValue.cs | 24 +++ src/ntcore/Generated/StringEntryImpl.cs | 6 + src/ntcore/Generated/StringPublisher.cs | 2 + src/ntcore/Natives/NtCore.cs | 152 +----------------- .../Natives/RefNetworkTableValueMarshaller.cs | 130 +++++++++++++++ src/ntcore/RefNetworkTableValue.cs | 2 + 6 files changed, 168 insertions(+), 148 deletions(-) create mode 100644 src/ntcore/Natives/RefNetworkTableValueMarshaller.cs diff --git a/src/ntcore/Generated/RefNetworkTableValue.cs b/src/ntcore/Generated/RefNetworkTableValue.cs index ffff1369..8524a78e 100644 --- a/src/ntcore/Generated/RefNetworkTableValue.cs +++ b/src/ntcore/Generated/RefNetworkTableValue.cs @@ -134,9 +134,33 @@ public static RefNetworkTableValue MakeString(string value, long time) return new RefNetworkTableValue(value, time); } + public static RefNetworkTableValue MakeString(ReadOnlySpan value) + { + return new RefNetworkTableValue(value, true); + } + + public static RefNetworkTableValue MakeString(ReadOnlySpan value, long time) + { + return new RefNetworkTableValue(value, time, true); + } + internal readonly ReadOnlySpan m_byteSpan; + internal RefNetworkTableValue(ReadOnlySpan value, bool isString) + { + Type = NetworkTableType.String; + Time = NtCore.Now(); + m_byteSpan = value; + } + + internal RefNetworkTableValue(ReadOnlySpan value, long time, bool isString) + { + Type = NetworkTableType.String; + Time = time; + m_byteSpan = value; + } + internal RefNetworkTableValue(ReadOnlySpan value) { Type = NetworkTableType.Raw; diff --git a/src/ntcore/Generated/StringEntryImpl.cs b/src/ntcore/Generated/StringEntryImpl.cs index 380d943d..8b2a8a5b 100644 --- a/src/ntcore/Generated/StringEntryImpl.cs +++ b/src/ntcore/Generated/StringEntryImpl.cs @@ -113,5 +113,11 @@ public void Unpublish() NtCore.Unpublish(Handle); } + public void Set(ReadOnlySpan value) + { + RefNetworkTableValue ntValue = RefNetworkTableValue.MakeString(value, 0); + NtCore.SetEntryValue(Handle, ntValue); + } + private readonly string m_defaultValue; } diff --git a/src/ntcore/Generated/StringPublisher.cs b/src/ntcore/Generated/StringPublisher.cs index f997777a..e84918d7 100644 --- a/src/ntcore/Generated/StringPublisher.cs +++ b/src/ntcore/Generated/StringPublisher.cs @@ -27,6 +27,8 @@ public interface IStringPublisher : IPublisher */ void Set(string value); + void Set(ReadOnlySpan value); + /** * Publish a new value. * diff --git a/src/ntcore/Natives/NtCore.cs b/src/ntcore/Natives/NtCore.cs index a5ee911a..3d8be15c 100644 --- a/src/ntcore/Natives/NtCore.cs +++ b/src/ntcore/Natives/NtCore.cs @@ -62,165 +62,21 @@ public static NetworkTableValue GetEntryValue(T entry) where T : struct, INtE [LibraryImport("ntcore", EntryPoint = "NT_SetDefaultEntryValue")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I4)] - internal static unsafe partial bool SetDefaultEntryValue(int entry, NetworkTableValueMarshaller.NativeNetworkTableValue* defaultValue); + internal static unsafe partial bool SetDefaultEntryValue(int entry, in RefNetworkTableValue defaultValue); public static unsafe bool SetDefaultEntryValue(T entry, in RefNetworkTableValue defaultValue) where T : struct, INtEntryHandle { - NetworkTableValueMarshaller.NativeNetworkTableValue nativeValue = new NetworkTableValueMarshaller.NativeNetworkTableValue - { - type = defaultValue.Type, - lastChange = defaultValue.Time, - serverTime = 0 - }; - switch (defaultValue.Type) - { - case NetworkTableType.Boolean: - nativeValue.data.valueBoolean = defaultValue.m_structValue.boolValue ? 1 : 0; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.Double: - nativeValue.data.valueDouble = defaultValue.m_structValue.doubleValue; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.String: - byte[] stringData = Encoding.UTF8.GetBytes(defaultValue.m_stringValue!); - fixed (byte* stringPtr = stringData) - { - nativeValue.data.valueString = new(stringPtr, (nuint)stringData.Length); - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.Raw: - fixed (byte* stringPtr = defaultValue.m_byteSpan) - { - nativeValue.data.valueRaw.data = stringPtr; - nativeValue.data.valueRaw.size = (nuint)defaultValue.m_byteSpan.Length; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.BooleanArray: - int[] boolArrayData = new int[defaultValue.m_boolSpan.Length]; - for (int i = 0; i < boolArrayData.Length; i++) - { - boolArrayData[i] = defaultValue.m_boolSpan[i] ? 1 : 0; - } - fixed (int* boolPtr = boolArrayData) - { - nativeValue.data.arrBoolean.arr = boolPtr; - nativeValue.data.arrBoolean.size = (nuint)boolArrayData.Length; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.DoubleArray: - fixed (double* doublePtr = defaultValue.m_doubleSpan) - { - nativeValue.data.arrDouble.arr = doublePtr; - nativeValue.data.arrDouble.size = (nuint)defaultValue.m_doubleSpan.Length; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.IntegerArray: - fixed (long* intPtr = defaultValue.m_longSpan) - { - nativeValue.data.arrInt.arr = intPtr; - nativeValue.data.arrInt.size = (nuint)defaultValue.m_longSpan.Length; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.FloatArray: - fixed (float* floatPtr = defaultValue.m_floatSpan) - { - nativeValue.data.arrFloat.arr = floatPtr; - nativeValue.data.arrFloat.size = (nuint)defaultValue.m_floatSpan.Length; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.StringArray: - // TODO - throw new NotImplementedException(); - case NetworkTableType.Integer: - nativeValue.data.valueInt = defaultValue.m_structValue.longValue; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.Float: - nativeValue.data.valueFloat = defaultValue.m_structValue.floatValue; - return SetDefaultEntryValue(entry.Handle, &nativeValue); - default: - return SetDefaultEntryValue(entry.Handle, &nativeValue); - } + return SetDefaultEntryValue(entry.Handle, defaultValue); } [LibraryImport("ntcore", EntryPoint = "NT_SetEntryValue")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] [return: MarshalAs(UnmanagedType.I4)] - internal static unsafe partial bool SetEntryValue(int entry, NetworkTableValueMarshaller.NativeNetworkTableValue* value); + internal static unsafe partial bool SetEntryValue(int entry, in RefNetworkTableValue value); public static unsafe bool SetEntryValue(T entry, in RefNetworkTableValue value) where T : struct, INtEntryHandle { - NetworkTableValueMarshaller.NativeNetworkTableValue nativeValue = new NetworkTableValueMarshaller.NativeNetworkTableValue - { - type = value.Type, - lastChange = value.Time, - serverTime = 0 - }; - switch (value.Type) - { - case NetworkTableType.Boolean: - nativeValue.data.valueBoolean = value.m_structValue.boolValue ? 1 : 0; - return SetEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.Double: - nativeValue.data.valueDouble = value.m_structValue.doubleValue; - return SetEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.String: - byte[] stringData = Encoding.UTF8.GetBytes(value.m_stringValue!); - fixed (byte* stringPtr = stringData) - { - nativeValue.data.valueString = new(stringPtr, (nuint)stringData.Length); - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.Raw: - fixed (byte* stringPtr = value.m_byteSpan) - { - nativeValue.data.valueRaw.data = stringPtr; - nativeValue.data.valueRaw.size = (nuint)value.m_byteSpan.Length; - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.BooleanArray: - int[] boolArrayData = new int[value.m_boolSpan.Length]; - for (int i = 0; i < boolArrayData.Length; i++) - { - boolArrayData[i] = value.m_boolSpan[i] ? 1 : 0; - } - fixed (int* boolPtr = boolArrayData) - { - nativeValue.data.arrBoolean.arr = boolPtr; - nativeValue.data.arrBoolean.size = (nuint)boolArrayData.Length; - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.DoubleArray: - fixed (double* doublePtr = value.m_doubleSpan) - { - nativeValue.data.arrDouble.arr = doublePtr; - nativeValue.data.arrDouble.size = (nuint)value.m_doubleSpan.Length; - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.IntegerArray: - fixed (long* intPtr = value.m_longSpan) - { - nativeValue.data.arrInt.arr = intPtr; - nativeValue.data.arrInt.size = (nuint)value.m_longSpan.Length; - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.FloatArray: - fixed (float* floatPtr = value.m_floatSpan) - { - nativeValue.data.arrFloat.arr = floatPtr; - nativeValue.data.arrFloat.size = (nuint)value.m_floatSpan.Length; - return SetEntryValue(entry.Handle, &nativeValue); - } - case NetworkTableType.StringArray: - // TODO - throw new NotImplementedException(); - case NetworkTableType.Integer: - nativeValue.data.valueInt = value.m_structValue.longValue; - return SetEntryValue(entry.Handle, &nativeValue); - case NetworkTableType.Float: - nativeValue.data.valueFloat = value.m_structValue.floatValue; - return SetEntryValue(entry.Handle, &nativeValue); - default: - return SetEntryValue(entry.Handle, &nativeValue); - } + return SetEntryValue(entry.Handle, value); } [LibraryImport("ntcore", EntryPoint = "NT_SetEntryFlags")] diff --git a/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs b/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs new file mode 100644 index 00000000..c38c1b6b --- /dev/null +++ b/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using System.Text; +using WPIUtil; +using WPIUtil.Marshal; + +namespace NetworkTables.Natives; + +[CustomMarshaller(typeof(RefNetworkTableValue), MarshalMode.ManagedToUnmanagedIn, typeof(RefNetworkTableValueMarshaller))] +public unsafe ref struct RefNetworkTableValueMarshaller +{ + private ref byte m_toPin; + + private ref byte* m_toAssignPin; + + private bool m_doAssignment; + + private NetworkTableValueMarshaller.NativeNetworkTableValue m_nativeValue; + + public void FromManaged(in RefNetworkTableValue managed) + { + m_nativeValue.type = managed.Type; + m_nativeValue.lastChange = managed.Time; + m_nativeValue.serverTime = 0; + + switch (managed.Type) + { + case NetworkTableType.Boolean: + m_nativeValue.data.valueBoolean = managed.m_structValue.boolValue ? 1 : 0; + break; + case NetworkTableType.Double: + m_nativeValue.data.valueDouble = managed.m_structValue.doubleValue; + break; + case NetworkTableType.Integer: + m_nativeValue.data.valueInt = managed.m_structValue.longValue; + break; + case NetworkTableType.Float: + m_nativeValue.data.valueFloat = managed.m_structValue.floatValue; + break; + case NetworkTableType.Raw: + m_toPin = managed.m_byteSpan.GetPinnableReference(); + m_toAssignPin = m_nativeValue.data.valueRaw.data; + m_nativeValue.data.valueRaw.size = (nuint)managed.m_byteSpan.Length; + m_doAssignment = true; + break; + case NetworkTableType.DoubleArray: + m_toPin = MemoryMarshal.AsBytes(managed.m_doubleSpan).GetPinnableReference(); + m_toAssignPin = (byte*)m_nativeValue.data.arrDouble.arr; + m_nativeValue.data.arrDouble.size = (nuint)managed.m_doubleSpan.Length; + m_doAssignment = true; + break; + case NetworkTableType.IntegerArray: + m_toPin = MemoryMarshal.AsBytes(managed.m_longSpan).GetPinnableReference(); + m_toAssignPin = (byte*)m_nativeValue.data.arrInt.arr; + m_nativeValue.data.arrInt.size = (nuint)managed.m_longSpan.Length; + m_doAssignment = true; + break; + case NetworkTableType.FloatArray: + m_toPin = MemoryMarshal.AsBytes(managed.m_floatSpan).GetPinnableReference(); + m_toAssignPin = (byte*)m_nativeValue.data.arrFloat.arr; + m_nativeValue.data.arrFloat.size = (nuint)managed.m_floatSpan.Length; + m_doAssignment = true; + break; + case NetworkTableType.BooleanArray: + int[] boolArrayData = new int[managed.m_boolSpan.Length]; + for (int i = 0; i < boolArrayData.Length; i++) + { + boolArrayData[i] = managed.m_boolSpan[i] ? 1 : 0; + } + m_toPin = MemoryMarshal.AsBytes(boolArrayData.AsSpan()).GetPinnableReference(); + m_toAssignPin = (byte*)m_nativeValue.data.arrBoolean.arr; + m_nativeValue.data.arrBoolean.size = (nuint)managed.m_boolSpan.Length; + m_doAssignment = true; + break; + case NetworkTableType.String: + byte[] stringArrayData = Encoding.UTF8.GetBytes(managed.m_stringValue!); + m_toPin = MemoryMarshal.AsBytes(stringArrayData.AsSpan()).GetPinnableReference(); + m_nativeValue.data.valueString = new(null, (nuint)stringArrayData.Length); + m_toAssignPin = m_nativeValue.data.valueString.Str; + m_doAssignment = true; + break; + case NetworkTableType.StringArray: + WpiStringMarshaller.WpiStringNative[] strings = new WpiStringMarshaller.WpiStringNative[managed.m_stringSpan.Length]; + + for (int i = 0; i < strings.Length; i++) + { + int len = Encoding.UTF8.GetByteCount(managed.m_stringSpan[i]); + byte* mem = (byte*)NativeMemory.Alloc((nuint)len); + Encoding.UTF8.GetBytes(managed.m_stringSpan[i], new Span(mem, len)); + strings[i] = new WpiStringMarshaller.WpiStringNative(mem, (nuint)len); + } + + m_toPin = MemoryMarshal.AsBytes(strings.AsSpan()).GetPinnableReference(); + m_toAssignPin = (byte*)m_nativeValue.data.arrString.arr; + m_nativeValue.data.arrString.size = (nuint)managed.m_stringSpan.Length; + m_doAssignment = true; + break; + default: + break; + } + } + + public readonly ref readonly byte GetPinnableReference() + { + return ref m_toPin; + } + + public NetworkTableValueMarshaller.NativeNetworkTableValue ToUnmanaged() + { + if (m_doAssignment) + { + m_toAssignPin = (byte*)Unsafe.AsPointer(ref m_toPin); + } + return m_nativeValue; + } + + public void Free() + { + if (m_nativeValue.type == NetworkTableType.StringArray) + { + int len = (int)m_nativeValue.data.arrString.size; + for (int i = 0; i < len; i++) + { + NativeMemory.Free(m_nativeValue.data.arrString.arr[i].Str); + } + } + } +} diff --git a/src/ntcore/RefNetworkTableValue.cs b/src/ntcore/RefNetworkTableValue.cs index b318cddf..13803cc6 100644 --- a/src/ntcore/RefNetworkTableValue.cs +++ b/src/ntcore/RefNetworkTableValue.cs @@ -1,9 +1,11 @@ using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using NetworkTables.Natives; namespace NetworkTables; [StructLayout(LayoutKind.Auto)] +[NativeMarshalling(typeof(RefNetworkTableValueMarshaller))] public readonly ref partial struct RefNetworkTableValue { /**