summaryrefslogtreecommitdiffstats
path: root/lib/adds.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/adds.h86
1 files changed, 86 insertions, 0 deletions
diff --git a/lib/adds.h b/lib/adds.h
new file mode 100644
index 0000000..6544ce5
--- /dev/null
+++ b/lib/adds.h
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: 2023, Alejandro Colomar <alx@kernel.org>
+// SPDX-License-Identifier: BSD-3-Clause
+
+
+#ifndef SHADOW_INCLUDE_LIB_ADDS_H_
+#define SHADOW_INCLUDE_LIB_ADDS_H_
+
+
+#include <config.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "sizeof.h"
+
+
+#define addsl(a, b, ...) \
+({ \
+ long addend_[] = {a, b, __VA_ARGS__}; \
+ \
+ addslN(NITEMS(addend_), addend_); \
+})
+
+
+inline long addsl2(long a, long b);
+inline long addslN(size_t n, long addend[n]);
+
+inline int cmpl(const void *p1, const void *p2);
+
+
+inline long
+addsl2(long a, long b)
+{
+ if (a > 0 && b > LONG_MAX - a) {
+ errno = EOVERFLOW;
+ return LONG_MAX;
+ }
+ if (a < 0 && b < LONG_MIN - a) {
+ errno = EOVERFLOW;
+ return LONG_MIN;
+ }
+ return a + b;
+}
+
+
+inline long
+addslN(size_t n, long addend[n])
+{
+ int e;
+
+ if (n == 0) {
+ errno = EDOM;
+ return 0;
+ }
+
+ e = errno;
+ while (n > 1) {
+ qsort(addend, n, sizeof(addend[0]), cmpl);
+
+ errno = 0;
+ addend[0] = addsl2(addend[0], addend[--n]);
+ if (errno == EOVERFLOW)
+ return addend[0];
+ }
+ errno = e;
+ return addend[0];
+}
+
+
+inline int
+cmpl(const void *p1, const void *p2)
+{
+ const long *l1 = p1;
+ const long *l2 = p2;
+
+ if (*l1 < *l2)
+ return -1;
+ if (*l1 > *l2)
+ return +1;
+ return 0;
+}
+
+
+#endif // include guard