Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 01a35cb

Browse files
authored
Merge pull request #407 from vmuriart/custom_marshal
Refactor Managed-Native conversion - ICustomMarshaler
2 parents 7dba617 + 78939c5 commit 01a35cb

File tree

5 files changed

+238
-184
lines changed

5 files changed

+238
-184
lines changed

src/runtime/CustomMarshaler.cs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using System;
2+
using System.Linq;
3+
using System.Runtime.InteropServices;
4+
using System.Text;
5+
6+
namespace Python.Runtime
7+
{
8+
/// <summary>
9+
/// Abstract class defining boiler plate methods that
10+
/// Custom Marshalers will use.
11+
/// </summary>
12+
public abstract class MarshalerBase : ICustomMarshaler
13+
{
14+
public object MarshalNativeToManaged(IntPtr pNativeData)
15+
{
16+
throw new NotImplementedException();
17+
}
18+
19+
public abstract IntPtr MarshalManagedToNative(object managedObj);
20+
21+
public void CleanUpNativeData(IntPtr pNativeData)
22+
{
23+
Marshal.FreeHGlobal(pNativeData);
24+
}
25+
26+
public void CleanUpManagedData(object managedObj)
27+
{
28+
// Let GC deal with it
29+
}
30+
31+
public int GetNativeDataSize()
32+
{
33+
return IntPtr.Size;
34+
}
35+
}
36+
37+
38+
/// <summary>
39+
/// Custom Marshaler to deal with Managed String to Native
40+
/// conversion differences on UCS2/UCS4.
41+
/// </summary>
42+
public class StrMarshaler : MarshalerBase
43+
{
44+
private static readonly MarshalerBase Instance = new StrMarshaler();
45+
private static readonly Encoding PyEncoding = Runtime.PyEncoding;
46+
47+
public override IntPtr MarshalManagedToNative(object managedObj)
48+
{
49+
var s = managedObj as string;
50+
51+
if (s == null)
52+
{
53+
return IntPtr.Zero;
54+
}
55+
56+
byte[] bStr = PyEncoding.GetBytes(s + "\0");
57+
IntPtr mem = Marshal.AllocHGlobal(bStr.Length);
58+
try
59+
{
60+
Marshal.Copy(bStr, 0, mem, bStr.Length);
61+
}
62+
catch (Exception)
63+
{
64+
Marshal.FreeHGlobal(mem);
65+
throw;
66+
}
67+
68+
return mem;
69+
}
70+
71+
public static ICustomMarshaler GetInstance(string cookie)
72+
{
73+
return Instance;
74+
}
75+
}
76+
77+
78+
/// <summary>
79+
/// Custom Marshaler to deal with Managed String Arrays to Native
80+
/// conversion differences on UCS2/UCS4.
81+
/// </summary>
82+
public class StrArrayMarshaler : MarshalerBase
83+
{
84+
private static readonly MarshalerBase Instance = new StrArrayMarshaler();
85+
private static readonly Encoding PyEncoding = Runtime.PyEncoding;
86+
87+
public override IntPtr MarshalManagedToNative(object managedObj)
88+
{
89+
var argv = managedObj as string[];
90+
91+
if (argv == null)
92+
{
93+
return IntPtr.Zero;
94+
}
95+
96+
int totalStrLength = argv.Sum(arg => arg.Length + 1);
97+
int memSize = argv.Length * IntPtr.Size + totalStrLength * Runtime.UCS;
98+
99+
IntPtr mem = Marshal.AllocHGlobal(memSize);
100+
try
101+
{
102+
// Preparing array of pointers to strings
103+
IntPtr curStrPtr = mem + argv.Length * IntPtr.Size;
104+
for (var i = 0; i < argv.Length; i++)
105+
{
106+
byte[] bStr = PyEncoding.GetBytes(argv[i] + "\0");
107+
Marshal.Copy(bStr, 0, curStrPtr, bStr.Length);
108+
Marshal.WriteIntPtr(mem + i * IntPtr.Size, curStrPtr);
109+
curStrPtr += bStr.Length;
110+
}
111+
}
112+
catch (Exception)
113+
{
114+
Marshal.FreeHGlobal(mem);
115+
throw;
116+
}
117+
118+
return mem;
119+
}
120+
121+
public static ICustomMarshaler GetInstance(string cookie)
122+
{
123+
return Instance;
124+
}
125+
}
126+
127+
128+
/// <summary>
129+
/// Custom Marshaler to deal with Managed String to Native
130+
/// conversion on UTF-8. Use on functions that expect UTF-8 encoded
131+
/// strings like `PyUnicode_FromStringAndSize`
132+
/// </summary>
133+
/// <remarks>
134+
/// If instead we used `MarshalAs(UnmanagedType.LPWStr)` the output to
135+
/// `foo` would be `f\x00o\x00o\x00`.
136+
/// </remarks>
137+
public class Utf8Marshaler : MarshalerBase
138+
{
139+
private static readonly MarshalerBase Instance = new Utf8Marshaler();
140+
private static readonly Encoding PyEncoding = Encoding.UTF8;
141+
142+
public override IntPtr MarshalManagedToNative(object managedObj)
143+
{
144+
var s = managedObj as string;
145+
146+
if (s == null)
147+
{
148+
return IntPtr.Zero;
149+
}
150+
151+
byte[] bStr = PyEncoding.GetBytes(s + "\0");
152+
IntPtr mem = Marshal.AllocHGlobal(bStr.Length);
153+
try
154+
{
155+
Marshal.Copy(bStr, 0, mem, bStr.Length);
156+
}
157+
catch (Exception)
158+
{
159+
Marshal.FreeHGlobal(mem);
160+
throw;
161+
}
162+
163+
return mem;
164+
}
165+
166+
public static ICustomMarshaler GetInstance(string cookie)
167+
{
168+
return Instance;
169+
}
170+
}
171+
}

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Compile Include="constructorbinder.cs" />
9090
<Compile Include="constructorbinding.cs" />
9191
<Compile Include="converter.cs" />
92+
<Compile Include="CustomMarshaler.cs" />
9293
<Compile Include="debughelper.cs" />
9394
<Compile Include="delegatemanager.cs" />
9495
<Compile Include="delegateobject.cs" />

