From: Paul Gofman pgofman@codeweavers.com
--- dlls/msvcp140/msvcp140.spec | 6 +-- dlls/msvcp140/tests/msvcp140.c | 90 +++++++++++++++++++++++++++++++++- dlls/msvcp90/locale.c | 48 ++++++++++-------- dlls/msvcp90/msvcp90.h | 2 + 4 files changed, 121 insertions(+), 25 deletions(-)
diff --git a/dlls/msvcp140/msvcp140.spec b/dlls/msvcp140/msvcp140.spec index 600cbfa345c..0a8af0ec04b 100644 --- a/dlls/msvcp140/msvcp140.spec +++ b/dlls/msvcp140/msvcp140.spec @@ -175,9 +175,9 @@ @ cdecl -arch=arm ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@I@Z(ptr ptr long) codecvt_char16_ctor_locinfo @ thiscall -arch=i386 ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@I@Z(ptr ptr long) codecvt_char16_ctor_locinfo @ cdecl -arch=win64 ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@_K@Z(ptr ptr long) codecvt_char16_ctor_locinfo -@ cdecl -arch=arm ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z(ptr long long long) codecvt_char16_ctor_mode -@ thiscall -arch=i386 ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z(ptr long long long) codecvt_char16_ctor_mode -@ cdecl -arch=win64 ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z(ptr long long long) codecvt_char16_ctor_mode +@ cdecl -arch=arm ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z(ptr ptr long long long) codecvt_char16_ctor_mode +@ thiscall -arch=i386 ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z(ptr ptr long long long) codecvt_char16_ctor_mode +@ cdecl -arch=win64 ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z(ptr ptr long long long) codecvt_char16_ctor_mode @ cdecl -arch=arm ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@I@Z(ptr long) codecvt_char16_ctor_refs @ thiscall -arch=i386 ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@I@Z(ptr long) codecvt_char16_ctor_refs @ cdecl -arch=win64 ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@_K@Z(ptr long) codecvt_char16_ctor_refs diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index b1ab13fe80b..bc65d80bb85 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -89,6 +89,8 @@ struct thiscall_thunk
static void * (WINAPI *call_thiscall_func1)( void *func, void *this ); static void * (WINAPI *call_thiscall_func2)( void *func, void *this, const void *a ); +static void * (WINAPI *call_thiscall_func5)( void *func, void *this, const void *a, const void *b, + const void *c, const void *d );
static void init_thiscall_thunk(void) { @@ -101,16 +103,20 @@ static void init_thiscall_thunk(void) thunk->jmp_edx = 0xe2ff; /* jmp *%edx */ call_thiscall_func1 = (void *)thunk; call_thiscall_func2 = (void *)thunk; + call_thiscall_func5 = (void *)thunk; }
#define call_func1(func,_this) call_thiscall_func1(func,_this) -#define call_func2(func,_this,a) call_thiscall_func2(func,_this,a) +#define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a)) +#define call_func5(func,_this,a,b,c,d) call_thiscall_func5(func,_this,(const void*)(a),(const void*)(b), \ + (const void*)(c), (const void*)(d))
#else
#define init_thiscall_thunk() #define call_func1(func,_this) func(_this) #define call_func2(func,_this,a) func(_this,a) +#define call_func5(func,_this,a,b,c,d) func(_this,a,b,c,d)
#endif /* __i386__ */ typedef unsigned char MSVCP_bool; @@ -284,6 +290,40 @@ static int (__cdecl *p__unlink)(const char*);
static BOOLEAN (WINAPI *pCreateSymbolicLinkW)(const WCHAR *, const WCHAR *, DWORD);
+typedef void (*vtable_ptr)(void); +typedef SIZE_T MSVCP_size_t; + +/* class locale::facet */ +typedef struct { + const vtable_ptr *vtable; + unsigned int refs; +} locale_facet; + +/* class codecvt_base */ +typedef struct { + locale_facet facet; +} codecvt_base; + +typedef enum convert_mode +{ + consume_header = 4, + generate_header = 2, + little_endian = 1 +} codecvt_convert_mode; + +/* class codecvt<char16> */ +typedef struct { + codecvt_base base; + unsigned int max_code; + codecvt_convert_mode convert_mode; +} codecvt_char16; + +static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); +static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, unsigned int refs); +static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo, + ULONG max_code, codecvt_convert_mode mode, unsigned int refs); +static void (__thiscall * p_codecvt_char16_dtor)(codecvt_char16 *this); + static HMODULE msvcp; #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -323,6 +363,10 @@ static BOOL init(void)
SET(p__Fiopen_wchar, "?_Fiopen@std@@YAPEAU_iobuf@@PEB_WHH@Z"); SET(p__Fiopen, "?_Fiopen@std@@YAPEAU_iobuf@@PEBDHH@Z"); + SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QEAAXXZ"); + SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@_K@Z"); + SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z"); + SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MEAA@XZ"); } else { #ifdef __arm__ SET(p_task_continuation_context_ctor, "??0task_continuation_context@Concurrency@@AAA@XZ"); @@ -336,6 +380,10 @@ static BOOL init(void) SET(p__TaskEventLogger__LogTaskExecutionCompleted, "?_LogTaskExecutionCompleted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); SET(p__TaskEventLogger__LogWorkItemCompleted, "?_LogWorkItemCompleted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); SET(p__TaskEventLogger__LogWorkItemStarted, "?_LogWorkItemStarted@_TaskEventLogger@details@Concurrency@@QAAXXZ"); + SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QAAXXZ"); + SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@I@Z"); + SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); + SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MAA@XZ(ptr)"); #else SET(p_task_continuation_context_ctor, "??0task_continuation_context@Concurrency@@AAE@XZ"); SET(p__ContextCallback__Assign, "?_Assign@_ContextCallback@details@Concurrency@@AAEXPAX@Z"); @@ -348,6 +396,10 @@ static BOOL init(void) SET(p__TaskEventLogger__LogTaskExecutionCompleted, "?_LogTaskExecutionCompleted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); SET(p__TaskEventLogger__LogWorkItemCompleted, "?_LogWorkItemCompleted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); SET(p__TaskEventLogger__LogWorkItemStarted, "?_LogWorkItemStarted@_TaskEventLogger@details@Concurrency@@QAEXXZ"); + SET(p_codecvt_char16_ctor, "??_F?$codecvt@_SDU_Mbstatet@@@std@@QAEXXZ"); + SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@I@Z"); + SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); + SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MAE@XZ"); #endif SET(p__Schedule_chore, "?_Schedule_chore@details@Concurrency@@YAHPAU_Threadpool_chore@12@@Z"); SET(p__Reschedule_chore, "?_Reschedule_chore@details@Concurrency@@YAHPBU_Threadpool_chore@12@@Z"); @@ -1810,6 +1862,41 @@ static void test__Fiopen(void) p_setlocale(LC_ALL, "C"); }
+void test_codecvt_char16(void) +{ + char buffer[256]; + codecvt_char16 *this, *this2; + + this = (codecvt_char16 *)buffer; + memset(buffer, 0xcc, sizeof(buffer)); + this2 = call_func1(p_codecvt_char16_ctor, this); + ok(!this->base.facet.refs, "got %u.\n", this->base.facet.refs); + ok(this->convert_mode == consume_header, "got %#x.\n", this->max_code); + ok(this->max_code == MAX_UCSCHAR, "got %#x.\n", this->max_code); + ok(*(unsigned int *)(buffer + sizeof(*this)) == 0xcccccccc, "got %#x.\n", *(unsigned int *)(buffer + sizeof(*this))); + call_func1(p_codecvt_char16_dtor, this); + + this = (codecvt_char16 *)buffer; + memset(buffer, 0xcc, sizeof(buffer)); + this2 = call_func2(p_codecvt_char16_ctor_refs, this, 12); + ok(this2 == this, "got %p, %p.\n", this2, this); + ok(this->base.facet.refs == 12, "got %u.\n", this->base.facet.refs); + ok(this->convert_mode == consume_header, "got %#x.\n", this->max_code); + ok(this->max_code == MAX_UCSCHAR, "got %#x.\n", this->max_code); + ok(*(unsigned int *)(buffer + sizeof(*this)) == 0xcccccccc, "got %#x.\n", *(unsigned int *)(buffer + sizeof(*this))); + call_func1(p_codecvt_char16_dtor, this); + + this = (codecvt_char16 *)buffer; + memset(buffer, 0xcc, sizeof(buffer)); + this2 = call_func5(p_codecvt_char16_ctor_mode, this, (void *)0xdeadbeef, 0x55, 0x44, 12); + ok(this2 == this, "got %p, %p.\n", this2, this); + ok(this->base.facet.refs == 12, "got %#x.\n", this->base.facet.refs); + ok(this->convert_mode == 0x44, "got %#x.\n", this->convert_mode); + ok(this->max_code == 0x55, "got %#x.\n", this->max_code); + ok(*(unsigned int *)(buffer + sizeof(*this)) == 0xcccccccc, "got %#x.\n", *(unsigned int *)(buffer + sizeof(*this))); + call_func1(p_codecvt_char16_dtor, this); +} + START_TEST(msvcp140) { if(!init()) return; @@ -1839,5 +1926,6 @@ START_TEST(msvcp140) test_Copy_file(); test__Mtx(); test__Fiopen(); + test_codecvt_char16(); FreeLibrary(msvcp); } diff --git a/dlls/msvcp90/locale.c b/dlls/msvcp90/locale.c index 192ee8b98ef..069251661aa 100644 --- a/dlls/msvcp90/locale.c +++ b/dlls/msvcp90/locale.c @@ -4006,46 +4006,51 @@ void __thiscall codecvt_char16__Init(codecvt_char16 *this, const _Locinfo *locin FIXME("(%p %p) stub\n", this, locinfo); }
+/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z */ +/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z */ +/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z */ +DEFINE_THISCALL_WRAPPER(codecvt_char16_ctor_mode, 16) +codecvt_char16* __thiscall codecvt_char16_ctor_mode(codecvt_char16 *this, const _Locinfo *locinfo, + ULONG max_code, codecvt_convert_mode mode, unsigned int refs) +{ + TRACE("(%p %ld %d %u)\n", this, max_code, mode, refs); + + codecvt_base_ctor_refs(&this->base, refs); + this->base.facet.vtable = &codecvt_char16_vtable; + this->convert_mode = mode; + this->max_code = max_code; + return this; +} + /* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@I@Z */ /* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@I@Z */ /* ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@_K@Z */ DEFINE_THISCALL_WRAPPER(codecvt_char16_ctor_locinfo, 12) codecvt_char16* __thiscall codecvt_char16_ctor_locinfo(codecvt_char16 *this, - const _Locinfo *locinfo, size_t refs) + const _Locinfo *locinfo, unsigned int refs) { - FIXME("(%p %p %Iu) stub\n", this, locinfo, refs); - return NULL; + TRACE("(%p %p %u)\n", this, locinfo, refs); + return codecvt_char16_ctor_mode(this, locinfo, MAX_UCSCHAR, consume_header, refs); }
/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@I@Z */ /* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@I@Z */ /* ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@_K@Z */ DEFINE_THISCALL_WRAPPER(codecvt_char16_ctor_refs, 8) -codecvt_char* __thiscall codecvt_char16_ctor_refs(codecvt_char16 *this, size_t refs) -{ - FIXME("(%p %Iu) stub\n", this, refs); - return NULL; -} - -/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z */ -/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z */ -/* ??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z */ -DEFINE_THISCALL_WRAPPER(codecvt_char16_ctor_mode, 16) -codecvt_char16* __thiscall codecvt_char16_ctor_mode(codecvt_char16 *this, - ULONG max_code, codecvt_convert_mode mode, size_t refs) +codecvt_char16* __thiscall codecvt_char16_ctor_refs(codecvt_char16 *this, unsigned int refs) { - FIXME("(%p %ld %d %Iu) stub\n", this, max_code, mode, refs); - return NULL; + TRACE("(%p %u)\n", this, refs); + return codecvt_char16_ctor_locinfo(this, NULL, refs); }
/* ??_F?$codecvt@_SDU_Mbstatet@@@std@@QAAXXZ */ /* ??_F?$codecvt@_SDU_Mbstatet@@@std@@QAEXXZ */ /* ??_F?$codecvt@_SDU_Mbstatet@@@std@@QEAAXXZ */ DEFINE_THISCALL_WRAPPER(codecvt_char16_ctor, 4) -codecvt_char* __thiscall codecvt_char16_ctor(codecvt_char16 *this) +codecvt_char16* __thiscall codecvt_char16_ctor(codecvt_char16 *this) { - FIXME("(%p) stub\n", this); - return NULL; + TRACE("(%p)\n", this); + return codecvt_char16_ctor_refs(this, 0); }
/* ??1?$codecvt@_SDU_Mbstatet@@@std@@MAA@XZ */ @@ -4054,7 +4059,8 @@ codecvt_char* __thiscall codecvt_char16_ctor(codecvt_char16 *this) DEFINE_THISCALL_WRAPPER(codecvt_char16_dtor, 4) void __thiscall codecvt_char16_dtor(codecvt_char16 *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + codecvt_base_dtor(&this->base); }
DEFINE_THISCALL_WRAPPER(codecvt_char16_vector_dtor, 8) diff --git a/dlls/msvcp90/msvcp90.h b/dlls/msvcp90/msvcp90.h index b6e20826ff7..1994b49473b 100644 --- a/dlls/msvcp90/msvcp90.h +++ b/dlls/msvcp90/msvcp90.h @@ -248,6 +248,8 @@ typedef enum convert_mode /* class codecvt<char16> */ typedef struct { codecvt_base base; + unsigned int max_code; + codecvt_convert_mode convert_mode; } codecvt_char16;
/* class codecvt<char32> */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/msvcp140/tests/msvcp140.c | 245 ++++++++++++++++++++++++++++++++- dlls/msvcp90/locale.c | 86 +++++++++++- 2 files changed, 326 insertions(+), 5 deletions(-)
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index bc65d80bb85..3fb5570c6e6 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <locale.h> #include <share.h> +#include <uchar.h>
#include "windef.h" #include "winbase.h" @@ -91,6 +92,8 @@ static void * (WINAPI *call_thiscall_func1)( void *func, void *this ); static void * (WINAPI *call_thiscall_func2)( void *func, void *this, const void *a ); static void * (WINAPI *call_thiscall_func5)( void *func, void *this, const void *a, const void *b, const void *c, const void *d ); +static void * (WINAPI *call_thiscall_func8)( void *func, void *this, const void *a, const void *b, + const void *c, const void *d, const void *e, const void *f, const void *g );
static void init_thiscall_thunk(void) { @@ -104,12 +107,15 @@ static void init_thiscall_thunk(void) call_thiscall_func1 = (void *)thunk; call_thiscall_func2 = (void *)thunk; call_thiscall_func5 = (void *)thunk; + call_thiscall_func8 = (void *)thunk; }
#define call_func1(func,_this) call_thiscall_func1(func,_this) #define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a)) #define call_func5(func,_this,a,b,c,d) call_thiscall_func5(func,_this,(const void*)(a),(const void*)(b), \ (const void*)(c), (const void*)(d)) +#define call_func8(func,_this,a,b,c,d,e,f,g) call_thiscall_func8(func,_this,(const void*)(a),(const void*)(b), \ + (const void*)(c), (const void*)(d), (const void*)(e), (const void*)(f), (const void*)(g))
#else
@@ -117,6 +123,7 @@ static void init_thiscall_thunk(void) #define call_func1(func,_this) func(_this) #define call_func2(func,_this,a) func(_this,a) #define call_func5(func,_this,a,b,c,d) func(_this,a,b,c,d) +#define call_func8(func,_this,a,b,c,d,e,f,g) func(_this,a,b,c,d,e,f,g)
#endif /* __i386__ */ typedef unsigned char MSVCP_bool; @@ -318,11 +325,29 @@ typedef struct { codecvt_convert_mode convert_mode; } codecvt_char16;
+typedef struct { + int wchar; + unsigned short byte, state; +} _Mbstatet; + +typedef enum { + CODECVT_ok = 0, + CODECVT_partial = 1, + CODECVT_error = 2, + CODECVT_noconv = 3 +} codecvt_base_result; + static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, unsigned int refs); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo, ULONG max_code, codecvt_convert_mode mode, unsigned int refs); static void (__thiscall * p_codecvt_char16_dtor)(codecvt_char16 *this); +static int (__thiscall * p_codecvt_char16_do_out)(const codecvt_char16 *this, _Mbstatet *state, + const char16_t *from, const char16_t *from_end, const char16_t **from_next, + char *to, char *to_end, char **to_next); +static int (__thiscall * p_codecvt_char16_do_in)(const codecvt_char16 *this, _Mbstatet *state, + const char *from, const char *from_end, const char **from_next, + char16_t *to, char16_t *to_end, char16_t **to_next);
static HMODULE msvcp; #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) @@ -367,6 +392,8 @@ static BOOL init(void) SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@_K@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QEAA@AEBV_Locinfo@1@KW4_Codecvt_mode@1@_K@Z"); SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MEAA@XZ"); + SET(p_codecvt_char16_do_out, "?do_out@?$codecvt@_SDU_Mbstatet@@@std@@MEBAHAEAU_Mbstatet@@PEB_S1AEAPEB_SPEAD3AEAPEAD@Z"); + SET(p_codecvt_char16_do_in, "?do_in@?$codecvt@_SDU_Mbstatet@@@std@@MEBAHAEAU_Mbstatet@@PEBD1AEAPEBDPEA_S3AEAPEA_S@Z"); } else { #ifdef __arm__ SET(p_task_continuation_context_ctor, "??0task_continuation_context@Concurrency@@AAA@XZ"); @@ -384,6 +411,8 @@ static BOOL init(void) SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@I@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAA@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MAA@XZ(ptr)"); + SET(p_codecvt_char16_do_out, "?do_out@?$codecvt@_SDU_Mbstatet@@@std@@MBAHAAU_Mbstatet@@PB_S1AAPB_SPAD3AAPAD@Z"); + SET(p_codecvt_char16_do_in, "?do_in@?$codecvt@_SDU_Mbstatet@@@std@@MBAHAAU_Mbstatet@@PBD1AAPBDPA_S3AAPA_S@Z"); #else SET(p_task_continuation_context_ctor, "??0task_continuation_context@Concurrency@@AAE@XZ"); SET(p__ContextCallback__Assign, "?_Assign@_ContextCallback@details@Concurrency@@AAEXPAX@Z"); @@ -400,6 +429,8 @@ static BOOL init(void) SET(p_codecvt_char16_ctor_refs, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@I@Z"); SET(p_codecvt_char16_ctor_mode, "??0?$codecvt@_SDU_Mbstatet@@@std@@QAE@ABV_Locinfo@1@KW4_Codecvt_mode@1@I@Z"); SET(p_codecvt_char16_dtor, "??1?$codecvt@_SDU_Mbstatet@@@std@@MAE@XZ"); + SET(p_codecvt_char16_do_out, "?do_out@?$codecvt@_SDU_Mbstatet@@@std@@MBEHAAU_Mbstatet@@PB_S1AAPB_SPAD3AAPAD@Z"); + SET(p_codecvt_char16_do_in, "?do_in@?$codecvt@_SDU_Mbstatet@@@std@@MBEHAAU_Mbstatet@@PBD1AAPBDPA_S3AAPA_S@Z"); #endif SET(p__Schedule_chore, "?_Schedule_chore@details@Concurrency@@YAHPAU_Threadpool_chore@12@@Z"); SET(p__Reschedule_chore, "?_Reschedule_chore@details@Concurrency@@YAHPBU_Threadpool_chore@12@@Z"); @@ -1862,10 +1893,42 @@ static void test__Fiopen(void) p_setlocale(LC_ALL, "C"); }
+static const char bom_header[] = { 0xef, 0xbb, 0xbf }; + void test_codecvt_char16(void) { - char buffer[256]; + static const struct + { + const WCHAR *wstr; + const char *str; + int short_output; + } + tests[] = + { + { L"\xfeff", "\xef\xbb\xbf" }, + { L"\xfffe", "\xef\xbf\xbe" }, + { L"\xfeff""a", "\xef\xbb\xbf""a", 1 }, + { L"abc", "abc", 1 }, + { L"\x2a8", "\xca\xa8" }, + { L"\xd83d\xdcd8\xd83d\xde79", "\xf0\x9f\x93\x98\xf0\x9f\x99\xb9", 3 }, + { L"\x08a4", "\xe0\xa2\xa4", }, + { L"\x01a7", "\xc6\xa7", }, + { L"\x01a7\x08a4", "\xc6\xa7\xe0\xa2\xa4", 3}, + }; + static DWORD test_flags[] = + { + 0, + consume_header, + generate_header, + consume_header | generate_header, + }; + char16_t str16[16], *str16_ptr; codecvt_char16 *this, *this2; + unsigned int i, j, len, wlen; + char str[16], expect_str[16]; + char buffer[256], *str_ptr; + _Mbstatet state; + int ret;
this = (codecvt_char16 *)buffer; memset(buffer, 0xcc, sizeof(buffer)); @@ -1888,13 +1951,189 @@ void test_codecvt_char16(void)
this = (codecvt_char16 *)buffer; memset(buffer, 0xcc, sizeof(buffer)); - this2 = call_func5(p_codecvt_char16_ctor_mode, this, (void *)0xdeadbeef, 0x55, 0x44, 12); + this2 = call_func5(p_codecvt_char16_ctor_mode, this, (void *)0xdeadbeef, 0xffffffff, 0x44, 12); ok(this2 == this, "got %p, %p.\n", this2, this); ok(this->base.facet.refs == 12, "got %#x.\n", this->base.facet.refs); ok(this->convert_mode == 0x44, "got %#x.\n", this->convert_mode); - ok(this->max_code == 0x55, "got %#x.\n", this->max_code); + ok(this->max_code == 0xffffffff, "got %#x.\n", this->max_code); ok(*(unsigned int *)(buffer + sizeof(*this)) == 0xcccccccc, "got %#x.\n", *(unsigned int *)(buffer + sizeof(*this))); call_func1(p_codecvt_char16_dtor, this); + + for (j = 0; j < ARRAY_SIZE(test_flags); ++j) + { + winetest_push_context("flags %#lx", test_flags[j]); + this = (codecvt_char16 *)buffer; + memset(buffer, 0xcc, sizeof(buffer)); + call_func5(p_codecvt_char16_ctor_mode, this, (void *)0xdeadbeef, MAX_UCSCHAR, test_flags[j], 0); + + str16[0] = 'a'; + memset(&state, 0, sizeof(state)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str, &str_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16, (const char16_t **)&str16_ptr, str, str, &str_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + wcscpy(str16, L"\xd83d\xdcd8\xd83d\xde79"); + + memset(&state, 0, sizeof(state)); + memset(str, 0, sizeof(str)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str + 1, &str_ptr); + if (test_flags[j] & generate_header) + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!strcmp(str, "\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); + } + + memset(&state, 0, sizeof(state)); + memset(str, 0, sizeof(str)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str + 2, &str_ptr); + if (test_flags[j] & generate_header) + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!strcmp(str, "\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); + } + + memset(&state, 0, sizeof(state)); + memset(str, 0, sizeof(str)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str + 3, &str_ptr); + if (test_flags[j] & generate_header) + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!strcmp(str, "\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); + } + + memset(&state, 0, sizeof(state)); + memset(str, 0, sizeof(str)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str + 4, &str_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + if (test_flags[j] & generate_header) + { + ok(!strcmp(str, "\xef\xbb\xbf\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 4, "got %Id.\n", str_ptr - str); + } + else + { + ok(!strcmp(str, "\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + } + ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16_ptr, str16 + wcslen(str16), (const char16_t **)&str16_ptr, str, str + 8, &str_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 4, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 7, "got %Id.\n", str_ptr - str); + ok(!strcmp(str, "\x9f\x93\x98\xf0\x9f\x99\xb9"), "got %s.\n", debugstr_a(str)); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + memset(str, 0, sizeof(str)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str + 3, &str_ptr); + if (test_flags[j] & generate_header) + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!strcmp(str, "\xf0"), "got %s.\n", debugstr_a(str)); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("test %u", i); + wcscpy(str16, tests[i].wstr); + wlen = wcslen(str16); + len = strlen(tests[i].str); + memset(&state, 0, sizeof(state)); + if (test_flags[j] & generate_header) + { + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + wlen, (const char16_t **)&str16_ptr, str, str + len + 3, &str_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == wlen, "got %Id, expected %u.\n", str16_ptr - str16, wlen); + ok(str_ptr - str == len + 3, "got %Id, expected %u.\n", str_ptr - str, len + 3); + memcpy(expect_str, bom_header, sizeof(bom_header)); + strcpy(expect_str + sizeof(bom_header), tests[i].str); + ok(!strncmp(str, expect_str, len + 3), "got %s, expected %s.\n", debugstr_an(str, len), debugstr_an(expect_str, len + 3)); + + memset(&state, 0, sizeof(state)); + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + wlen, (const char16_t **)&str16_ptr, str, str + 2, &str_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id, expected %u.\n", str16_ptr - str16, 0); + ok(str_ptr - str == 0, "got %Id, expected %u.\n", str_ptr - str, 0); + } + else + { + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + wlen, (const char16_t **)&str16_ptr, str, str + len, &str_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == wlen, "got %Id, expected %u.\n", str16_ptr - str16, wlen); + ok(str_ptr - str == len, "got %Id, expected %u.\n", str_ptr - str, len); + ok(!strncmp(str, tests[i].str, len), "got %s, expected %s.\n", debugstr_an(str, len), debugstr_an(tests[i].str, len)); + + ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + wlen, (const char16_t **)&str16_ptr, str, str + len - 1, &str_ptr); + if (tests[i].short_output) + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == wlen - 1, "got %Id, expected %u.\n", str16_ptr - str16, wlen - 1); + ok(str_ptr - str == len - tests[i].short_output, "got %Id, expected %u.\n", str_ptr - str, len - tests[i].short_output); + ok(!strncmp(str, tests[i].str, len - 1), "got %s, expected %s.\n", debugstr_an(str, len - 1), debugstr_an(tests[i].str, len - 1)); + } + else + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id, expected %u.\n", str16_ptr - str16, 0); + ok(str_ptr - str == 0, "got %Id, expected %u.\n", str_ptr - str, 0); + } + } + + winetest_pop_context(); + } + call_func1(p_codecvt_char16_dtor, this); + winetest_pop_context(); + } }
START_TEST(msvcp140) diff --git a/dlls/msvcp90/locale.c b/dlls/msvcp90/locale.c index 069251661aa..0eb47bd6ab5 100644 --- a/dlls/msvcp90/locale.c +++ b/dlls/msvcp90/locale.c @@ -4193,9 +4193,91 @@ int __thiscall codecvt_char16_do_out(const codecvt_char16 *this, _Mbstatet *stat const char16_t *from, const char16_t *from_end, const char16_t **from_next, char *to, char *to_end, char **to_next) { - FIXME("(%p %p %p %p %p %p %p %p) stub\n", this, state, from, + static const char bom_header[] = { 0xef, 0xbb, 0xbf }; + unsigned int ch, out_len; + BOOL surrogate; + + TRACE("(%p %p %p %p %p %p %p %p)\n", this, state, from, from_end, from_next, to, to_end, to_next); - return 0; + + if (this->convert_mode & ~(generate_header | consume_header)) + FIXME("convert_mode %#x.\n", this->convert_mode); + + *from_next = from; + *to_next = to; + + while(*from_next != from_end && *to_next != to_end) + { + surrogate = FALSE; + + if ((ch = MBSTATET_TO_INT(state)) & ~1) + { + if (!IS_LOW_SURROGATE(**from_next)) + return CODECVT_error; + ch = (ch << 10) + (**from_next & 0x3ff); + } + else if (IS_HIGH_SURROGATE(**from_next)) + { + surrogate = TRUE; + ch = 0x10000 + ((**from_next & 0x3ff) << 10); + } + else + { + ch = **from_next; + } + + if (ch < 0x80 || surrogate) + out_len = 1; + else if (ch < 0x800) + out_len = 2; + else + out_len = 3; + + if (this->convert_mode & generate_header && !MBSTATET_TO_INT(state)) + { + if (out_len + sizeof(bom_header) > to_end - *to_next) + break; + memcpy(*to_next, bom_header, sizeof(bom_header)); + *to_next += sizeof(bom_header); + } + else if (out_len > to_end - *to_next) + break; + + ++*from_next; + if (surrogate) + { + MBSTATET_TO_INT(state) = ((ch - 0x10000) >> 10) + 0x40; + *(*to_next)++ = 0xf0 | (ch >> 18); + continue; + } + if (ch < 0x80) + { + *(*to_next)++ = ch; + } + else if (ch < 0x800) + { + *(*to_next)++ = 0xc0 | (ch >> 6); + *(*to_next)++ = 0x80 | (ch & 0x3f); + } + else if (ch < 0x10000) + { + *(*to_next)++ = 0xe0 | (ch >> 12); + *(*to_next)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*to_next)++ = 0x80 | (ch & 0x3f); + } + else + { + *(*to_next)++ = 0x80 | ((ch >> 12) & 0x3f); + *(*to_next)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*to_next)++ = 0x80 | (ch & 0x3f); + } + MBSTATET_TO_INT(state) = 1; + } + + if (*from_next != from) + return CODECVT_ok; + + return CODECVT_partial; }
/* ?out@?$codecvt@_SDU_Mbstatet@@@std@@QBAHAAU_Mbstatet@@PB_S1AAPB_SPAD3AAPAD@Z */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/msvcp140/tests/msvcp140.c | 210 +++++++++++++++++++++++++++++++++ dlls/msvcp90/locale.c | 122 ++++++++++++++++++- 2 files changed, 330 insertions(+), 2 deletions(-)
diff --git a/dlls/msvcp140/tests/msvcp140.c b/dlls/msvcp140/tests/msvcp140.c index 3fb5570c6e6..35b10b6604e 100644 --- a/dlls/msvcp140/tests/msvcp140.c +++ b/dlls/msvcp140/tests/msvcp140.c @@ -1895,6 +1895,11 @@ static void test__Fiopen(void)
static const char bom_header[] = { 0xef, 0xbb, 0xbf };
+static BOOL str_has_bom_header(const char *str) +{ + return !strncmp(str, bom_header, sizeof(bom_header)); +} + void test_codecvt_char16(void) { static const struct @@ -2082,6 +2087,183 @@ void test_codecvt_char16(void) ok(state.wchar == 0x7d, "got %#x.\n", state.wchar); }
+ strcpy(str, "abc"); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 2, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"ab"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 2, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 2, (const char **)&str_ptr, str16, str16 + 3, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 2, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"ab"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 2, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\x92\x80\x92"); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 2, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808\xdc12"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 4, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 1, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808\xdc12"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 2, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808\xdc12"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 1, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0xdc00, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 , &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 + 1, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0xdc00, "got %#x.\n", state.wchar); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0xdc00, "got %#x.\n", state.wchar); + + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str + 3, str + 4, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xdc12"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 4, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\x92\x80\x92"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 + 1, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd808"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0xdc00, "got %#x.\n", state.wchar); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str + 3, str + 4, (const char **)&str_ptr, str16, str16 + 1, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xdc12"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 4, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + strcpy(str, "\xe0\xa1\x93"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 1, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\x0853"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + strcpy(str, "\xe0\xa1\x93"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16, &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + strcpy(str, "\xe0\xa1\x93"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 2, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\xff\xff\xff"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 2, (const char **)&str_ptr, str16, str16 + 4, &str16_ptr); + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\xff\xff\xff"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 4, &str16_ptr); + ok(ret == CODECVT_error, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\x82\x80\x92"); + + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 + 2, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\x2012"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 4, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + + strcpy(str, "\xf0\xff\xff\xff"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 4, (const char **)&str_ptr, str16, str16 + 4, &str16_ptr); + ok(ret == CODECVT_error, "got %d.\n", ret); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + ok(str_ptr - str == 1, "got %Id.\n", str_ptr - str); + ok(state.wchar == 0, "got %#x.\n", state.wchar); + + strcpy(str, "\xed\xa0\x80"); + memset(&state, 0, sizeof(state)); + memset(str16, 0, sizeof(str16)); + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + 3, (const char **)&str_ptr, str16, str16 + 4, &str16_ptr); + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str16_ptr - str16 == 1, "got %Id.\n", str16_ptr - str16); + ok(!wcscmp(str16, L"\xd800"), "got %s.\n", debugstr_w(str16)); + ok(str_ptr - str == 3, "got %Id.\n", str_ptr - str); + ok(state.wchar == 1, "got %#x.\n", state.wchar); + for (i = 0; i < ARRAY_SIZE(tests); ++i) { winetest_push_context("test %u", i); @@ -2128,6 +2310,34 @@ void test_codecvt_char16(void) ok(str_ptr - str == 0, "got %Id, expected %u.\n", str_ptr - str, 0); } } + strcpy(str, tests[i].str); + memset(&state, 0, sizeof(state)); + + ret = (int)call_func8(p_codecvt_char16_do_in, this, &state, str, str + len, (const char **)&str_ptr, str16, str16 + wlen + 10, &str16_ptr); + if (test_flags[j] & consume_header && str_has_bom_header(str)) + { + if (strlen(str) == sizeof(bom_header)) + { + ok(ret == CODECVT_partial, "got %d.\n", ret); + ok(str_ptr - str == 0, "got %Id.\n", str_ptr - str); + ok(str16_ptr - str16 == 0, "got %Id.\n", str16_ptr - str16); + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str_ptr - str == len, "got %Id.\n", str_ptr - str); + ok(str16_ptr - str16 == wlen - 1, "got %Id.\n", str16_ptr - str16); + ok(!wcsncmp(str16, tests[i].wstr + 1, wlen - 1), "got %s, expected %s.\n", + debugstr_wn(str16, wlen), debugstr_wn(tests[i].wstr, wlen)); + } + } + else + { + ok(ret == CODECVT_ok, "got %d.\n", ret); + ok(str_ptr - str == len, "got %Id, expected %u.\n", str_ptr - str, len); + ok(str16_ptr - str16 == wlen, "got %Id, expected %u.\n", str16_ptr - str16, wlen); + ok(!wcsncmp(str16, tests[i].wstr, wlen), "got %s, expected %s.\n", debugstr_wn(str16, wlen), debugstr_wn(tests[i].wstr, wlen)); + }
winetest_pop_context(); } diff --git a/dlls/msvcp90/locale.c b/dlls/msvcp90/locale.c index 0eb47bd6ab5..66f7dae1400 100644 --- a/dlls/msvcp90/locale.c +++ b/dlls/msvcp90/locale.c @@ -4125,9 +4125,127 @@ int __thiscall codecvt_char16_do_in(const codecvt_char16 *this, _Mbstatet *state const char *from, const char *from_end, const char **from_next, char16_t *to, char16_t *to_end, char16_t **to_next) { - FIXME("(%p %p %p %p %p %p %p %p) stub\n", this, state, from, + static const char utf8_length[128] = + { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80-0x8f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90-0x9f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0-0xaf */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb0-0xbf */ + 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xc0-0xcf */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xd0-0xdf */ + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 0xe0-0xef */ + 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0 /* 0xf0-0xff */ + }; + /* first byte mask depending on UTF-8 sequence length */ + static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 }; + + unsigned int res, len; + unsigned char ch; + const char *end; + BOOL fourbyte; + + TRACE("(%p %p %p %p %p %p %p %p)\n", this, state, from, from_end, from_next, to, to_end, to_next); - return 0; + + if (this->convert_mode & ~(generate_header | consume_header)) + FIXME("convert_mode %#x.\n", this->convert_mode); + + *from_next = from; + *to_next = to; + + while (*from_next != from_end && *to_next != to_end) + { + fourbyte = FALSE; + ch = **from_next; + if ((res = MBSTATET_TO_INT(state)) & ~1) + { + ch = ch ^ 0x80; + if (ch >= 0x40) + return CODECVT_error; + res |= ch; + ++*from_next; + MBSTATET_TO_INT(state) = 1; + } + else if (ch < 0x80) + { + res = ch; + ++*from_next; + } + else + { + if (!(len = utf8_length[ch - 0x80])) + { + ++*from_next; + return CODECVT_error; + } + + if (from_end - *from_next < min(len, 3)) + break; + ++*from_next; + res = ch & utf8_mask[len - 1]; + end = *from_next + len - 1; + switch (len) + { + case 4: + if ((ch = end[-3] ^ 0x80) >= 0x40) + return CODECVT_error; + res = (res << 6) | ch; + fourbyte = TRUE; + /* fallthrough */ + case 3: + if ((ch = end[-2] ^ 0x80) >= 0x40) + return CODECVT_error; + res = (res << 6) | ch; + ++*from_next; + /* fallthrough */ + case 2: + if (len == 4) + ch = 0; + else if ((ch = end[-1] ^ 0x80) >= 0x40) + return CODECVT_error; + res = (res << 6) | ch; + ++*from_next; + break; + } + } + + if (res > this->max_code) + return CODECVT_error; + + if (fourbyte) + { + if (res > 0x10000) + { + res -= 0x10000; + MBSTATET_TO_INT(state) = 0xdc00 | (res & 0x3ff); + res = 0xd800 | (res >> 10); + } + else + { + MBSTATET_TO_INT(state) = res; + continue; + } + } + if (!MBSTATET_TO_INT(state)) + { + MBSTATET_TO_INT(state) = 1; + if (this->convert_mode & consume_header && res == 0xfeff) + { + if (*from_next == from_end) + { + *from_next = from; + break; + } + continue; + } + } + *(*to_next)++ = res; + } + + if (*from_next != from) + return CODECVT_ok; + + return CODECVT_partial; }
/* ?in@?$codecvt@_SDU_Mbstatet@@@std@@QBAHAAU_Mbstatet@@PBD1AAPBDPA_S3AAPA_S@Z */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/msvcp90/locale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/msvcp90/locale.c b/dlls/msvcp90/locale.c index 66f7dae1400..41daeaebe08 100644 --- a/dlls/msvcp90/locale.c +++ b/dlls/msvcp90/locale.c @@ -3876,7 +3876,7 @@ int __thiscall codecvt_wchar_do_out(const codecvt_wchar *this, _Mbstatet *state, case -1: return CODECVT_error; default: - if(size > from_end-*from_next) { + if(size > to_end - *to_next) { *state = old_state; return CODECVT_partial; }
Need by Stardust Skate (through "R66" Twitter integration plugin).
It looks like char16_t conversions are locale agnostic and purely convert between utf8 and utf16 (with some relaxed validation).
The last patch is not related, just looks like an obvious bug in wchar conversion implementation I spotted while working with the code.
Piotr Caban (@piotr) commented about dlls/msvcp140/tests/msvcp140.c:
- consume_header = 4,
- generate_header = 2,
- little_endian = 1
+} codecvt_convert_mode;
+/* class codecvt<char16> */ +typedef struct {
- codecvt_base base;
- unsigned int max_code;
- codecvt_convert_mode convert_mode;
+} codecvt_char16;
+static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); +static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, unsigned int refs); +static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo,
ULONG max_code, codecvt_convert_mode mode, unsigned int refs);
```suggestion:-3+0 static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, size_t refs); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo, ULONG max_code, codecvt_convert_mode mode, size_t refs); ```
Why are you changing last argument type to `unsigned int`? It doesn't match with mangled function name. There are similar changes that needs to be undone in implementation.
Piotr Caban (@piotr) commented about dlls/msvcp140/tests/msvcp140.c:
ok(this->convert_mode == 0x44, "got %#x.\n", this->convert_mode);
- ok(this->max_code == 0x55, "got %#x.\n", this->max_code);
- ok(this->max_code == 0xffffffff, "got %#x.\n", this->max_code); ok(*(unsigned int *)(buffer + sizeof(*this)) == 0xcccccccc, "got %#x.\n", *(unsigned int *)(buffer + sizeof(*this))); call_func1(p_codecvt_char16_dtor, this);
- for (j = 0; j < ARRAY_SIZE(test_flags); ++j)
- {
winetest_push_context("flags %#lx", test_flags[j]);
this = (codecvt_char16 *)buffer;
memset(buffer, 0xcc, sizeof(buffer));
call_func5(p_codecvt_char16_ctor_mode, this, (void *)0xdeadbeef, MAX_UCSCHAR, test_flags[j], 0);
str16[0] = 'a';
memset(&state, 0, sizeof(state));
ret = (int)call_func8(p_codecvt_char16_do_out, this, &state, str16, str16 + 1, (const char16_t **)&str16_ptr, str, str, &str_ptr);
Please try to avoid adding long lines (exceeding 120 chars).
Piotr Caban (@piotr) commented about dlls/msvcp140/tests/msvcp140.c:
p_setlocale(LC_ALL, "C");
}
+void test_codecvt_char16(void) +{
- char buffer[256];
- codecvt_char16 *this, *this2;
- this = (codecvt_char16 *)buffer;
Is there any reason for using `char[256]` buffer here instead of `codecvt_char16`? It's usually not very useful to check structure size this way since some fields may be unused in constructor.
Piotr Caban (@piotr) commented about dlls/msvcp140/tests/msvcp140.c:
- CODECVT_partial = 1,
- CODECVT_error = 2,
- CODECVT_noconv = 3
+} codecvt_base_result;
static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); static codecvt_char16 *(__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, unsigned int refs); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo, ULONG max_code, codecvt_convert_mode mode, unsigned int refs); static void (__thiscall * p_codecvt_char16_dtor)(codecvt_char16 *this); +static int (__thiscall * p_codecvt_char16_do_out)(const codecvt_char16 *this, _Mbstatet *state,
const char16_t *from, const char16_t *from_end, const char16_t **from_next,
char *to, char *to_end, char **to_next);
+static int (__thiscall * p_codecvt_char16_do_in)(const codecvt_char16 *this, _Mbstatet *state,
const char *from, const char *from_end, const char **from_next,
char16_t *to, char16_t *to_end, char16_t **to_next);
It's not important but this line belongs to next patch.
I forgot to mention: there is also compilation failure that needs to be addressed.
On Tue Apr 1 14:55:43 2025 +0000, Piotr Caban wrote:
static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor)(codecvt_char16 *this); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_refs)(codecvt_char16 *this, size_t refs); static codecvt_char16 * (__thiscall * p_codecvt_char16_ctor_mode)(codecvt_char16 *this, void *locinfo, ULONG max_code, codecvt_convert_mode mode, size_t refs);
Why are you changing last argument type to `unsigned int`? It doesn't match with mangled function name. There are similar changes that needs to be undone in implementation.
[test.patch](/uploads/9a6536295ba63cb61806540eb24351a5/test.patch)
The attached test (on top of my patches) shows that refs in the structure at least is not size_t, I was testing something like that initially. I should probably get the constructor arguments back but change refs in the structure in the implementation? It doesn't affect much because the structure is aligned anyway.
On Tue Apr 1 14:55:44 2025 +0000, Piotr Caban wrote:
Is there any reason for using `char[256]` buffer here instead of `codecvt_char16`? It's usually not very useful to check structure size this way since some fields may be unused in constructor.
That was the post-structure canary values check, yes. I will remove those checks and the buffer.
On Tue Apr 1 15:28:29 2025 +0000, Paul Gofman wrote:
[test.patch](/uploads/9a6536295ba63cb61806540eb24351a5/test.patch) The attached test (on top of my patches) shows that refs in the structure at least is not size_t, I was testing something like that initially. I should probably get the constructor arguments back but change refs in the structure in the implementation? It doesn't affect much because the structure is aligned anyway.
This change will break similar test in msvcp90: ```diff diff --git a/dlls/msvcp90/tests/ios.c b/dlls/msvcp90/tests/ios.c index 91da0b91dd8..eb4febeb57b 100644 --- a/dlls/msvcp90/tests/ios.c +++ b/dlls/msvcp90/tests/ios.c @@ -2586,7 +2586,9 @@ static void test_time_get__Getint(void) time_get_char time_get; int i, ret, v;
+ memset(&time_get, 0xcc, sizeof(time_get)); call_func1(p_time_get_char_ctor, &time_get); + trace("--> %Ix\n", time_get.facet.refs); ```
It looks like locale_facet was changed but only in msvcp140 version (I didn't check anything except msvcp90 and msvcp140, what version it changed in needs to be verified).
On Tue Apr 1 16:08:20 2025 +0000, Piotr Caban wrote:
This change will break similar test in msvcp90:
diff --git a/dlls/msvcp90/tests/ios.c b/dlls/msvcp90/tests/ios.c index 91da0b91dd8..eb4febeb57b 100644 --- a/dlls/msvcp90/tests/ios.c +++ b/dlls/msvcp90/tests/ios.c @@ -2586,7 +2586,9 @@ static void test_time_get__Getint(void) time_get_char time_get; int i, ret, v; + memset(&time_get, 0xcc, sizeof(time_get)); call_func1(p_time_get_char_ctor, &time_get); + trace("--> %Ix\n", time_get.facet.refs);
It looks like locale_facet was changed but only in msvcp140 version (I didn't check anything except msvcp90 and msvcp140, what version it changed in needs to be verified).
The attached ad-hoc test suggests it changed in msvcp110:
``` ios.c:2609: Test failed: got the same function. ios.c:2612: msvcp90.dll --> 0 ios.c:2612: msvcp100.dll --> 0 ios.c:2612: msvcp110.dll --> cccccccc00000000 ios.c:2612: msvcp120.dll --> cccccccc00000000 ios.c:2612: msvcp140.dll --> cccccccc00000000 ``` [test.patch](/uploads/96789ca97822c1b317a306b80802b98b/test.patch)
I guess I will update the structure definition accordingly.