Skip to content

Commit

Permalink
(ForNeVeR#354) Runtime: introduce the CPtr type
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR authored and kant2002 committed Apr 11, 2023
1 parent cd3bcc0 commit b7aa5e0
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 53 deletions.
35 changes: 35 additions & 0 deletions Cesium.Runtime/CPtr.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace Cesium.Runtime;

/// <summary>A class encapsulating an opaque pointer (aka <code>void*</code> in C).</summary>
public unsafe readonly struct CPtr
{
private readonly long _value;

private CPtr(long value)
{
_value = value;
}

public static implicit operator CPtr(void* ptr) => new((long)ptr);
public void* AsPtr() => (void*)_value;
public TResult* AsPtr<TResult>() where TResult : unmanaged => (TResult*)_value;
public IntPtr AsIntPtr() => (IntPtr)AsPtr();
}


/// <summary>A class encapsulating an object pointer.</summary>
/// <typeparam name="T">Type this pointer may be resolved to.</typeparam>
public unsafe readonly struct CPtr<T> where T : unmanaged
{
private readonly long _value;

private CPtr(long value)
{
_value = value;
}

public static implicit operator CPtr<T>(T* ptr) => new((long)ptr);
public T* AsPtr() => (T*)_value;
public TResult* AsPtr<TResult>() where TResult : unmanaged => (TResult*)_value;
public IntPtr AsIntPtr() => (IntPtr)AsPtr();
}
25 changes: 25 additions & 0 deletions Cesium.Runtime/RuntimeHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,29 @@ public static void FreeGlobalField(void* field)
{
Marshal.FreeHGlobal((IntPtr)field);
}

internal static string? Unmarshal(byte* str)
{
#if NETSTANDARD
Encoding encoding = Encoding.UTF8;
int byteLength = 0;
byte* search = str;
while (*search != '\0')
{
byteLength++;
search++;
}

int stringLength = encoding.GetCharCount(str, byteLength);
string s = new string('\0', stringLength);
fixed (char* pTempChars = s)
{
encoding.GetChars(str, byteLength, pTempChars, stringLength);
}

return s;
#else
return Marshal.PtrToStringUTF8((nint)str);
#endif
}
}
50 changes: 10 additions & 40 deletions Cesium.Runtime/StdIoFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
using System.Runtime.InteropServices;
#if NETSTANDARD
using System.Text;
#endif

namespace Cesium.Runtime;

/// <summary>
/// Functions declared in the stdio.h
/// </summary>
public unsafe static class StdIoFunctions
{
public static void PutS(byte* str)
public static void PutS(CPtr<byte> str)
{
try
{
Console.Write(Unmarshal(str));
Console.Write(RuntimeHelpers.Unmarshal(str.AsPtr()));
// return 0; // TODO[#156]: Uncomment
}
catch (Exception) // TODO[#154]: Exception handling.
Expand All @@ -24,9 +19,9 @@ public static void PutS(byte* str)
}
}

