Skip to content

Commit 402f68c

Browse files
committed
Adds pointer methods to native strings
Easier interop
1 parent f2d591b commit 402f68c

File tree

3 files changed

+71
-21
lines changed

3 files changed

+71
-21
lines changed

src/FRC-Utilities/CachedNativeString.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,32 @@ namespace FRC
88
/// Holds a Cached UTF8 string to pass to native code. There is no way to initialize or dispose of this
99
/// string from user code.
1010
/// </summary>
11-
[StructLayout(LayoutKind.Sequential)]
12-
public struct CachedNativeString
11+
public unsafe struct CachedNativeString
1312
{
1413
/// <summary>
1514
/// Pointer to this string, null terminated. Do not modify this pointer.
1615
/// </summary>
17-
public IntPtr Buffer;
16+
public byte* Buffer;
1817
/// <summary>
1918
/// The Length of this string without the null terminator;
2019
/// </summary>
2120
public UIntPtr Length;
2221

22+
private string m_string;
23+
2324
internal CachedNativeString(string vStr)
2425
{
26+
m_string = vStr;
2527
unsafe
2628
{
2729
fixed (char* str = vStr)
2830
{
2931
var encoding = Encoding.UTF8;
3032
int bytes = encoding.GetByteCount(str, vStr.Length);
31-
Buffer = Marshal.AllocHGlobal((bytes + 1) * sizeof(byte));
33+
Buffer = (byte*)Marshal.AllocHGlobal((bytes + 1) * sizeof(byte));
3234
Length = (UIntPtr)bytes;
33-
byte* data = (byte*)Buffer.ToPointer();
34-
encoding.GetBytes(str, vStr.Length, data, bytes);
35-
data[bytes] = 0;
35+
encoding.GetBytes(str, vStr.Length, Buffer, bytes);
36+
Buffer[bytes] = 0;
3637
}
3738
}
3839
}
@@ -43,7 +44,7 @@ internal CachedNativeString(string vStr)
4344
/// <returns>The contained string</returns>
4445
public override string ToString()
4546
{
46-
return UTF8String.ReadUTF8String(Buffer, Length);
47+
return m_string;
4748
}
4849
}
4950
}

src/FRC-Utilities/DisposableNativeString.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,47 @@ namespace FRC
99
/// <summary>
1010
/// Holds a UTF8 string to pass to native code. Make sure to properly dispose of this to avoid a memory leak
1111
/// </summary>
12-
[StructLayout(LayoutKind.Sequential)]
13-
public struct DisposableNativeString : IDisposable
12+
public unsafe struct DisposableNativeString : IDisposable
1413
{
1514
/// <summary>
1615
/// Pointer to this string, null terminated. Do not modify this pointer.
1716
/// </summary>
18-
public IntPtr Buffer;
17+
public byte* Buffer;
1918
/// <summary>
2019
/// The Length of this string without the null terminator;
2120
/// </summary>
2221
public UIntPtr Length;
2322

23+
private string m_string;
24+
2425
/// <summary>
2526
/// Creates a new UTF8 string from a managed string
2627
/// </summary>
2728
/// <param name="vStr">The managed string</param>
2829
public DisposableNativeString(string vStr)
2930
{
31+
m_string = vStr;
3032
unsafe
3133
{
3234
fixed (char* str = vStr)
3335
{
3436
var encoding = Encoding.UTF8;
3537
int bytes = encoding.GetByteCount(str, vStr.Length);
36-
Buffer = Marshal.AllocHGlobal((bytes + 1) * sizeof(byte));
38+
Buffer = (byte*)Marshal.AllocHGlobal((bytes + 1) * sizeof(byte));
3739
Length = (UIntPtr)bytes;
38-
byte* data = (byte*)Buffer.ToPointer();
39-
encoding.GetBytes(str, vStr.Length, data, bytes);
40-
data[bytes] = 0;
40+
encoding.GetBytes(str, vStr.Length, Buffer, bytes);
41+
Buffer[bytes] = 0;
4142
}
4243
}
4344
}
4445

4546
/// <summary>
4647
/// Disposes of the native string
4748
/// </summary>
48-
public void Dispose()
49+
public unsafe void Dispose()
4950
{
50-
Marshal.FreeHGlobal(Buffer);
51+
Marshal.FreeHGlobal((IntPtr)Buffer);
52+
m_string = null;
5153
}
5254

5355
/// <summary>
@@ -56,7 +58,7 @@ public void Dispose()
5658
/// <returns>The contained string</returns>
5759
public override string ToString()
5860
{
59-
return UTF8String.ReadUTF8String(Buffer, Length);
61+
return m_string;
6062
}
6163
}
6264
}

