Skip to content
This repository
Browse code

Marshal strings as UTF8 (affects #18)

Starting with OpenGL 4.2, strings passed to GL.ShaderSource are allowed
to contain multi-byte characters in comments (issue #18). This patch
modifies the marshaling code to use UTF8.GetBytes in order to marshal
strings, instead of Marshal.StringToHGlobalAnsi().
  • Loading branch information...
commit 94b04c02ca14f8e6611e895163c09fd84fa6bf5d 1 parent 3ee6d1d
thefiddler authored
8 Source/Generator.Rewrite/Program.cs
@@ -371,10 +371,10 @@ static void EmitStringBuilderEpilogue(MethodDefinition wrapper, MethodDefinition
371 371 static void EmitStringParameter(MethodDefinition wrapper, TypeReference p, MethodBody body, ILProcessor il)
372 372 {
373 373 // string marshaling:
374   - // IntPtr ptr = Marshal.StringToHGlobalAnsi(str);
  374 + // IntPtr ptr = MarshalStringToPtr(str);
375 375 // try { calli }
376 376 // finally { Marshal.FreeHGlobal(ptr); }
377   - var marshal_str_to_ptr = wrapper.Module.Import(TypeMarshal.Methods.First(m => m.Name == "StringToHGlobalAnsi"));
  377 + var marshal_str_to_ptr = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "MarshalStringToPtr"));
378 378
379 379 // IntPtr ptr;
380 380 var variable_name = p.Name + "_string_ptr";
@@ -396,9 +396,9 @@ static void EmitStringEpilogue(MethodDefinition wrapper, MethodBody body, ILProc
396 396 var p = wrapper.Parameters[i].ParameterType;
397 397 if (p.Name == "String" && !p.IsArray)
398 398 {
399   - var free = wrapper.Module.Import(TypeMarshal.Methods.First(m => m.Name == "FreeHGlobal"));
  399 + var free = wrapper.Module.Import(TypeBindingsBase.Methods.First(m => m.Name == "FreeStringPtr"));
400 400
401   - // Marshal.FreeHGlobal(ptr)
  401 + // FreeStringPtr(ptr)
402 402 var variable_name = p.Name + "_string_ptr";
403 403 var v = body.Variables.First(m => m.Name == variable_name);
404 404 il.Emit(OpCodes.Ldloc, v.Index);
64 Source/OpenTK/BindingsBase.cs
@@ -145,7 +145,57 @@ protected static void MarshalPtrToStringBuilder(IntPtr ptr, StringBuilder sb)
145 145 }
146 146
147 147 /// <summary>
148   - /// Marshals a string array to unmanaged memory by calling
  148 + /// Marshal a <c>System.String</c> to unmanaged memory.
  149 + /// The resulting string is encoded in UTF-8 and must be freed
  150 + /// with <c>FreeStringPtr</c>.
  151 + /// </summary>
  152 + /// <param name="str">The <c>System.String</c> to marshal.</param>
  153 + /// <returns>
  154 + /// An unmanaged pointer containing the marshalled string.
  155 + /// This pointer must be freed with <c>FreeStringPtr</c>
  156 + /// </returns>
  157 + protected static IntPtr MarshalStringToPtr(string str)
  158 + {
  159 + if (String.IsNullOrEmpty(str))
  160 + {
  161 + return IntPtr.Zero;
  162 + }
  163 +
  164 + // Allocate a buffer big enough to hold the marshalled string.
  165 + // We use GetMaxByteCount() as it is faster than GetByteCount().
  166 + // The downside is that it may allocate up to 3x more memory than
  167 + // strictly necessary.
  168 + int max_count = Encoding.UTF8.GetMaxByteCount(str.Length) + 1;
  169 + IntPtr ptr = Marshal.AllocHGlobal(max_count);
  170 + if (ptr == IntPtr.Zero)
  171 + {
  172 + throw new OutOfMemoryException();
  173 + }
  174 +
  175 + // Pin the managed string and convert it to UTF-8 using
  176 + // the pointer overload of System.Encoding.UTF8.GetBytes().
  177 + unsafe
  178 + {
  179 + fixed (char* pstr = str)
  180 + {
  181 + int actual_count = Encoding.UTF8.GetBytes(pstr, str.Length, (byte*)ptr, max_count);
  182 + Marshal.WriteByte(ptr, actual_count, 0); // Append '\0' at the end of the string
  183 + return ptr;
  184 + }
  185 + }
  186 + }
  187 +
  188 + /// <summary>
  189 + /// Frees a marshalled string that allocated by <c>MarshalStringToPtr</c>.
  190 + /// </summary>
  191 + /// <param name="ptr">An unmanaged pointer allocated with <c>MarshalStringToPtr</param>
  192 + protected static void FreeStringPtr(IntPtr ptr)
  193 + {
  194 + Marshal.FreeHGlobal(ptr);
  195 + }
  196 +
  197 + /// <summary>
  198 + /// Marshals a <c>System.String</c> array to unmanaged memory by calling
149 199 /// Marshal.AllocHGlobal for each element.
150 200 /// </summary>
151 201 /// <returns>An unmanaged pointer to an array of null-terminated strings</returns>
@@ -163,12 +213,7 @@ protected static IntPtr MarshalStringArrayToPtr(string[] str_array)
163 213
164 214 for (int i = 0; i < str_array.Length; i++)
165 215 {
166   - IntPtr str = Marshal.StringToHGlobalAnsi(str_array[i]);
167   - if (str == IntPtr.Zero)
168   - {
169   - throw new OutOfMemoryException();
170   - }
171   -
  216 + IntPtr str = MarshalStringToPtr(str_array[i]);
172 217 Marshal.WriteIntPtr(ptr, i * IntPtr.Size, str);
173 218 }
174 219 }
@@ -176,10 +221,9 @@ protected static IntPtr MarshalStringArrayToPtr(string[] str_array)
176 221 }
177 222
178 223 /// <summary>
179   - /// Frees a string array that has previously been
180   - /// marshalled by <c>MarshalStringArrayToPtr</c>.
  224 + /// Frees a marshalled string that allocated by <c>MarshalStringArrayToPtr</c>.
181 225 /// </summary>
182   - /// <param name="ptr">An unmanaged pointer allocated by <c>MarshalStringArrayToPtr</c></param>
  226 + /// <param name="ptr">An unmanaged pointer allocated with <c>MarshalStringArrayToPtr</c></param>
183 227 /// <param name="length">The length of the string array.</param>
184 228 protected static void FreeStringArrayPtr(IntPtr ptr, int length)
185 229 {

0 comments on commit 94b04c0

Please sign in to comment.
Something went wrong with that request. Please try again.