using System.Reflection;
using System.Threading;
using System.Runtime.InteropServices.ComTypes;
+using System.Text;
using System.Runtime.ConstrainedExecution;
#if !FULL_AOT_RUNTIME
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static IntPtr AllocCoTaskMem (int cb);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr AllocCoTaskMemSize (UIntPtr sizet);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
FreeCoTaskMem (s);
}
+ public static void ZeroFreeCoTaskMemUTF8 (IntPtr s)
+ {
+ ClearAnsi (s);
+ FreeCoTaskMem (s);
+ }
+
public static void ZeroFreeGlobalAllocAnsi (IntPtr s)
{
ClearAnsi (s);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static string PtrToStringAnsi (IntPtr ptr, int len);
+ public static string PtrToStringUTF8 (IntPtr ptr)
+ {
+ return PtrToStringAnsi (ptr);
+ }
+
+ public static string PtrToStringUTF8 (IntPtr ptr, int byteLen)
+ {
+ return PtrToStringAnsi (ptr, byteLen);
+ }
+
public static string PtrToStringAuto (IntPtr ptr)
{
return SystemDefaultCharSize == 2
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static IntPtr StringToBSTR (string s);
- //
- // I believe this is wrong, because in Mono and in P/Invoke
- // we treat "Ansi" conversions as UTF-8 conversions, while
- // this one does not do this
- //
public static IntPtr StringToCoTaskMemAnsi (string s)
{
- int length = s.Length + 1;
- IntPtr ctm = AllocCoTaskMem (length);
-
- byte[] asBytes = new byte[length];
- for (int i = 0; i < s.Length; i++)
- asBytes[i] = (byte)s[i];
- asBytes[s.Length] = 0;
-
- copy_to_unmanaged (asBytes, 0, ctm, length);
- return ctm;
+ return StringToAllocatedMemoryUTF8 (s);
}
public static IntPtr StringToCoTaskMemAuto (string s)
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static IntPtr StringToHGlobalAnsi (string s);
+ unsafe public static IntPtr StringToAllocatedMemoryUTF8(String s)
+ {
+ const int MAX_UTF8_CHAR_SIZE = 3;
+ if (s == null)
+ return IntPtr.Zero;
+
+ int nb = (s.Length + 1) * MAX_UTF8_CHAR_SIZE;
+
+ // Overflow checking
+ if (nb < s.Length)
+ throw new ArgumentOutOfRangeException("s");
+
+ IntPtr pMem = AllocCoTaskMemSize(new UIntPtr((uint)nb +1));
+
+ if (pMem == IntPtr.Zero)
+ throw new OutOfMemoryException();
+
+ byte* pbMem = (byte*)pMem;
+ int nbWritten = s.GetBytesFromEncoding(pbMem, nb, Encoding.UTF8);
+ pbMem[nbWritten] = 0;
+ return pMem;
+ }
+
public static IntPtr StringToHGlobalAuto (string s)
{
return SystemDefaultCharSize == 2
}
}
+ readonly String[] TestStrings = new String[] {
+ "", //Empty String
+ "Test String",
+ "A", //Single character string
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself. " +
+ "This is a very long string as it repeats itself.",
+ "This \n is \n a \n multiline \n string",
+ "This \0 is \0 a \0 string \0 with \0 nulls",
+ "\0string",
+ "string\0",
+ "\0\0\0\0\0\0\0\0"
+ };
+
+ [Test]
+ public unsafe void PtrToStringUTF8_Test ()
+ {
+ int i = 0;
+ foreach (String srcString in TestStrings)
+ {
+ i++;
+ // we assume string null terminated
+ if (srcString.Contains("\0"))
+ continue;
+
+ IntPtr ptrString = Marshal.StringToAllocatedMemoryUTF8(srcString);
+ string retString = Marshal.PtrToStringUTF8(ptrString);
+
+ Assert.AreEqual (srcString, retString, "#" + i);
+ if (srcString.Length > 0)
+ {
+ string retString2 = Marshal.PtrToStringUTF8(ptrString, srcString.Length - 1);
+ Assert.AreEqual (srcString.Substring(0, srcString.Length - 1), retString2, "#s" + i);
+ }
+ Marshal.FreeHGlobal(ptrString);
+ }
+ }
+
[Test]
public unsafe void UnsafeAddrOfPinnedArrayElement ()
{
[System.Runtime.InteropServices.ComVisible(false)]
HString = 0x2f, // Windows Runtime HSTRING
+
+ [System.Runtime.InteropServices.ComVisible(false)]
+ LPUTF8Str = 0x30, // UTF8 string
}
#if !MONO
return s;
}
+
+ unsafe internal int GetBytesFromEncoding(byte* pbNativeBuffer, int cbNativeBuffer,Encoding encoding)
+ {
+ // encoding == Encoding.UTF8
+ fixed (char* pwzChar = &this.m_firstChar)
+ {
+ return encoding.GetBytes(pwzChar, m_stringLength, pbNativeBuffer, cbNativeBuffer);
+ }
+ }
+
#if !MONO
[System.Security.SecuritySafeCritical] // auto-generated
unsafe internal int ConvertToAnsi(byte *pbNativeBuffer, int cbNativeBuffer, bool fBestFit, bool fThrowOnUnmappableChar)
ICALL_TYPE(MARSHAL, "System.Runtime.InteropServices.Marshal", MARSHAL_2)
#endif
ICALL(MARSHAL_2, "AllocCoTaskMem", ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMem)
+ICALL(MARSHAL_51,"AllocCoTaskMemSize(uintptr)", ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize)
ICALL(MARSHAL_3, "AllocHGlobal", ves_icall_System_Runtime_InteropServices_Marshal_AllocHGlobal)
ICALL(MARSHAL_50, "BufferToBSTR", ves_icall_System_Runtime_InteropServices_Marshal_BufferToBSTR)
ICALL(MARSHAL_4, "DestroyStructure", ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure)
static void *
mono_marshal_string_to_utf16_copy (MonoString *s);
-#ifndef HOST_WIN32
static gpointer
-mono_string_to_lpstr (MonoString *string_obj);
-#endif
+mono_string_to_utf8str (MonoString *string_obj);
static MonoStringBuilder *
mono_string_utf8_to_builder2 (char *text);
register_icall (mono_string_new_wrapper, "mono_string_new_wrapper", "obj ptr", FALSE);
register_icall (mono_string_new_len_wrapper, "mono_string_new_len_wrapper", "obj ptr int", FALSE);
register_icall (ves_icall_mono_string_to_utf8, "ves_icall_mono_string_to_utf8", "ptr obj", FALSE);
- register_icall (mono_string_to_lpstr, "mono_string_to_lpstr", "ptr obj", FALSE);
+ register_icall (mono_string_to_utf8str, "mono_string_to_utf8str", "ptr obj", FALSE);
register_icall (mono_string_to_ansibstr, "mono_string_to_ansibstr", "ptr object", FALSE);
register_icall (mono_string_builder_to_utf8, "mono_string_builder_to_utf8", "ptr object", FALSE);
register_icall (mono_string_builder_to_utf16, "mono_string_builder_to_utf16", "ptr object", FALSE);
if (!sb || !text)
return;
- int len = strlen (text);
- if (len > mono_string_builder_capacity (sb))
- len = mono_string_builder_capacity (sb);
-
GError *error = NULL;
glong copied;
- gunichar2* ut = g_utf8_to_utf16 (text, len, NULL, &copied, &error);
+ gunichar2* ut = g_utf8_to_utf16 (text, strlen (text), NULL, &copied, &error);
+ int capacity = mono_string_builder_capacity (sb);
+
+ if (copied > capacity)
+ copied = capacity;
if (!error) {
MONO_OBJECT_SETREF (sb, chunkPrevious, NULL);
return sb;
}
-
void
mono_string_utf16_to_builder (MonoStringBuilder *sb, gunichar2 *text)
{
{
MonoError error;
GError *gerror = NULL;
-
+ glong byte_count;
if (!sb)
return NULL;
guint str_len = mono_string_builder_string_length (sb);
- gchar *tmp = g_utf16_to_utf8 (str_utf16, str_len, NULL, NULL, &gerror);
+ gchar *tmp = g_utf16_to_utf8 (str_utf16, str_len, NULL, &byte_count, &gerror);
if (gerror) {
g_error_free (gerror);
return NULL;
} else {
guint len = mono_string_builder_capacity (sb) + 1;
- gchar *res = (gchar *)mono_marshal_alloc (len * sizeof (gchar), &error);
+ gchar *res = (gchar *)mono_marshal_alloc (MAX (byte_count+1, len * sizeof (gchar)), &error);
if (!mono_error_ok (&error)) {
mono_marshal_free (str_utf16);
g_free (tmp);
return NULL;
}
- g_assert (str_len < len);
- memcpy (res, tmp, str_len * sizeof (gchar));
- res[str_len] = '\0';
+ memcpy (res, tmp, byte_count);
+ res[byte_count] = '\0';
mono_marshal_free (str_utf16);
g_free (tmp);
}
/* This is a JIT icall, it sets the pending exception and returns NULL on error. */
-#ifndef HOST_WIN32
static gpointer
-mono_string_to_lpstr (MonoString *s)
+mono_string_to_utf8str (MonoString *s)
{
MonoError error;
char *result = mono_string_to_utf8_checked (s, &error);
mono_error_set_pending_exception (&error);
return result;
}
-#endif /* HOST_WIN32 */
gpointer
mono_string_to_ansibstr (MonoString *string_obj)
#endif
mono_mb_emit_byte (mb, CEE_STIND_REF);
break;
+
+ // In Mono historically LPSTR was treated as a UTF8STR
case MONO_MARSHAL_CONV_STR_LPSTR:
+ case MONO_MARSHAL_CONV_STR_UTF8STR:
mono_mb_emit_ldloc (mb, 1);
mono_mb_emit_ldloc (mb, 0);
mono_mb_emit_byte (mb, CEE_LDIND_I);
case MONO_MARSHAL_CONV_LPTSTR_STR:
*ind_store_type = CEE_STIND_REF;
return mono_string_new_wrapper;
+ case MONO_MARSHAL_CONV_UTF8STR_STR:
case MONO_MARSHAL_CONV_LPSTR_STR:
*ind_store_type = CEE_STIND_REF;
return mono_string_new_wrapper;
#ifdef TARGET_WIN32
return mono_marshal_string_to_utf16;
#else
- return mono_string_to_lpstr;
+ return mono_string_to_utf8str;
#endif
+ // In Mono historically LPSTR was treated as a UTF8STR
+ case MONO_MARSHAL_CONV_STR_UTF8STR:
case MONO_MARSHAL_CONV_STR_LPSTR:
- return mono_string_to_lpstr;
+ return mono_string_to_utf8str;
case MONO_MARSHAL_CONV_STR_BSTR:
return mono_string_to_bstr;
case MONO_MARSHAL_CONV_BSTR_STR:
case MONO_MARSHAL_CONV_STR_TBSTR:
case MONO_MARSHAL_CONV_STR_ANSIBSTR:
return mono_string_to_ansibstr;
+ case MONO_MARSHAL_CONV_SB_UTF8STR:
case MONO_MARSHAL_CONV_SB_LPSTR:
return mono_string_builder_to_utf8;
case MONO_MARSHAL_CONV_SB_LPTSTR:
case MONO_MARSHAL_CONV_FTN_DEL:
*ind_store_type = CEE_STIND_REF;
return mono_ftnptr_to_delegate;
+ case MONO_MARSHAL_CONV_UTF8STR_SB:
case MONO_MARSHAL_CONV_LPSTR_SB:
*ind_store_type = CEE_STIND_REF;
return mono_string_utf8_to_builder;
mono_mb_emit_byte (mb, CEE_NEG);
mono_mb_emit_byte (mb, CEE_STIND_I2);
break;
+ // In Mono historically LPSTR was treated as a UTF8STR
+ case MONO_MARSHAL_CONV_STR_UTF8STR:
case MONO_MARSHAL_CONV_STR_LPWSTR:
case MONO_MARSHAL_CONV_STR_LPSTR:
case MONO_MARSHAL_CONV_STR_LPTSTR:
return MONO_MARSHAL_CONV_STR_LPTSTR;
case MONO_NATIVE_BSTR:
return MONO_MARSHAL_CONV_STR_BSTR;
+ case MONO_NATIVE_UTF8STR:
+ return MONO_MARSHAL_CONV_STR_UTF8STR;
default:
return MONO_MARSHAL_CONV_INVALID;
}
switch (encoding) {
case MONO_NATIVE_LPWSTR:
return MONO_MARSHAL_CONV_SB_LPWSTR;
- break;
case MONO_NATIVE_LPSTR:
return MONO_MARSHAL_CONV_SB_LPSTR;
- break;
+ case MONO_NATIVE_UTF8STR:
+ return MONO_MARSHAL_CONV_SB_UTF8STR;
case MONO_NATIVE_LPTSTR:
return MONO_MARSHAL_CONV_SB_LPTSTR;
- break;
default:
return MONO_MARSHAL_CONV_INVALID;
}
case MONO_NATIVE_LPWSTR:
*need_free = FALSE;
return MONO_MARSHAL_CONV_LPWSTR_STR;
+ case MONO_NATIVE_UTF8STR:
+ return MONO_MARSHAL_CONV_UTF8STR_STR;
case MONO_NATIVE_LPSTR:
case MONO_NATIVE_VBBYREFSTR:
return MONO_MARSHAL_CONV_LPSTR_STR;
*/
*need_free = FALSE;
return MONO_MARSHAL_CONV_LPWSTR_SB;
+ case MONO_NATIVE_UTF8STR:
+ return MONO_MARSHAL_CONV_UTF8STR_SB;
case MONO_NATIVE_LPSTR:
return MONO_MARSHAL_CONV_LPSTR_SB;
break;
case MONO_NATIVE_LPSTR:
mono_mb_emit_icall (mb, mono_string_utf8_to_builder2);
break;
+ case MONO_NATIVE_UTF8STR:
+ mono_mb_emit_icall (mb, mono_string_utf8_to_builder2);
+ break;
default:
g_assert_not_reached ();
}
mono_mb_emit_ldloc (mb, 0);
mono_mb_emit_icall (mb, conv_to_icall (MONO_MARSHAL_CONV_FTN_DEL, NULL));
mono_mb_emit_stloc (mb, 3);
+ } else if (klass == mono_defaults.stringbuilder_class){
+ // FIXME: implement
} else {
/* set src */
mono_mb_emit_stloc (mb, 0);
encoding = mono_marshal_get_string_encoding (m->piinfo, spec);
// FIXME:
- g_assert (encoding == MONO_NATIVE_LPSTR);
+ g_assert (encoding == MONO_NATIVE_LPSTR || encoding == MONO_NATIVE_UTF8STR);
g_assert (!t->byref);
g_assert (encoding != -1);
case MONO_MARSHAL_CONV_STR_BSTR:
case MONO_MARSHAL_CONV_STR_ANSIBSTR:
case MONO_MARSHAL_CONV_STR_TBSTR:
+ case MONO_MARSHAL_CONV_STR_UTF8STR:
mono_marshal_free (*(gpointer *)cpos);
break;
return res;
}
+void*
+ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize (gulong size)
+{
+ void *res = mono_marshal_alloc_co_task_mem (size);
+
+ if (!res) {
+ mono_set_pending_exception (mono_domain_get ()->out_of_memory_ex);
+ return NULL;
+ }
+ return res;
+}
+
void
ves_icall_System_Runtime_InteropServices_Marshal_FreeCoTaskMem (void *ptr)
{
case MONO_NATIVE_BSTR:
case MONO_NATIVE_ANSIBSTR:
case MONO_NATIVE_TBSTR:
+ case MONO_NATIVE_UTF8STR:
case MONO_NATIVE_LPARRAY:
case MONO_NATIVE_SAFEARRAY:
case MONO_NATIVE_IUNKNOWN:
switch (string_encoding) {
case MONO_NATIVE_LPWSTR:
return mono_marshal_string_to_utf16_copy ((MonoString*)o);
- break;
case MONO_NATIVE_LPSTR:
- return mono_string_to_lpstr ((MonoString*)o);
- break;
+ case MONO_NATIVE_UTF8STR:
+ // Same code path, because in Mono, we treated strings as Utf8
+ return mono_string_to_utf8str ((MonoString*)o);
default:
g_warning ("marshaling conversion %d not implemented", string_encoding);
g_assert_not_reached ();
switch (string_encoding) {
case MONO_NATIVE_LPWSTR:
case MONO_NATIVE_LPSTR:
+ case MONO_NATIVE_UTF8STR:
mono_marshal_free (ptr);
break;
default:
void*
ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMem (int size);
+void*
+ves_icall_System_Runtime_InteropServices_Marshal_AllocCoTaskMemSize (gulong size);
+
void
ves_icall_System_Runtime_InteropServices_Marshal_FreeCoTaskMem (void *ptr);
case MONO_NATIVE_TBSTR:
*conv = MONO_MARSHAL_CONV_STR_TBSTR;
return MONO_NATIVE_TBSTR;
+ case MONO_NATIVE_UTF8STR:
+ *conv = MONO_MARSHAL_CONV_STR_LPTSTR;
+ return MONO_NATIVE_UTF8STR;
case MONO_NATIVE_BYVALTSTR:
if (unicode)
*conv = MONO_MARSHAL_CONV_STR_BYVALWSTR;
MONO_NATIVE_LPSTRUCT = 0x2b,
MONO_NATIVE_CUSTOM = 0x2c,
MONO_NATIVE_ERROR = 0x2d,
+ // TODO: MONO_NATIVE_IINSPECTABLE = 0x2e
+ // TODO: MONO_NATIVE_HSTRING = 0x2f
+ MONO_NATIVE_UTF8STR = 0x30,
MONO_NATIVE_MAX = 0x50 /* no info */
} MonoMarshalNative;
MONO_MARSHAL_FREE_ARRAY,
MONO_MARSHAL_CONV_BSTR_STR,
MONO_MARSHAL_CONV_SAFEHANDLE,
- MONO_MARSHAL_CONV_HANDLEREF
+ MONO_MARSHAL_CONV_HANDLEREF,
+ MONO_MARSHAL_CONV_STR_UTF8STR,
+ MONO_MARSHAL_CONV_SB_UTF8STR,
+ MONO_MARSHAL_CONV_UTF8STR_STR,
+ MONO_MARSHAL_CONV_UTF8STR_SB,
} MonoMarshalConv;
#define MONO_MARSHAL_CONV_INVALID ((MonoMarshalConv)-1)
typeof-ptr.cs \
static-constructor.cs \
pinvoke.cs \
+ pinvoke-utf8.cs \
pinvoke3.cs \
pinvoke11.cs \
pinvoke13.cs \
if HOST_WIN32
PLATFORM_DISABLED_TESTS=async-exc-compilation.exe finally_guard.exe finally_block_ending_in_dead_bb.exe \
- bug-18026.exe monitor.exe threadpool-exceptions5.exe
+ bug-18026.exe monitor.exe threadpool-exceptions5.exe pinvoke-utf8.exe
endif
endif
return s.array [0] + s.array [1] + s.array [2];
}
+const int NSTRINGS = 6;
+//test strings
+const char *utf8Strings[] = {
+ "Managed",
+ "Sîne klâwen durh die wolken sint geslagen" ,
+ "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम्",
+ "我能吞下玻璃而不伤身体",
+ "ღმერთსი შემვედრე,შემვედრე, ნუთუ კვლა დამხსნას შემვედრე,სოფლისა შემვედრე, შემვედრე,შემვედრე,შემვედრე,შრომასა, ცეცხლს, წყალსა და მიწასა, ჰაერთა თანა მრომასა; მომცნეს ფრთენი და აღვფრინდე, მივჰხვდე მას ჩემსა ნდომასა, დღისით და ღამით ვჰხედვიდე მზისა ელვათა კრთომაასაშემვედრე,შემვედრე,",
+ "Τη γλώσσα μου έδωσαν ελληνική",
+"\0"
+};
+
+LIBTEST_API char *
+build_return_string(const char* pReturn)
+{
+ char *ret = 0;
+ if (pReturn == 0 || *pReturn == 0)
+ return ret;
+
+ size_t strLength = strlen(pReturn);
+ ret = (char *)(malloc(sizeof(char)* (strLength + 1)));
+ memset(ret, '\0', strLength + 1);
+ strncpy(ret, pReturn, strLength);
+ return ret;
+}
+
+LIBTEST_API char *
+StringParameterInOut(/*[In,Out]*/ char *s, int index)
+{
+ // return a copy
+ return build_return_string(s);
+}
+
+LIBTEST_API void
+StringParameterRefOut(/*out*/ char **s, int index)
+{
+ char *pszTextutf8 = (char*)utf8Strings[index];
+ size_t strLength = strlen(pszTextutf8);
+ *s = (char *)(malloc(sizeof(char)* (strLength + 1)));
+ memcpy(*s, pszTextutf8, strLength);
+ (*s)[strLength] = '\0';
+}
+
+LIBTEST_API void
+StringParameterRef(/*ref*/ char **s, int index)
+{
+ char *pszTextutf8 = (char*)utf8Strings[index];
+ size_t strLength = strlen(pszTextutf8);
+ // do byte by byte validation of in string
+ size_t szLen = strlen(*s);
+ for (size_t i = 0; i < szLen; i++)
+ {
+ if ((*s)[i] != pszTextutf8[i])
+ {
+ printf("[in] managed string do not match native string\n");
+ abort ();
+ }
+ }
+
+ if (*s)
+ {
+ free(*s);
+ }
+ // overwrite the orginal
+ *s = (char *)(malloc(sizeof(char)* (strLength + 1)));
+ memcpy(*s, pszTextutf8, strLength);
+ (*s)[strLength] = '\0';
+}
+
+LIBTEST_API void
+StringBuilderParameterInOut(/*[In,Out] StringBuilder*/ char *s, int index)
+{
+ // if string.empty
+ if (s == 0 || *s == 0)
+ return;
+
+ char *pszTextutf8 = (char*)utf8Strings[index];
+
+ // do byte by byte validation of in string
+ size_t szLen = strlen(s);
+ for (size_t i = 0; i < szLen; i++)
+ {
+ if (s[i] != pszTextutf8[i])
+ {
+ printf("[in] managed string do not match native string\n");
+ abort ();
+ }
+ }
+
+ // modify the string inplace
+ size_t outLen = strlen(pszTextutf8);
+ for (size_t i = 0; i < outLen; i++) {
+ s[i] = pszTextutf8[i];
+ }
+ s[outLen] = '\0';
+}
+
+//out string builder
+LIBTEST_API void
+StringBuilderParameterOut(/*[Out] StringBuilder*/ char *s, int index)
+{
+ char *pszTextutf8 = (char*)utf8Strings[index];
+
+ printf ("SBPO: Receiving %s\n", s);
+ // modify the string inplace
+ size_t outLen = strlen(pszTextutf8);
+ for (size_t i = 0; i < outLen; i++) {
+ s[i] = pszTextutf8[i];
+ }
+ s[outLen] = '\0';
+}
+
+LIBTEST_API char *
+StringParameterOut(/*[Out]*/ char *s, int index)
+{
+ // return a copy
+ return build_return_string(s);
+}
+
+// Utf8 field
+typedef struct FieldWithUtf8
+{
+ char *pFirst;
+ int index;
+}FieldWithUtf8;
+
+//utf8 struct field
+LIBTEST_API void
+TestStructWithUtf8Field(struct FieldWithUtf8 fieldStruct)
+{
+ char *pszManagedutf8 = fieldStruct.pFirst;
+ int stringIndex = fieldStruct.index;
+ char *pszNative = 0;
+ size_t outLen = 0;
+
+ if (pszManagedutf8 == 0 || *pszManagedutf8 == 0)
+ return;
+
+ pszNative = (char*)utf8Strings[stringIndex];
+
+ outLen = strlen(pszNative);
+ // do byte by byte comparision
+ for (size_t i = 0; i < outLen; i++)
+ {
+ if (pszNative[i] != pszManagedutf8[i])
+ {
+ printf("Native and managed string do not match.\n");
+ abort ();
+ }
+ }
+}
+
+typedef void (* Callback2)(char *text, int index);
+
+LIBTEST_API void
+Utf8DelegateAsParameter(Callback2 managedCallback)
+{
+ for (int i = 0; i < NSTRINGS; ++i)
+ {
+ char *pszNative = 0;
+ pszNative = (char*)utf8Strings[i];
+ managedCallback(pszNative, i);
+ }
+}
+
+
+LIBTEST_API char*
+StringBuilderParameterReturn(int index)
+{
+ char *pszTextutf8 = (char*)utf8Strings[index];
+ size_t strLength = strlen(pszTextutf8);
+ char * ret = (char *)(malloc(sizeof(char)* (strLength + 1)));
+ memcpy(ret, pszTextutf8, strLength);
+ ret[strLength] = '\0';
+
+ return ret;
+}
+
LIBTEST_API int STDCALL
mono_test_marshal_pointer_array (int *arr[])
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Collections.Generic;
+
+
+// UTF8
+class UTF8StringTests
+{
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.LPUTF8Str)]
+ public static extern string StringParameterInOut([In, Out][MarshalAs(UnmanagedType.LPUTF8Str)]string s, int index);
+ public static bool TestInOutStringParameter(string orgString, int index)
+ {
+ string passedString = orgString;
+ string expectedNativeString = passedString;
+
+ string nativeString = StringParameterInOut(passedString, index);
+ if (!(nativeString == expectedNativeString))
+ {
+ Console.WriteLine("StringParameterInOut: nativeString != expecedNativeString ");
+ return false;
+ }
+ return true;
+ }
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.LPUTF8Str)]
+ public static extern string StringParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)]string s, int index);
+ public static bool TestOutStringParameter(string orgString, int index)
+ {
+ string passedString = orgString;
+ string expecedNativeString = passedString;
+ string nativeString = StringParameterInOut(passedString, index);
+ if (!(nativeString == expecedNativeString))
+ {
+ Console.WriteLine("StringParameterInOut: nativeString != expecedNativeString ");
+ return false;
+ }
+ return true;
+ }
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void StringParameterRefOut([MarshalAs(UnmanagedType.LPUTF8Str)]out string s, int index);
+ public static bool TestStringPassByOut(string orgString, int index)
+ {
+ // out string
+ string expectedNative = string.Empty;
+ StringParameterRefOut(out expectedNative, index);
+ if (orgString != expectedNative)
+ {
+ Console.WriteLine ("TestStringPassByOut : expectedNative != outString");
+ return false;
+ }
+ return true;
+ }
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void StringParameterRef([MarshalAs(UnmanagedType.LPUTF8Str)]ref string s, int index);
+ public static bool TestStringPassByRef(string orgString, int index)
+ {
+ string orgCopy = new string(orgString.ToCharArray());
+ StringParameterRef(ref orgString, index);
+ if (orgString != orgCopy)
+ {
+ Console.WriteLine("TestStringPassByOut : string mismatch");
+ return false;
+ }
+ return true;
+ }
+
+ public static bool EmptyStringTest()
+ {
+ StringParameterInOut(string.Empty, 0);
+ StringParameterOut(string.Empty, 0);
+ return true;
+ }
+}
+
+// UTF8 stringbuilder
+class UTF8StringBuilderTests
+{
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void StringBuilderParameterInOut([In,Out][MarshalAs(UnmanagedType.LPUTF8Str)]StringBuilder s, int index);
+ public static bool TestInOutStringBuilderParameter(string expectedString, int index)
+ {
+ StringBuilder nativeStrBuilder = new StringBuilder(expectedString);
+
+ StringBuilderParameterInOut(nativeStrBuilder, index);
+
+ if (!nativeStrBuilder.ToString().Equals(expectedString))
+ {
+ Console.WriteLine($"TestInOutStringBuilderParameter: nativeString != expecedNativeString index={index} got={nativeStrBuilder} and expected={expectedString} ");
+ return false;
+ }
+ return true;
+ }
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void StringBuilderParameterOut([Out][MarshalAs(UnmanagedType.LPUTF8Str)]StringBuilder s, int index);
+ public static bool TestOutStringBuilderParameter(string expectedString, int index)
+ {
+ // string builder capacity
+ StringBuilder nativeStringBuilder = new StringBuilder(expectedString.Length);
+
+ StringBuilderParameterOut(nativeStringBuilder, index);
+
+ if (!nativeStringBuilder.ToString().Equals(expectedString))
+ {
+ Console.WriteLine("TestOutStringBuilderParameter: string != expecedString ");
+ return false;
+ }
+ return true;
+ }
+
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.LPUTF8Str,SizeConst = 512)]
+ public static extern StringBuilder StringBuilderParameterReturn(int index);
+ public static bool TestReturnStringBuilder(string expectedReturn, int index)
+ {
+ StringBuilder nativeString = StringBuilderParameterReturn(index);
+ if (!expectedReturn.Equals(nativeString.ToString()))
+ {
+ Console.WriteLine(string.Format( "TestReturnStringBuilder: nativeString {0} != expecedNativeString {1}",nativeString.ToString(),expectedReturn) );
+ return false;
+ }
+ return true;
+ }
+}
+
+// UTF8 string as struct field
+class UTF8StructMarshalling
+{
+ public struct Utf8Struct
+ {
+ [MarshalAs(UnmanagedType.LPUTF8Str)]
+ public string FirstName;
+ public int index;
+ }
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void TestStructWithUtf8Field(Utf8Struct utfStruct);
+ public static bool TestUTF8StructMarshalling(string[] utf8Strings)
+ {
+ Utf8Struct utf8Struct = new Utf8Struct();
+ for (int i = 0; i < utf8Strings.Length; i++)
+ {
+ utf8Struct.FirstName = utf8Strings[i];
+ utf8Struct.index = i;
+ TestStructWithUtf8Field(utf8Struct);
+ }
+ return true;
+ }
+}
+
+// UTF8 string as delegate parameter
+class UTF8DelegateMarshalling
+{
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void DelegateUTF8Parameter([MarshalAs(UnmanagedType.LPUTF8Str)]string utf8String, int index);
+
+ [DllImport("libtest", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Utf8DelegateAsParameter(DelegateUTF8Parameter param);
+
+ static bool failed;
+ public static bool TestUTF8DelegateMarshalling()
+ {
+ failed = false;
+ Utf8DelegateAsParameter(new DelegateUTF8Parameter(Utf8StringCallback));
+
+ return !failed;
+ }
+
+ public static void Utf8StringCallback(string nativeString, int index)
+ {
+ if (string.CompareOrdinal(nativeString, Test.utf8Strings[index]) != 0)
+ {
+ Console.WriteLine("Utf8StringCallback string do not match");
+ failed = true;
+ }
+ }
+}
+
+class Test
+{
+ //test strings
+ public static string[] utf8Strings = {
+ "Managed",
+ "Sîne klâwen durh die wolken sint geslagen" ,
+ "काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम्",
+ "我能吞下玻璃而不伤身体",
+ "ღმერთსი შემვედრე,შემვედრე, ნუთუ კვლა დამხსნას შემვედრე,სოფლისა შემვედრე, შემვედრე,შემვედრე,შემვედრე,შრომასა, ცეცხლს, წყალსა და მიწასა, ჰაერთა თანა მრომასა; მომცნეს ფრთენი და აღვფრინდე, მივჰხვდე მას ჩემსა ნდომასა, დღისით და ღამით ვჰხედვიდე მზისა ელვათა კრთომაასაშემვედრე,შემვედრე,",
+ "Τη γλώσσα μου έδωσαν ελληνική",
+ null,
+ };
+
+ public static int Main(string[] args)
+ {
+ // Test string as [In,Out] parameter
+ for (int i = 0; i < utf8Strings.Length; i++)
+ if (!UTF8StringTests.TestInOutStringParameter(utf8Strings[i], i))
+ return i+1;
+
+ // Test string as [Out] parameter
+ for (int i = 0; i < utf8Strings.Length; i++)
+ if (!UTF8StringTests.TestOutStringParameter(utf8Strings[i], i))
+ return i+100;
+
+ for (int i = 0; i < utf8Strings.Length - 1; i++)
+ if (!UTF8StringTests.TestStringPassByOut(utf8Strings[i], i))
+ return i+200;
+
+ for (int i = 0; i < utf8Strings.Length - 1; i++)
+ if (!UTF8StringTests.TestStringPassByRef(utf8Strings[i], i))
+ return i+300;
+
+
+ // Test StringBuilder as [In,Out] parameter
+ for (int i = 0; i < utf8Strings.Length - 1; i++)
+ if (!UTF8StringBuilderTests.TestInOutStringBuilderParameter(utf8Strings[i], i))
+ return i+400;
+
+#if NOT_YET
+ // This requires support for [Out] in StringBuilder
+
+ // Test StringBuilder as [Out] parameter
+ for (int i = 0; i < utf8Strings.Length - 1; i++){
+ if (!UTF8StringBuilderTests.TestOutStringBuilderParameter(utf8Strings[i], i))
+ return i+500;
+ }
+
+#endif
+
+ // utf8 string as struct fields
+ if (!UTF8StructMarshalling.TestUTF8StructMarshalling(utf8Strings))
+ return 600;
+
+ // delegate
+ try {
+ UTF8DelegateMarshalling.TestUTF8DelegateMarshalling();
+ } catch (ExecutionEngineException){
+ // Known issue on AOT - we do not AOT this yet.
+ }
+
+#if NOT_YET
+ // This requires special support for StringBuilder return values
+ // Test StringBuilder as return value
+ for (int i = 0; i < utf8Strings.Length - 1; i++)
+ if (!UTF8StringBuilderTests.TestReturnStringBuilder(utf8Strings[i], i))
+ return 700+i;
+#endif
+ // String.Empty tests
+ if (!UTF8StringTests.EmptyStringTest())
+ return 800;
+
+ return 0;
+ }
+}