Implementation is based on similar gstreamer utility code, adjusted for signed arguments, and with platform optimizations removed.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfplat/main.c | 168 +++++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 2 +- dlls/mfplat/tests/mfplat.c | 39 +++++++++ include/mfapi.h | 1 + 4 files changed, 209 insertions(+), 1 deletion(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 5a35350f5c5..f27cc272c30 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -19,6 +19,7 @@ #include <stdarg.h> #include <string.h> #include <math.h> +#include <limits.h>
#define COBJMACROS #define NONAMELESSUNION @@ -8928,3 +8929,170 @@ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **man
return S_OK; } + +static void llmult128(ULARGE_INTEGER *c1, ULARGE_INTEGER *c0, LONGLONG val, LONGLONG num) +{ + ULARGE_INTEGER a1, b0, v, n; + + v.QuadPart = llabs(val); + n.QuadPart = llabs(num); + + /* do 128 bits multiply + * nh nl + * * vh vl + * ---------- + * a0 = vl * nl + * a1 = vl * nh + * b0 = vh * nl + * b1 = + vh * nh + * ------------------- + * c1h c1l c0h c0l + * + * "a0" is optimized away, result is stored directly in c0. "b1" is + * optimized away, result is stored directly in c1. + */ + c0->QuadPart = (ULONGLONG)v.LowPart * n.LowPart; + a1.QuadPart = (ULONGLONG)v.LowPart * n.HighPart; + b0.QuadPart = (ULONGLONG)v.HighPart * n.LowPart; + + /* add the high word of a0 to the low words of a1 and b0 using c1 as + * scrach space to capture the carry. the low word of the result becomes + * the final high word of c0 */ + c1->QuadPart = (ULONGLONG)c0->HighPart + a1.LowPart + b0.LowPart; + c0->HighPart = c1->LowPart; + + /* add the carry from the result above (found in the high word of c1) and + * the high words of a1 and b0 to b1, the result is c1. */ + c1->QuadPart = (ULONGLONG)v.HighPart * n.HighPart + c1->HighPart + a1.HighPart + b0.HighPart; +} + +static ULONGLONG lldiv128(ULARGE_INTEGER c1, ULARGE_INTEGER c0, LONGLONG denom) +{ + ULARGE_INTEGER q1, q0, rhat; + ULARGE_INTEGER v, cmp1, cmp2; + unsigned int s = 0; + + v.QuadPart = llabs(denom); + + /* 64bit numerator */ + if (c1.QuadPart == 0) + return c0.QuadPart / v.QuadPart; + + /* 96bit numerator, 32bit denominator */ + if (v.HighPart == 0 && c1.HighPart == 0) + { + ULONGLONG low = c0.LowPart, high = c0.HighPart + ((ULONGLONG)c1.LowPart << 32); + low += (high % v.LowPart) << 32; + return ((high / v.LowPart) << 32) + (low / v.LowPart); + } + + /* 128bit numerator, 32bit denominator */ + if (v.HighPart == 0) + return UI64_MAX; + + /* count number of leading zeroes */ + BitScanReverse(&s, v.HighPart); + s = 31 - s; + + if (s) + { + /* normalize divisor and dividend */ + v.QuadPart <<= s; + c1.QuadPart = (c1.QuadPart << s) | (c0.HighPart >> (32 - s)); + c0.QuadPart <<= s; + } + + q1.QuadPart = c1.QuadPart / v.HighPart; + rhat.QuadPart = c1.QuadPart - q1.QuadPart * v.HighPart; + + cmp1.HighPart = rhat.LowPart; + cmp1.LowPart = c0.HighPart; + cmp2.QuadPart = q1.QuadPart * v.LowPart; + + while (q1.HighPart || cmp2.QuadPart > cmp1.QuadPart) + { + q1.QuadPart--; + rhat.QuadPart += v.HighPart; + if (rhat.HighPart) + break; + cmp1.HighPart = rhat.LowPart; + cmp2.QuadPart -= v.LowPart; + } + c1.HighPart = c1.LowPart; + c1.LowPart = c0.HighPart; + c1.QuadPart -= q1.QuadPart * v.QuadPart; + q0.QuadPart = c1.QuadPart / v.HighPart; + rhat.QuadPart = c1.QuadPart - q0.QuadPart * v.HighPart; + + cmp1.HighPart = rhat.LowPart; + cmp1.LowPart = c0.LowPart; + cmp2.QuadPart = q0.QuadPart * v.LowPart; + + while (q0.HighPart || cmp2.QuadPart > cmp1.QuadPart) + { + q0.QuadPart--; + rhat.QuadPart += v.HighPart; + if (rhat.HighPart) + break; + cmp1.HighPart = rhat.LowPart; + cmp2.QuadPart -= v.LowPart; + } + q0.HighPart += q1.LowPart; + + return q0.QuadPart; +} + +/*********************************************************************** + * MFllMulDiv (mfplat.@) + */ +LONGLONG WINAPI MFllMulDiv(LONGLONG val, LONGLONG num, LONGLONG denom, LONGLONG factor) +{ +#define LLOVERFLOW (sign ? I64_MIN : I64_MAX) + unsigned int sign, factor_sign, denom_sign; + ULARGE_INTEGER c1, c0; + ULONGLONG ret; + + TRACE("%s, %s, %s, %s.\n", wine_dbgstr_longlong(val), wine_dbgstr_longlong(num), + wine_dbgstr_longlong(denom), wine_dbgstr_longlong(factor)); + + /* compute 128-bit numerator product */ + llmult128(&c1, &c0, val, num); + + sign = (val < 0) ^ (num < 0); + factor_sign = factor < 0; + denom_sign = denom < 0; + + factor = llabs(factor); + if (sign == factor_sign) + { + if (UI64_MAX - c0.QuadPart < factor) + { + if (c1.QuadPart == UI64_MAX) return LLOVERFLOW; + c1.QuadPart++; + } + c0.QuadPart += factor; + } + else + { + if (c0.QuadPart >= factor) + c0.QuadPart -= factor; + else + { + if (c1.QuadPart) + c1.QuadPart--; + else + sign = !sign; + + c0.QuadPart = factor - c0.QuadPart; + } + } + + if (c1.QuadPart >= denom) return LLOVERFLOW; + + /* compute quotient, fits in 64 bits */ + ret = lldiv128(c1, c0, denom); + sign ^= denom_sign; + if (ret >= I64_MAX) return LLOVERFLOW; + return sign ? -(LONGLONG)ret : ret; +#undef LLOVERFLOW +} diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index cbda9404aa9..d8558cee0cb 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -176,6 +176,6 @@ @ stdcall MFUnwrapMediaType(ptr ptr) @ stub MFValidateMediaTypeSize @ stdcall MFWrapMediaType(ptr ptr ptr ptr) -@ stub MFllMulDiv +@ stdcall -ret64 MFllMulDiv(int64 int64 int64 int64) @ stub PropVariantFromStream @ stub PropVariantToStream diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 89547be0a4f..b0c7298c28d 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -20,6 +20,7 @@
#include <stdarg.h> #include <string.h> +#include <limits.h>
#define COBJMACROS
@@ -7130,6 +7131,43 @@ static void test_MFLockSharedWorkQueue(void) ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); }
+static void test_MFllMulDiv(void) +{ + /* (a * b + d) / c */ + static const struct muldivtest + { + LONGLONG a; + LONGLONG b; + LONGLONG c; + LONGLONG d; + LONGLONG result; + } + muldivtests[] = + { + { 0, 0, 0, 0, _I64_MAX }, + { 1000000, 1000000, 2, 0, 500000000000 }, + { _I64_MAX, 3, _I64_MAX, 0, 3 }, + { _I64_MAX, 3, _I64_MAX, 1, 3 }, + { -10000, 3, 100, 0, -300 }, + { 2, 0, 3, 5, 1 }, + { 2, 1, 1, -3, -1 }, + /* a * b product does not fit in uint64_t */ + { _I64_MAX, 4, 8, 0, _I64_MAX / 2 }, + /* Large a * b product, large denominator */ + { _I64_MAX, 4, 0x100000000, 0, 0x1ffffffff }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(muldivtests); ++i) + { + LONGLONG result; + + result = MFllMulDiv(muldivtests[i].a, muldivtests[i].b, muldivtests[i].c, muldivtests[i].d); + ok(result == muldivtests[i].result, "%u: unexpected result %s, expected %s.\n", i, + wine_dbgstr_longlong(result), wine_dbgstr_longlong(muldivtests[i].result)); + } +} + START_TEST(mfplat) { char **argv; @@ -7194,6 +7232,7 @@ START_TEST(mfplat) test_dxgi_surface_buffer(); test_sample_allocator(); test_MFMapDX9FormatToDXGIFormat(); + test_MFllMulDiv();
CoUninitialize(); } diff --git a/include/mfapi.h b/include/mfapi.h index c1cf766c005..986f5b9e3e4 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -552,6 +552,7 @@ HRESULT WINAPI MFTEnumEx(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_IN HRESULT WINAPI MFInitAttributesFromBlob(IMFAttributes *attributes, const UINT8 *buffer, UINT size); HRESULT WINAPI MFInitMediaTypeFromWaveFormatEx(IMFMediaType *mediatype, const WAVEFORMATEX *format, UINT32 size); HRESULT WINAPI MFInvokeCallback(IMFAsyncResult *result); +LONGLONG WINAPI MFllMulDiv(LONGLONG val, LONGLONG num, LONGLONG denom, LONGLONG factor); HRESULT WINAPI MFLockPlatform(void); HRESULT WINAPI MFLockSharedWorkQueue(const WCHAR *name, LONG base_priority, DWORD *taskid, DWORD *queue); DXGI_FORMAT WINAPI MFMapDX9FormatToDXGIFormat(DWORD format);