src/runtime/converter.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -599,21 +599,10 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
599599
{
600600
if (Runtime.PyUnicode_GetSize(value) == 1)
601601
{
602-
op = Runtime.PyUnicode_AS_UNICODE(value);
603-
if (Runtime.UCS == 2) // Don't trust linter, statement not always true.
604-
{
605-
// 2011-01-02: Marshal as character array because the cast
606-
// result = (char)Marshal.ReadInt16(op); throws an OverflowException
607-
// on negative numbers with Check Overflow option set on the project
608-
Char[] buff = new Char[1];
609-
Marshal.Copy(op, buff, 0, 1);
610-
result = buff[0];
611-
}
612-
else // UCS4
613-
{
614-
// XXX this is probably NOT correct?
615-
result = (char)Marshal.ReadInt32(op);
616-
}
602+
op = Runtime.PyUnicode_AsUnicode(value);
603+
Char[] buff = new Char[1];
604+
Marshal.Copy(op, buff, 0, 1);
605+
result = buff[0];
617606
return true;
618607
}
619608
goto type_error;

src/runtime/debughelper.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,27 @@ internal static void debug(string msg)
115115
Console.WriteLine("thread {0} : {1}", tid, caller);
116116
Console.WriteLine(" {0}", msg);
117117
}
118+
119+
/// <summary>
120+
/// Helper function to inspect/compare managed to native conversions.
121+
/// Especially useful when debugging CustomMarshaler.
122+
/// </summary>
123+
/// <param name="bytes"></param>
124+
[Conditional("DEBUG")]
125+
public static void PrintHexBytes(byte[] bytes)
126+
{
127+
if ((bytes == null) || (bytes.Length == 0))
128+
{
129+
Console.WriteLine("<none>");
130+
}
131+
else
132+
{
133+
foreach (byte t in bytes)
134+
{
135+
Console.Write("{0:X2} ", t);
136+
}
137+
Console.WriteLine();
138+
}
139+
}
118140
}
119141
}

0 commit comments

Comments
 (0)