Skip to content

Commit 946b9a0

Browse files
committed
Add template to generate PInvoke-based implementation
1 parent 908f0f0 commit 946b9a0

File tree

5 files changed

+523
-670
lines changed

5 files changed

+523
-670
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
using System;
2+
using System.Linq;
3+
using System.Runtime.InteropServices;
4+
using System.Text;
5+
6+
namespace Python.Runtime.Native
7+
{
8+
/// <summary>
9+
/// Abstract class defining boiler plate methods that
10+
/// Custom Marshalers will use.
11+
/// </summary>
12+
internal abstract class MarshalerBase : ICustomMarshaler
13+
{
14+
#if UCS2 && PYTHON2
15+
internal static Encoding PyEncoding = Encoding.Unicode;
16+
internal static int UCS = 2;
17+
#else
18+
internal static Encoding PyEncoding = Encoding.UTF32;
19+
internal static int UCS = 4;
20+
#endif
21+
22+
public object MarshalNativeToManaged(IntPtr pNativeData)
23+
{
24+
throw new NotImplementedException();
25+
}
26+
27+
public abstract IntPtr MarshalManagedToNative(object managedObj);
28+
29+
public void CleanUpNativeData(IntPtr pNativeData)
30+
{
31+
Marshal.FreeHGlobal(pNativeData);
32+
}
33+
34+
public void CleanUpManagedData(object managedObj)
35+
{
36+
// Let GC deal with it
37+
}
38+
39+
public int GetNativeDataSize()
40+
{
41+
return IntPtr.Size;
42+
}
43+
}
44+
45+
46+
/// <summary>
47+
/// Custom Marshaler to deal with Managed String to Native
48+
/// conversion differences on UCS2/UCS4.
49+
/// </summary>
50+
internal class UcsMarshaler : MarshalerBase
51+
{
52+
private static readonly MarshalerBase Instance = new UcsMarshaler();
53+
54+
public override IntPtr MarshalManagedToNative(object managedObj)
55+
{
56+
var s = managedObj as string;
57+
58+
if (s == null)
59+
{
60+
return IntPtr.Zero;
61+
}
62+
63+
byte[] bStr = PyEncoding.GetBytes(s + "\0");
64+
IntPtr mem = Marshal.AllocHGlobal(bStr.Length);
65+
try
66+
{
67+
Marshal.Copy(bStr, 0, mem, bStr.Length);
68+
}
69+
catch (Exception)
70+
{
71+
Marshal.FreeHGlobal(mem);
72+
throw;
73+
}
74+
75+
return mem;
76+
}
77+
78+
public static ICustomMarshaler GetInstance(string cookie)
79+
{
80+
return Instance;
81+
}
82+
83+
public static string PtrToStringUni(IntPtr p)
84+
{
85+
if (p == IntPtr.Zero)
86+
{
87+
return null;
88+
}
89+
90+
int size = GetUnicodeByteLength(p);
91+
var buffer = new byte[size];
92+
Marshal.Copy(p, buffer, 0, size);
93+
return PyEncoding.GetString(buffer, 0, size);
94+
}
95+
96+
public static int GetUnicodeByteLength(IntPtr p)
97+
{
98+
var len = 0;
99+
while (true)
100+
{
101+
#if UCS2 && PYTHON2
102+
int c = Marshal.ReadInt16(p, len * 2);
103+
#else
104+
int c = Marshal.ReadInt32(p, len * 4);
105+
#endif
106+
107+
if (c == 0)
108+
{
109+
return len * UCS;
110+
}
111+
checked
112+
{
113+
++len;
114+
}
115+
}
116+
}
117+
118+
/// <summary>
119+
/// Utility function for Marshaling Unicode on PY3 and AnsiStr on PY2.
120+
/// Use on functions whose Input signatures changed between PY2/PY3.
121+
/// Ex. Py_SetPythonHome
122+
/// </summary>
123+
/// <param name="s">Managed String</param>
124+
/// <returns>
125+
/// Ptr to Native String ANSI(PY2)/Unicode(PY3/UCS2)/UTF32(PY3/UCS4.
126+
/// </returns>
127+
/// <remarks>
128+
/// You MUST deallocate the IntPtr of the Return when done with it.
129+
/// </remarks>
130+
public static IntPtr Py3UnicodePy2StringtoPtr(string s)
131+
{
132+
#if PYTHON2
133+
return Marshal.StringToHGlobalAnsi(s);
134+
#else
135+
return Instance.MarshalManagedToNative(s);
136+
#endif
137+
}
138+
139+
/// <summary>
140+
/// Utility function for Marshaling Unicode IntPtr on PY3 and
141+
/// AnsiStr IntPtr on PY2 to Managed Strings. Use on Python functions
142+
/// whose return type changed between PY2/PY3.
143+
/// Ex. Py_GetPythonHome
144+
/// </summary>
145+
/// <param name="p">Native Ansi/Unicode/UTF32 String</param>
146+
/// <returns>
147+
/// Managed String
148+
/// </returns>
149+
public static string PtrToPy3UnicodePy2String(IntPtr p)
150+
{
151+
#if PYTHON2
152+
return Marshal.PtrToStringAnsi(p);
153+
#else
154+
return PtrToStringUni(p);
155+
#endif
156+
}
157+
}
158+
159+
160+
/// <summary>
161+
/// Custom Marshaler to deal with Managed String Arrays to Native
162+
/// conversion differences on UCS2/UCS4.
163+
/// </summary>
164+
internal class StrArrayMarshaler : MarshalerBase
165+
{
166+
private static readonly MarshalerBase Instance = new StrArrayMarshaler();
167+
168+
public override IntPtr MarshalManagedToNative(object managedObj)
169+
{
170+
var argv = managedObj as string[];
171+
172+
if (argv == null)
173+
{
174+
return IntPtr.Zero;
175+
}
176+
177+
int totalStrLength = argv.Sum(arg => arg.Length + 1);
178+
int memSize = argv.Length * IntPtr.Size + totalStrLength * UCS;
179+
180+
IntPtr mem = Marshal.AllocHGlobal(memSize);
181+
try
182+
{
183+
// Preparing array of pointers to strings
184+
IntPtr curStrPtr = mem + argv.Length * IntPtr.Size;
185+
for (var i = 0; i < argv.Length; i++)
186+
{
187+
byte[] bStr = PyEncoding.GetBytes(argv[i] + "\0");
188+
Marshal.Copy(bStr, 0, curStrPtr, bStr.Length);
189+
Marshal.WriteIntPtr(mem + i * IntPtr.Size, curStrPtr);
190+
curStrPtr += bStr.Length;
191+
}
192+
}
193+
catch (Exception)
194+
{
195+
Marshal.FreeHGlobal(mem);
196+
throw;
197+
}
198+
199+
return mem;
200+
}
201+
202+
public static ICustomMarshaler GetInstance(string cookie)
203+
{
204+
return Instance;
205+
}
206+
}
207+
208+
209+
/// <summary>
210+
/// Custom Marshaler to deal with Managed String to Native
211+
/// conversion on UTF-8. Use on functions that expect UTF-8 encoded
212+
/// strings like `PyUnicode_FromStringAndSize`
213+
/// </summary>
214+
/// <remarks>
215+
/// If instead we used `MarshalAs(UnmanagedType.LPWStr)` the output to
216+
/// `foo` would be `f\x00o\x00o\x00`.
217+
/// </remarks>
218+
internal class Utf8Marshaler : MarshalerBase
219+
{
220+
private static readonly MarshalerBase Instance = new Utf8Marshaler();
221+
private static new readonly Encoding PyEncoding = Encoding.UTF8;
222+
223+
public override IntPtr MarshalManagedToNative(object managedObj)
224+
{
225+
var s = managedObj as string;
226+
227+
if (s == null)
228+
{
229+
return IntPtr.Zero;
230+
}
231+
232+
byte[] bStr = PyEncoding.GetBytes(s + "\0");
233+
IntPtr mem = Marshal.AllocHGlobal(bStr.Length);
234+
try
235+
{
236+
Marshal.Copy(bStr, 0, mem, bStr.Length);
237+
}
238+
catch (Exception)
239+
{
240+
Marshal.FreeHGlobal(mem);
241+
throw;
242+
}
243+
244+
return mem;
245+
}
246+
247+
public static ICustomMarshaler GetInstance(string cookie)
248+
{
249+
return Instance;
250+
}
251+
}
252+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy