[vlc-commits] [Git][videolan/vlc][3.0.x] 3 commits: stdckdint: add compatibility header

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Feb 21 09:07:21 UTC 2025



Steve Lhomme pushed to branch 3.0.x at VideoLAN / VLC


Commits:
ba82dcd2 by Rémi Denis-Courmont at 2025-02-21T08:36:34+00:00
stdckdint: add compatibility header

This only implements unsigned types for now, as there are no known use
cases for signed checked arithmetic as of yet. The macros will safely
error out at compilation time if signed types are misused.

(cherry picked from commit 38c10291760e55aa6ba4713be5ec1d37138a3bd9)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>

- - - - -
79146a8a by Thomas Guillem at 2025-02-21T08:36:34+00:00
compat: stdckdint: handle signed cases

(cherry picked from commit 9cab61e7fe0f8d54673228e40bedb35be8c4abf9)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>

- - - - -
0c0430b6 by Thomas Guillem at 2025-02-21T08:36:34+00:00
compat: test stdckdint

Test C23 header, clang/gcc builtins and compat versions.

(cherry picked from commit a7ff7c3c00d4bcd3e1230f46a18485b43af0672b)
Signed-off-by: Thomas Guillem <thomas at gllm.fr>

- - - - -


4 changed files:

- compat/Makefile.am
- + compat/stdckdint/stdckdint.h
- + compat/test/ckd.c
- configure.ac


Changes:

=====================================
compat/Makefile.am
=====================================
@@ -1,3 +1,4 @@
+noinst_HEADERS = stdckdint/stdckdint.h
 pkglib_LTLIBRARIES = libcompat.la
 libcompat_la_SOURCES = dummy.c
 libcompat_la_LIBADD = $(LTLIBOBJS) $(LIBRT) $(LIBM)
@@ -13,9 +14,23 @@ dummy.c:
 
 
 check_PROGRAMS = \
-	test_strnstr
+	test_strnstr \
+	test_ckd_ckd \
+	test_ckd_builtin \
+	test_ckd_compat \
+	$(NULL)
 
 TESTS = $(check_PROGRAMS)
 
 test_strnstr_SOURCES = test/strnstr.c
 test_strnstr_LDADD = libcompat.la
+
+test_ckd_ckd_SOURCES = test/ckd.c
+test_ckd_ckd_CFLAGS = -DTEST_CKD
+if HAVE_STDCKDINT
+test_ckd_ckd_CFLAGS += -DHAVE_CKD
+endif
+test_ckd_builtin_SOURCES = $(test_ckd_ckd_SOURCES)
+test_ckd_builtin_CFLAGS = -DTEST_BUILTIN
+test_ckd_compat_SOURCES = $(test_ckd_ckd_SOURCES)
+test_ckd_compat_CFLAGS = -DTEST_COMPAT