public static int PrintF(byte* str, void* varargs)
public static int PrintF(CPtr<byte> str, CPtr varargs)
{
var formatString = Unmarshal(str);
var formatString = RuntimeHelpers.Unmarshal(str.AsPtr());
if (formatString == null)
{
return -1;
Expand All @@ -52,42 +47,42 @@ public static int PrintF(byte* str, void* varargs)
switch (formatSpecifier)
{
case "s":
string? stringValue = Unmarshal((byte*)((long*)varargs)[consumedArgs]);
string? stringValue = RuntimeHelpers.Unmarshal((byte*)varargs.AsPtr<long>()[consumedArgs]);
Console.Write(stringValue);
consumedBytes += stringValue?.Length ?? 0;
consumedArgs++;
break;
case "c":
Console.Write((char)(byte)((long*)varargs)[consumedArgs]);
Console.Write((char)(byte)varargs.AsPtr<long>()[consumedArgs]);
consumedBytes++;
consumedArgs++;
break;
case "d":
case "li":
case "i":
int intValue = (int)((long*)varargs)[consumedArgs];
int intValue = (int)varargs.AsPtr<long>()[consumedArgs];
var intValueString = intValue.ToString();
Console.Write(intValueString);
consumedBytes += intValueString.Length;
consumedArgs++;
break;
case "u":
case "lu":
uint uintValue = (uint)((long*)varargs)[consumedArgs];
uint uintValue = (uint)varargs.AsPtr<long>()[consumedArgs];
var uintValueString = uintValue.ToString();
Console.Write(uintValueString);
consumedBytes += uintValueString.Length;
consumedArgs++;
break;
case "f":
var floatNumber = ((double*)varargs)[consumedArgs];
var floatNumber = varargs.AsPtr<double>()[consumedArgs];
string floatNumberString = floatNumber.ToString("F6");
Console.Write(floatNumberString);
consumedBytes += floatNumberString.Length;
consumedArgs++;
break;
case "p":
nint pointerValue = ((nint*)varargs)[consumedArgs];
nint pointerValue = varargs.AsPtr<nint>()[consumedArgs];
string pointerValueString = pointerValue.ToString("X");
Console.Write(pointerValueString);
consumedBytes += pointerValueString.Length;
Expand All @@ -105,29 +100,4 @@ public static int PrintF(byte* str, void* varargs)
Console.Write(remainderString);
return consumedBytes + remainderString.Length;
}

internal static string? Unmarshal(byte* str)
{
#if NETSTANDARD
Encoding encoding = Encoding.UTF8;
int byteLength = 0;
byte* search = str;
while (*search != '\0')
{
byteLength++;
search++;
}

int stringLength = encoding.GetCharCount(str, byteLength);
string s = new string('\0', stringLength);
fixed (char* pTempChars = s)
{
encoding.GetChars(str, byteLength, pTempChars, stringLength);
}

return s;
#else
return Marshal.PtrToStringUTF8((nint)str);
#endif
}
}
8 changes: 3 additions & 5 deletions Cesium.Runtime/StdLibFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Runtime.InteropServices;

namespace Cesium.Runtime;

/// <summary>
Expand All @@ -8,7 +6,7 @@ namespace Cesium.Runtime;
public unsafe static class StdLibFunctions
{
public const int RAND_MAX = 0x7FFFFFFF;
private static System.Random shared = new();
private static Random shared = new();

public static int Abs(int value)
{
Expand All @@ -31,9 +29,9 @@ public static void SRand(uint seed)
shared = new Random((int)seed);
}

public static int System(byte* command)
public static int System(CPtr<byte> command)
{
switch (StdIoFunctions.Unmarshal(command))
switch (RuntimeHelpers.Unmarshal(command.AsPtr()))
{
case "cls":
case "clear":
Expand Down
17 changes: 10 additions & 7 deletions Cesium.Runtime/StringFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
namespace Cesium.Runtime;

#if NETSTANDARD
using System.Text;
#endif
#if NET6_0
using System.Runtime.InteropServices;
using System.Diagnostics;
#endif

namespace Cesium.Runtime;

/// <summary>
/// Functions declared in the string.h
/// </summary>
public unsafe static class StringFunctions
{
public static uint StrLen(byte* str)
public static uint StrLen(CPtr<byte> str)
{
#if NETSTANDARD
Encoding encoding = Encoding.UTF8;
int byteLength = 0;
byte* search = str;
byte* search = str.AsPtr();
while (*search != '\0')
{
byteLength++;
search++;
}

int stringLength = encoding.GetCharCount(str, byteLength);
int stringLength = encoding.GetCharCount(str.AsPtr(), byteLength);
return (uint)stringLength;
#else
return (uint)(Marshal.PtrToStringUTF8((nint)str)?.Length ?? 0);
return (uint)(Marshal.PtrToStringUTF8(str.AsIntPtr())?.Length ?? 0);
#endif
}
}
2 changes: 2 additions & 0 deletions docs/architecture-sets.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ Cesium aims to support the following architecture sets:
- **Wide** architecture uses the fixed pointer size of 64 bits on all computers. This allows it to cover all the features of the C standard, for the cost of some redundancy on 32-bit architectures, and slightly different method signatures for .NET interop.

**This architecture is machine-independent** and results in producing of an Any CPU-targeting assembly.

Specifically for the **wide** architecture, the type `CPtr` was introduced. It corresponds to a 64-bit pointer universally, and the **wide** architecture uses it in place of normal pointer types everywhere in the API. For cross-compatibility with any architecture, this type is also used in the Cesium.Runtime library.
6 changes: 5 additions & 1 deletion docs/type-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ This should be kept in sync with section **6.7.2 Type Specifiers** of the actual
| `double _Complex` | N/A |
| `long double _Complex` | N/A |

All pointer types are mapped to the CLI pointers of the corresponding type.
All the pointer types are mapped to the CLI pointers of the corresponding type on **dynamic**, **32b** and **64b** architecture sets.

For the **wide** architecture set, the pointers are mapped to `Cesium.Runtime.CPtr` (for `void*`) and `Cesium.Runtime.CPtr<T>` (for typed pointers). This architecture requires 64-bit pointer types everywhere, and thus can't use the CLI pointer types.

To be compatible with both **wide** and other architecture sets, the Cesium.Runtime library uses `CPtr` in all of its standard APIs.

0 comments on commit b7aa5e0

Please sign in to comment.