src/FRC-Utilities/UTF8String.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ public static class UTF8String
1212
{
1313
private static readonly ConcurrentDictionary<string, CachedNativeString> s_stringCache = new ConcurrentDictionary<string, CachedNativeString>();
1414

15+
private static Func<string, CachedNativeString> CacheCreateFunc = (s) => new CachedNativeString(s);
16+
1517
/// <summary>
1618
/// Creates a UTF8 string that will be cached, using the already cached string is already created
1719
/// </summary>
1820
/// <param name="str">The string to cache</param>
1921
/// <returns>The cached UTF8 string</returns>
2022
public static CachedNativeString CreateCachedUTF8String(string str)
2123
{
22-
return s_stringCache.GetOrAdd(str, s => new CachedNativeString(s));
24+
return s_stringCache.GetOrAdd(str, CacheCreateFunc);
2325
}
2426

2527
/// <summary>
@@ -57,12 +59,36 @@ public static string ReadUTF8String(IntPtr str, UIntPtr size)
5759
#else
5860
int iSize = (int)size.ToUInt64();
5961
byte[] data = new byte[iSize];
60-
Marshal.Copy(str, data, 0, iSize);
62+
for (int i = 0; i < iSize; i++)
63+
{
64+
data[i] = ((byte*)str)[i];
65+
}
6166
return Encoding.UTF8.GetString(data);
6267
#endif
6368
}
6469
}
6570

71+
/// <summary>
72+
/// Reads a UTF8 string from a native pointer.
73+
/// </summary>
74+
/// <param name="str">The pointer to read from</param>
75+
/// <param name="size">The length of the string</param>
76+
/// <returns>The managed string</returns>
77+
public static unsafe string ReadUTF8String(byte* str, UIntPtr size)
78+
{
79+
#if (!NET451)
80+
return Encoding.UTF8.GetString(str, (int)size);
81+
#else
82+
int iSize = (int)size.ToUInt64();
83+
byte[] data = new byte[iSize];
84+
for (int i = 0; i < iSize; i++)
85+
{
86+
data[i] = str[i];
87+
}
88+
return Encoding.UTF8.GetString(data);
89+
#endif
90+
}
91+
6692
/// <summary>
6793
/// Reads a UTF8 string from a null termincated native pointer
6894
/// </summary>
@@ -82,9 +108,30 @@ public static string ReadUTF8String(IntPtr str)
82108
}
83109
count++;
84110
}
85-
111+
86112
return ReadUTF8String(str, (UIntPtr)count);
87113
}
88114
}
115+
116+
/// <summary>
117+
/// Reads a UTF8 string from a null termincated native pointer
118+
/// </summary>
119+
/// <param name="str">The pointer to read from (must be null terminated)</param>
120+
/// <returns>The managed string</returns>
121+
public static unsafe string ReadUTF8String(byte* str)
122+
{
123+
byte* data = str;
124+
int count = 0;
125+
while (true)
126+
{
127+
if (data[count] == 0)
128+
{
129+
break;
130+
}
131+
count++;
132+
}
133+
134+
return ReadUTF8String(str, (UIntPtr)count);
135+
}
89136
}
90137
}

0 commit comments

Comments
 (0)