Nikolay Sivov : mfplat: Add MFllMulDiv().

Alexandre Julliard julliard at winehq.org
Mon May 3 16:37:06 CDT 2021


Module: wine
Branch: master
Commit: 3e3490be39e4f09948fa5b12f3155283c6758ad0
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=3e3490be39e4f09948fa5b12f3155283c6758ad0

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Mon May  3 20:02:43 2021 +0300

mfplat: Add MFllMulDiv().

Implementation is based on similar gstreamer utility code,
adjusted for signed arguments, and with platform optimizations removed.

Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/mfplat/main.c         | 181 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/mfplat/mfplat.spec    |   2 +-
 dlls/mfplat/tests/mfplat.c |  39 ++++++++++
 include/mfapi.h            |   1 +
 4 files changed, 222 insertions(+), 1 deletion(-)

diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index 5a35350f5c5..dad4039fa16 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,183 @@ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **man
 
     return S_OK;
 }
+
+
+/*
+ * MFllMulDiv implementation is derived from gstreamer utility functions code (gstutils.c),
+ * released under LGPL2. Full authors list follows.
+ * ===================================================================================
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega at cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay at chello.be>
+ *                    2002 Thomas Vander Stichele <thomas at apestaart.org>
+ *                    2004 Wim Taymans <wim at fluendo.com>
+ *                    2015 Jan Schmidt <jan at centricular.com>
+ * ===================================================================================
+ */
+
+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);




More information about the wine-cvs mailing list