Skip to content

Commit

Permalink
NT Value marshaller
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Jan 24, 2024
1 parent 7977ae1 commit 15e8607
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 148 deletions.
24 changes: 24 additions & 0 deletions src/ntcore/Generated/RefNetworkTableValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,33 @@ public static RefNetworkTableValue MakeString(string value, long time)
return new RefNetworkTableValue(value, time);
}

public static RefNetworkTableValue MakeString(ReadOnlySpan<byte> value)
{
return new RefNetworkTableValue(value, true);
}

public static RefNetworkTableValue MakeString(ReadOnlySpan<byte> value, long time)
{
return new RefNetworkTableValue(value, time, true);
}


internal readonly ReadOnlySpan<byte> m_byteSpan;

internal RefNetworkTableValue(ReadOnlySpan<byte> value, bool isString)
{
Type = NetworkTableType.String;
Time = NtCore.Now();
m_byteSpan = value;
}

internal RefNetworkTableValue(ReadOnlySpan<byte> value, long time, bool isString)
{
Type = NetworkTableType.String;
Time = time;
m_byteSpan = value;
}

internal RefNetworkTableValue(ReadOnlySpan<byte> value)
{
Type = NetworkTableType.Raw;
Expand Down
6 changes: 6 additions & 0 deletions src/ntcore/Generated/StringEntryImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,11 @@ public void Unpublish()
NtCore.Unpublish(Handle);
}

public void Set(ReadOnlySpan<byte> value)
{
RefNetworkTableValue ntValue = RefNetworkTableValue.MakeString(value, 0);
NtCore.SetEntryValue(Handle, ntValue);
}

private readonly string m_defaultValue;
}
2 changes: 2 additions & 0 deletions src/ntcore/Generated/StringPublisher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public interface IStringPublisher : IPublisher
*/
void Set(string value);

void Set(ReadOnlySpan<byte> value);

/**
* Publish a new value.
*
Expand Down
152 changes: 4 additions & 148 deletions src/ntcore/Natives/NtCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,165 +62,21 @@ public static NetworkTableValue GetEntryValue<T>(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>(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>(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")]
Expand Down
130 changes: 130 additions & 0 deletions src/ntcore/Natives/RefNetworkTableValueMarshaller.cs
Original file line number Diff line number Diff line change
@@ -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<byte>(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);
}
}
}
}
2 changes: 2 additions & 0 deletions src/ntcore/RefNetworkTableValue.cs
Original file line number Diff line number Diff line change
@@ -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
{
/**
Expand Down

0 comments on commit 15e8607

Please sign in to comment.