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