=====================================
compat/stdckdint/stdckdint.h
=====================================
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ */
+
+#ifndef __STDC_VERSION_STDCKDINT_H__
+# define __STDC_VERSION_STDCKDINT_H__ 202311L
+
+# if defined(__GNUC__) || defined(__clang__)
+#  define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+#  define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+#  define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+# else
+#  include <limits.h>
+
+#  define __ckd_unsigned(suffix, type, MAX) \
+static inline _Bool __ckd_add_##suffix(type *r, type a, type b) \
+{ \
+    *r = a + b; \
+    return ((type)(a + b)) < a; \
+} \
+\
+static inline _Bool __ckd_sub_##suffix(type *r, type a, type b) \
+{ \
+    *r = a - b; \
+    return a < b; \
+} \
+\
+static inline _Bool __ckd_mul_##suffix(type *r, type a, type b) \
+{ \
+    *r = a * b; \
+    return b > 0 && a > (MAX / b); \
+}
+
+#  define __ckd_signed_common(suffix, type, MIN, MAX) \
+static inline _Bool __ckd_add_##suffix(type *r, type a, type b) \
+{ \
+    union suffix ua = { .v = a }; \
+    union suffix ub = { .v = b }; \
+    union suffix ur = { .uv = ua.uv + ub.uv }; \
+    *r = ur.v; \
+    if ((b > 0 && a > (MAX - b)) || (b < 0 && a < (MIN - b))) \
+        return 1; \
+    return 0; \
+} \
+\
+static inline _Bool __ckd_sub_##suffix(type *r, type a, type b) \
+{ \
+    union suffix ua = { .v = a }; \
+    union suffix ub = { .v = b }; \
+    union suffix ur = { .uv = ua.uv - ub.uv }; \
+    *r = ur.v; \
+    if ((b < 0 && a > (MAX + b)) || (b > 0 && a < (MIN + b))) \
+        return 1; \
+    return 0; \
+} \
+\
+static inline _Bool __ckd_mul_##suffix(type *r, type a, type b) \
+{ \
+    union suffix ua = { .v = a }; \
+    union suffix ub = { .v = b }; \
+    union suffix ur = { .uv = ua.uv * ub.uv }; \
+    *r = ur.v; \
+    if (a > 0) { \
+        if (b > 0) { \
+            if (a > (MAX / b)) return 1; \
+        } else if (b < 0) { \
+            if (b < (MIN / a)) return 1; \
+        } \
+    } else if (a < 0) { \
+        if (b > 0) { \
+            if (a < (MIN / b)) return 1; \
+        } else if (b < 0) { \
+            if (b < (MAX / a)) return 1; \
+        } \
+    } \
+    return 0; \
+}
+
+#  define __ckd_signed(suffix, type, MIN, MAX) \
+union suffix { \
+    unsigned type uv; \
+    type v; \
+}; \
+__ckd_signed_common(suffix, type, MIN, MAX)
+
+#  define __ckd_signed_forced(suffix, type, MIN, MAX) \
+union suffix { \
+    unsigned type uv; \
+    signed type v; \
+}; \
+__ckd_signed_common(suffix, signed type, MIN, MAX)
+
+#  define __ckd_func(op, r, a, b) \
+    _Generic (*(r), \
+        signed char:        __ckd_##op##_sc((signed char *)(r), a, b), \
+        short:              __ckd_##op##_ss((short *)(r), a, b), \
+        int:                __ckd_##op##_si((int *)(r), a, b), \
+        long:               __ckd_##op##_sl((long *)(r), a, b), \
+        long long:          __ckd_##op##_sll((long long *)(r), a, b), \
+        unsigned char:      __ckd_##op##_uc((unsigned char *)(r), a, b), \
+        unsigned short:     __ckd_##op##_us((unsigned short *)(r), a, b), \
+        unsigned int:       __ckd_##op##_ui((unsigned int *)(r), a, b), \
+        unsigned long:      __ckd_##op##_ul((unsigned long *)(r), a, b), \
+        unsigned long long: __ckd_##op##_ull((unsigned long long *)(r), a, b))
+
+__ckd_unsigned(uc,  unsigned char,      UCHAR_MAX)
+__ckd_unsigned(us,  unsigned short,     USHRT_MAX)
+__ckd_unsigned(ui,  unsigned int,       UINT_MAX)
+__ckd_unsigned(ul,  unsigned long,      ULONG_MAX)
+__ckd_unsigned(ull, unsigned long long, ULLONG_MAX)
+
+__ckd_signed_forced(sc,  char, SCHAR_MIN, SCHAR_MAX)
+__ckd_signed(ss,  short,       SHRT_MIN,  SHRT_MAX)
+__ckd_signed(si,  int,         INT_MIN,   INT_MAX)
+__ckd_signed(sl,  long,        LONG_MIN,  LONG_MAX)
+__ckd_signed(sll, long long,   LLONG_MIN, LLONG_MAX)
+
+#  define ckd_add(r, a, b) __ckd_func(add, r, a, b)
+#  define ckd_sub(r, a, b) __ckd_func(sub, r, a, b)
+#  define ckd_mul(r, a, b) __ckd_func(mul, r, a, b)
+# endif
+#endif /* __STDC_VERSION_STDCKDINT_H__ */


=====================================
compat/test/ckd.c
=====================================
@@ -0,0 +1,115 @@
+/*****************************************************************************
+ * stdckdint.h test case
+ *****************************************************************************
+ * Copyright © 2025 VideoLabs, VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <limits.h>
+#undef NDEBUG
+#include <assert.h>
+
+#if defined (TEST_CKD)
+# ifdef HAVE_CKD
+#  include "../include/stdckdint.h" /* Force include on the compiler path */
+# else
+#  define SKIP /* can't test it */
+# endif
+#elif defined (TEST_BUILTIN)
+# if defined(__GNUC__) || defined(__clang__)
+#  undef __STDC_VERSION_STDCKDINT_H__
+#  include "../stdckdint/stdckdint.h" /* Force include on the compat path */
+# else
+#  define SKIP /* can't test it */
+# endif
+#elif defined (TEST_COMPAT)
+# undef __STDC_VERSION_STDCKDINT_H__
+# undef __GNUC__
+# undef __clang__
+# include "../stdckdint/stdckdint.h"
+#else
+# error TEST_ not defined
+#endif
+
+int main(void)
+{
+#ifdef SKIP
+    return 77;
+#else
+    unsigned long long ures;
+    long long res;
+
+    /* multiplication */
+
+    /* zero */
+    assert(!ckd_mul(&ures, 0, 0) && ures == 0);
+    assert(!ckd_mul(&ures, 0, 1) && ures == 0);
+    assert(!ckd_mul(&ures, 1, 0) && ures == 0);
+
+    /* small cases */
+    assert(!ckd_mul(&ures, 2, 3) && ures == 6);
+    assert(!ckd_mul(&res, -3, 3) && res == -9);
+    assert(!ckd_mul(&res, -3, -3) && res == 9);
+
+    /* near positive overflow */
+    assert(!ckd_mul(&res, LLONG_MAX, 1) && res == LLONG_MAX);
+    assert(!ckd_mul(&res, LLONG_MAX / 2, 2) && res == LLONG_MAX / 2 * 2);
+    assert(ckd_mul(&res, LLONG_MAX, 2) && res == -2);
+    assert(!ckd_mul(&res, LLONG_MAX, -1) && res == -LLONG_MAX);
+
+    /* near negative overflow */
+    assert(!ckd_mul(&res, LLONG_MIN, 1) && res == LLONG_MIN);
+    assert(ckd_mul(&res, LLONG_MIN, -1) && res == LLONG_MIN);
+
+    /* additions */
+
+    /* small cases */
+    assert(!ckd_add(&ures, 0, 0) && ures == 0);
+    assert(!ckd_add(&ures, 0, 1) && ures == 1);
+    assert(!ckd_add(&ures, 1, 0) && ures == 1);
+    assert(!ckd_add(&ures, 1, 1) && ures == 2);
+
+    /* big edge cases */
+    assert(!ckd_add(&ures, ULLONG_MAX, 0ULL) && ures == ULLONG_MAX);
+    assert(!ckd_add(&ures, ULLONG_MAX - 1ULL, 1ULL) && ures == ULLONG_MAX);
+    assert(ckd_add(&ures, ULLONG_MAX, 1ULL) && ures == 0);
+    assert(ckd_add(&res, LLONG_MAX, 1ULL) && res == LLONG_MIN);
+
+    /* subtractions */
+
+    /* small cases */
+    assert(!ckd_sub(&ures, 0, 0) && ures == 0);
+    assert(!ckd_sub(&ures, 1, 0) && ures == 1);
+    assert(!ckd_sub(&ures, 1, 1) && ures == 0);
+
+    /* 0 - 1 */
+    assert(ckd_sub(&ures, 0, 1) && ures == ULLONG_MAX);
+    assert(!ckd_sub(&res, 0, 1) && res == -1);
+
+    /* edge cases: */
+    assert(!ckd_sub(&ures, ULLONG_MAX, 0) && ures == ULLONG_MAX);
+    assert(!ckd_sub(&ures, ULLONG_MAX, 1) && ures == ULLONG_MAX - 1ULL);
+    assert(ckd_sub(&ures, 0ULL, ULLONG_MAX) && ures == 1);
+    assert(ckd_sub(&ures, 2, 3) && ures == ULLONG_MAX);
+    assert(ckd_sub(&res, LLONG_MIN, 1) && res == LLONG_MAX);
+
+    return 0;
+#endif
+}


=====================================
configure.ac
=====================================
@@ -870,6 +870,14 @@ dnl
 dnl Check for headers
 dnl
 
+have_stdckdint="no"
+AC_CHECK_HEADER([stdckdint.h], [
+  have_stdckdint="yes"
+], [
+  CPPFLAGS="${CPPFLAGS} -I\$(top_srcdir)/compat/stdckdint"
+])
+AM_CONDITIONAL([HAVE_STDCKDINT], [test "${have_stdckdint}" = "yes"])
+
 dnl  POSIX
 AC_CHECK_HEADERS([arpa/inet.h pthread.h search.h sys/shm.h sys/socket.h sys/uio.h])
 AC_CHECK_HEADERS([net/if.h], [], [],



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7b9c6bf309d9da4c22b3605608893a0af9b73ce2...0c0430b699c05d26eb867ef78a4d37a17d5493e3

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7b9c6bf309d9da4c22b3605608893a0af9b73ce2...0c0430b699c05d26eb867ef78a4d37a17d5493e3
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list