-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathclamp.h
83 lines (73 loc) · 2.79 KB
/
clamp.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* this header file comes from libowfat, http://www.fefe.de/libowfat/ */
#ifndef _CLAMP_H
#define _CLAMP_H
#include <stdckdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
/* Idea from Stefan Reuther, fe5vsq.17c.1@stefan.msgid.phost.de */
#define PP_NARG(...) (sizeof((_Bool[]){__VA_ARGS__})/sizeof(_Bool))
// add n size_t values, return sum (but return SIZE_MAX on numeric overflow)
// we do this horrendous macro trickery so the compiler can optimize out
// the case where we add a bunch of constants and no overflow happens
#define clamp_add(...) clamp_addn(PP_NARG(__VA_ARGS__),__VA_ARGS__)
#define clamp_addn(a, ...) \
a==0?0: \
a==1?a: \
a==2?clamp_add2(__VA_ARGS__): \
a==3?clamp_add3(__VA_ARGS__,0): \
a==4?clamp_add4(__VA_ARGS__,0,0): \
clamp_add2plus(a,__VA_ARGS__)
#define clamp_add2(a,b,...) ({size_t ret; ckd_add(&ret,a,b)?(size_t)-1:ret;})
#define clamp_add3(a,b,c,...) clamp_add2(a,clamp_add2(b,c))
#define clamp_add4(a,b,c,d,...) clamp_add2(clamp_add2(a,b),clamp_add2(c,d))
#define clamp_add2plus(a,b,...) ({size_t ret; ckd_add(&ret,a,b)?(size_t)-1:__clamp_addn(a-2,ret,__VA_ARGS__);})
static inline size_t __clamp_addn(size_t n, ...) {
va_list l;
size_t ret;
va_start(l,n);
for (ret=0; n>0; --n) {
if (ckd_add(&ret,ret,va_arg(l,size_t))) {
ret=(size_t)-1;
break;
}
}
va_end(l);
return ret;
}
// multiply n size_t values, return product (but return SIZE_MAX on numeric overflow)
// we do this horrendous macro trickery so the compiler can optimize out
// the case where we add a bunch of constants and no overflow happens
#define clamp_mul(...) clamp_muln(PP_NARG(__VA_ARGS__),__VA_ARGS__)
#define clamp_muln(a, ...) \
a==0?0: \
a==1?a: \
a==2?clamp_mul2(__VA_ARGS__): \
a==3?clamp_mul3(__VA_ARGS__,0): \
a==4?clamp_mul4(__VA_ARGS__,0,0): \
clamp_mul2plus(a,__VA_ARGS__)
#define clamp_mul2(a,b,...) ({size_t ret; ckd_mul(&ret,a,b)?(size_t)-1:ret;})
#define clamp_mul3(a,b,c,...) clamp_mul2(a,clamp_mul2(b,c))
#define clamp_mul4(a,b,c,d,...) clamp_mul2(clamp_mul2(a,b),clamp_mul2(c,d))
#define clamp_mul2plus(a,b,...) ({size_t ret; ckd_mul(&ret,a,b)?(size_t)-1:__clamp_muln(a-2,ret,__VA_ARGS__);})
static inline size_t __clamp_muln(size_t n, ...) {
va_list l;
size_t ret;
va_start(l,n);
for (ret=0; n>0; --n) {
if (ckd_mul(&ret,ret,va_arg(l,size_t))) {
ret=(size_t)-1;
break;
}
}
va_end(l);
return ret;
}
// calculate size for allocating a header followed by an array of nelems
// of size elemsize each. Returns SIZE_MAX on numeric overflow, which
// will make malloc fail, so you don't have to check for overflow, only
// for malloc failure, which you hopefully already do.
static inline size_t clamp_hdrarray(size_t hdrsize,size_t nelems,size_t elemsize) {
return clamp_add(hdrsize,clamp_mul(nelems,elemsize));
}
#endif