diff --git a/src/ntcore/Natives/NetworkTableValueMarshaller.cs b/src/ntcore/Natives/NetworkTableValueMarshaller.cs index 5f661a7b..50ffe520 100644 --- a/src/ntcore/Natives/NetworkTableValueMarshaller.cs +++ b/src/ntcore/Natives/NetworkTableValueMarshaller.cs @@ -14,12 +14,12 @@ public static class ReturnFrom { public static NetworkTableValue ConvertToManaged(in NativeNetworkTableValue unmanaged) { - throw new NotImplementedException(); + return new(unmanaged); } public static void Free(NativeNetworkTableValue unmanaged) { - NetworkTableValue.Free(&unmanaged); + NtCore.DisposeValue(&unmanaged); } } diff --git a/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs b/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs index 22031d3d..fdd2f85a 100644 --- a/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs +++ b/src/ntcore/Natives/RefNetworkTableValueMarshaller.cs @@ -114,7 +114,9 @@ public void FromManaged(in RefNetworkTableValue managed, Span callerAlloca if (byteCount > stringSpan.Length) { stringSpan = new byte[byteCount]; - } else { + } + else + { stringSpan = stringSpan[..byteCount]; } int exactBytes = Encoding.UTF8.GetBytes(managed.m_stringValue!, stringSpan); diff --git a/src/ntcore/NetworkTableEvent.cs b/src/ntcore/NetworkTableEvent.cs index 67a2b07d..787e8506 100644 --- a/src/ntcore/NetworkTableEvent.cs +++ b/src/ntcore/NetworkTableEvent.cs @@ -8,7 +8,7 @@ namespace NetworkTables; [NativeMarshalling(typeof(NetworkTableEventMarshaller))] -public readonly struct NetworkTableEvent : INativeArrayFree, INativeFree +public readonly struct NetworkTableEvent : INativeArrayFree { public bool Is(EventFlags kind) => (Flags & kind) != 0; public required NtListener ListenerHandle { get; init; } @@ -19,11 +19,6 @@ namespace NetworkTables; public LogMessage? LogMessage { get; init; } public TimeSyncEventData? TimeSyncData { get; init; } - public static unsafe void Free(NetworkTableEventMarshaller.NativeNetworkTableEvent* ptr) - { - NtCore.DisposeEvent(ptr); - } - public static unsafe void FreeArray(NetworkTableEventMarshaller.NativeNetworkTableEvent* ptr, int len) { NtCore.DisposeEventArray(ptr, (nuint)len); diff --git a/src/ntcore/NetworkTableValue.cs b/src/ntcore/NetworkTableValue.cs index 14ba77d0..d09aeb2b 100644 --- a/src/ntcore/NetworkTableValue.cs +++ b/src/ntcore/NetworkTableValue.cs @@ -1,5 +1,7 @@ +using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using System.Text; using NetworkTables.Natives; using WPIUtil.Marshal; @@ -7,13 +9,8 @@ namespace NetworkTables; [NativeMarshalling(typeof(NetworkTableValueMarshaller))] [StructLayout(LayoutKind.Auto)] -public readonly partial struct NetworkTableValue : INativeArrayFree, INativeFree +public readonly partial struct NetworkTableValue : INativeArrayFree { - public static unsafe void Free(NetworkTableValueMarshaller.NativeNetworkTableValue* ptr) - { - NtCore.DisposeValue(ptr); - } - public static unsafe void FreeArray(NetworkTableValueMarshaller.NativeNetworkTableValue* ptr, int len) { NtCore.DisposeValueArray(ptr, (nuint)len); @@ -205,4 +202,66 @@ public static implicit operator RefNetworkTableValue(in NetworkTableValue value) _ => RefNetworkTableValue.MakeUnassigned(value.Time), }; } + + internal unsafe NetworkTableValue(in NetworkTableValueMarshaller.NativeNetworkTableValue value) + { + Time = value.lastChange; + ServerTime = value.serverTime; + Type = value.type; + + switch (Type) + { + case NetworkTableType.Boolean: + m_structValue = new(value.data.valueBoolean != 0); + break; + case NetworkTableType.Integer: + m_structValue = new(value.data.valueInt); + break; + case NetworkTableType.Float: + m_structValue = new(value.data.valueFloat); + break; + case NetworkTableType.Double: + m_structValue = new(value.data.valueDouble); + break; + case NetworkTableType.String: + m_objectValue = value.data.valueString.ConvertToString(); + break; + case NetworkTableType.Raw: + byte[] bytes = new byte[checked((int)value.data.valueRaw.size)]; + new ReadOnlySpan(value.data.valueRaw.data, bytes.Length).CopyTo(bytes); + m_objectValue = bytes; + break; + case NetworkTableType.BooleanArray: + bool[] boolArr = new bool[checked((int)value.data.arrBoolean.size)]; + for (int i = 0; i < boolArr.Length; i++) + { + boolArr[i] = value.data.valueRaw.data[i] != 0; + } + m_objectValue = boolArr; + break; + case NetworkTableType.IntegerArray: + long[] longArr = new long[checked((int)value.data.arrInt.size)]; + new ReadOnlySpan(value.data.arrInt.arr, longArr.Length).CopyTo(longArr); + m_objectValue = longArr; + break; + case NetworkTableType.FloatArray: + float[] floatArr = new float[checked((int)value.data.arrFloat.size)]; + new ReadOnlySpan(value.data.arrFloat.arr, floatArr.Length).CopyTo(floatArr); + m_objectValue = floatArr; + break; + case NetworkTableType.DoubleArray: + double[] doubleArr = new double[checked((int)value.data.arrDouble.size)]; + new ReadOnlySpan(value.data.arrDouble.arr, doubleArr.Length).CopyTo(doubleArr); + m_objectValue = doubleArr; + break; + case NetworkTableType.StringArray: + string[] stringArr = new string[checked((int)value.data.arrString.size)]; + for (int i = 0; i < stringArr.Length; i++) + { + stringArr[i] = value.data.arrString.arr[i].ConvertToString(); + } + m_objectValue = stringArr; + break; + } + } } diff --git a/src/ntcore/TopicInfo.cs b/src/ntcore/TopicInfo.cs index 88807de9..4bac298d 100644 --- a/src/ntcore/TopicInfo.cs +++ b/src/ntcore/TopicInfo.cs @@ -8,7 +8,7 @@ namespace NetworkTables.Natives; [NativeMarshalling(typeof(TopicInfoMarshaller))] [StructLayout(LayoutKind.Auto)] -public record struct TopicInfo(NtTopic TopicHandle, string Name, NetworkTableType Type, string TypeStr, string Properties) : INativeArrayFree, INativeFree +public record struct TopicInfo(NtTopic TopicHandle, string Name, NetworkTableType Type, string TypeStr, string Properties) : INativeArrayFree { public readonly Topic GetTopic(NetworkTableInstance inst) { @@ -34,12 +34,20 @@ public static class ReturnFrom { public static TopicInfo ConvertToManaged(in NativeTopicInfo unmanaged) { - return ReturnInArray.ConvertToManaged(unmanaged); + return new TopicInfo + { + TopicHandle = new NtTopic(unmanaged.topic), + Name = unmanaged.name.ConvertToString(), + Type = unmanaged.type, + TypeStr = unmanaged.typeStr.ConvertToString(), + Properties = unmanaged.properties.ConvertToString(), + + }; } public static void Free(NativeTopicInfo unmanaged) { - TopicInfo.Free(&unmanaged); + NtCore.DisposeTopicInfo(&unmanaged); } } @@ -47,15 +55,7 @@ public static class ReturnInArray { public static TopicInfo ConvertToManaged(in NativeTopicInfo unmanaged) { - return new TopicInfo - { - TopicHandle = new NtTopic(unmanaged.topic), - Name = unmanaged.name.ConvertToString(), - Type = unmanaged.type, - TypeStr = unmanaged.typeStr.ConvertToString(), - Properties = unmanaged.properties.ConvertToString(), - - }; + return ReturnFrom.ConvertToManaged(unmanaged); } public static NativeTopicInfo ConvertToUnmanaged(in TopicInfo managed) diff --git a/src/wpiutil/Marshal/INativeFree.cs b/src/wpiutil/Marshal/INativeFree.cs deleted file mode 100644 index dd7423e4..00000000 --- a/src/wpiutil/Marshal/INativeFree.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WPIUtil.Marshal; - -public interface INativeFree where T : unmanaged -{ - static abstract unsafe void Free(T* ptr); -} diff --git a/test/ntcore.test/RefNetworkTableValueMarshallerTest.cs b/test/ntcore.test/RefNetworkTableValueMarshallerTest.cs index fb500801..c6187816 100644 --- a/test/ntcore.test/RefNetworkTableValueMarshallerTest.cs +++ b/test/ntcore.test/RefNetworkTableValueMarshallerTest.cs @@ -7,7 +7,6 @@ namespace NetworkTables.Test; public class RefNetworkTableValueMarshallerTest { - private unsafe delegate void DataInDelegate(NetworkTableValueMarshaller.NativeNetworkTableValue* value, void* pinned); private static unsafe void HandleInMarshal(in RefNetworkTableValue value, DataInDelegate callback) @@ -35,17 +34,18 @@ private static unsafe void HandleInMarshal(in RefNetworkTableValue value, DataIn } - [Fact] public unsafe void TestBool() { - HandleInMarshal(RefNetworkTableValue.MakeBoolean(false), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeBoolean(false), (v, pinned) => + { Assert.Equal(NetworkTableType.Boolean, v->type); Assert.Equal(0, v->data.valueBoolean); Assert.True(pinned == null); }); - HandleInMarshal(RefNetworkTableValue.MakeBoolean(true), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeBoolean(true), (v, pinned) => + { Assert.Equal(NetworkTableType.Boolean, v->type); Assert.Equal(1, v->data.valueBoolean); Assert.True(pinned == null); @@ -55,13 +55,15 @@ public unsafe void TestBool() [Fact] public unsafe void TestInt() { - HandleInMarshal(RefNetworkTableValue.MakeInteger(42), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeInteger(42), (v, pinned) => + { Assert.Equal(NetworkTableType.Integer, v->type); Assert.Equal(42, v->data.valueInt); Assert.True(pinned == null); }); - HandleInMarshal(RefNetworkTableValue.MakeInteger(0), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeInteger(0), (v, pinned) => + { Assert.Equal(NetworkTableType.Integer, v->type); Assert.Equal(0, v->data.valueInt); Assert.True(pinned == null); @@ -71,13 +73,15 @@ public unsafe void TestInt() [Fact] public unsafe void TestDouble() { - HandleInMarshal(RefNetworkTableValue.MakeDouble(42.0), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeDouble(42.0), (v, pinned) => + { Assert.Equal(NetworkTableType.Double, v->type); Assert.Equal(42.0, v->data.valueDouble); Assert.True(pinned == null); }); - HandleInMarshal(RefNetworkTableValue.MakeDouble(56.5), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeDouble(56.5), (v, pinned) => + { Assert.Equal(NetworkTableType.Double, v->type); Assert.Equal(56.5, v->data.valueDouble); Assert.True(pinned == null); @@ -87,13 +91,15 @@ public unsafe void TestDouble() [Fact] public unsafe void TestFloat() { - HandleInMarshal(RefNetworkTableValue.MakeFloat(42.0f), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeFloat(42.0f), (v, pinned) => + { Assert.Equal(NetworkTableType.Float, v->type); Assert.Equal(42.0f, v->data.valueFloat, 1e-9); Assert.True(pinned == null); }); - HandleInMarshal(RefNetworkTableValue.MakeFloat(56.5f), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeFloat(56.5f), (v, pinned) => + { Assert.Equal(NetworkTableType.Float, v->type); Assert.Equal(56.5f, v->data.valueFloat, 1e-9); Assert.True(pinned == null); @@ -103,12 +109,14 @@ public unsafe void TestFloat() [Fact] public unsafe void TestUnassigned() { - HandleInMarshal(RefNetworkTableValue.MakeUnassigned(), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeUnassigned(), (v, pinned) => + { Assert.Equal(NetworkTableType.Unassigned, v->type); Assert.True(pinned == null); }); - HandleInMarshal(RefNetworkTableValue.MakeUnassigned(), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeUnassigned(), (v, pinned) => + { Assert.Equal(NetworkTableType.Unassigned, v->type); Assert.True(pinned == null); }); @@ -120,7 +128,8 @@ public unsafe void TestRaw() Span raw = stackalloc byte[3]; "abc"u8.CopyTo(raw); void* ptr = Unsafe.AsPointer(ref raw.GetPinnableReference()); - HandleInMarshal(RefNetworkTableValue.MakeRaw(raw), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeRaw(raw), (v, pinned) => + { Assert.Equal(NetworkTableType.Raw, v->type); Assert.Equal((nuint)3, v->data.valueRaw.size); ReadOnlySpan consumed = new ReadOnlySpan(v->data.valueRaw.data, (int)v->data.valueRaw.size); @@ -129,7 +138,8 @@ public unsafe void TestRaw() Assert.True(pinned == v->data.valueRaw.data); }); - HandleInMarshal(RefNetworkTableValue.MakeRaw(new()), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeRaw(new()), (v, pinned) => + { Assert.Equal(NetworkTableType.Raw, v->type); Assert.Equal((nuint)0, v->data.valueRaw.size); Assert.True(pinned == null); @@ -143,7 +153,8 @@ public unsafe void TestString() Span raw = stackalloc byte[3]; "abc"u8.CopyTo(raw); void* ptr = Unsafe.AsPointer(ref raw.GetPinnableReference()); - HandleInMarshal(RefNetworkTableValue.MakeString(raw), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeString(raw), (v, pinned) => + { Assert.Equal(NetworkTableType.String, v->type); Assert.Equal((nuint)3, v->data.valueString.Len); ReadOnlySpan consumed = new ReadOnlySpan(v->data.valueString.Str, (int)v->data.valueString.Len); @@ -152,7 +163,8 @@ public unsafe void TestString() Assert.True(pinned == v->data.valueString.Str); }); - HandleInMarshal(RefNetworkTableValue.MakeString("string"), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeString("string"), (v, pinned) => + { Assert.Equal(NetworkTableType.String, v->type); Assert.Equal((nuint)6, v->data.valueString.Len); ReadOnlySpan consumed = new ReadOnlySpan(v->data.valueString.Str, (int)v->data.valueString.Len); @@ -161,7 +173,8 @@ public unsafe void TestString() }); var longString = new string('a', 512); - HandleInMarshal(RefNetworkTableValue.MakeString(longString), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeString(longString), (v, pinned) => + { Assert.Equal(NetworkTableType.String, v->type); Assert.Equal((nuint)512, v->data.valueString.Len); ReadOnlySpan consumed = new ReadOnlySpan(v->data.valueString.Str, (int)v->data.valueString.Len); @@ -170,7 +183,8 @@ public unsafe void TestString() Assert.True(pinned == v->data.valueString.Str); }); - HandleInMarshal(RefNetworkTableValue.MakeString((string)null!), (v, pinned) => { + HandleInMarshal(RefNetworkTableValue.MakeString((string)null!), (v, pinned) => + { Assert.Equal(NetworkTableType.String, v->type); Assert.Equal((nuint)0, v->data.valueString.Len); Assert.True(pinned == null);