1/* @title: Alignment and Rounding */
2#pragma once
3#include <compiler/core.h>
4#include <compiler/intrinsic.h>
5#include <enforce.h>
6#include <stdbool.h>
7#include <stdint.h>
8
9/* Use UNCHECKED and WRAPPING when callers don't want to panic and want to
10 * validate input, whereas the defaults panic */
11
12#define _ALIGN_CAPTURE_UNCHECKED(x, align) \
13 __auto_type __aln_x = (x); \
14 __auto_type __aln_src_align = (align); \
15 ct_typecheck_integer_as(__aln_x, x); \
16 ct_typecheck_widenable_to(__aln_x, align); \
17 typeof(__aln_x) __aln_align = (typeof(__aln_x)) __aln_src_align
18
19#define _ALIGN_CAPTURE(x, align) \
20 _ALIGN_CAPTURE_UNCHECKED(x, align); \
21 (void) enforce(__aln_align > 0 && (__aln_align & (__aln_align - 1)) == 0)
22
23#define IS_POW2(x) \
24 ({ \
25 __auto_type __aln_p2 = (x); \
26 ct_typecheck_integer_as(__aln_p2, x); \
27 __aln_p2 > 0 && (__aln_p2 & (__aln_p2 - 1)) == 0; \
28 })
29
30#define ALIGN_DOWN(x, align) \
31 ({ \
32 _ALIGN_CAPTURE(x, align); \
33 __aln_x & ~(__aln_align - 1); \
34 })
35
36#define _ALIGN_UP_CAPTURE(x, align) \
37 _ALIGN_CAPTURE_UNCHECKED(x, align); \
38 typeof((__aln_x) + 0) __aln_mask = __aln_align - 1; \
39 typeof((__aln_x) + 0) __aln_sum; \
40 bool __aln_overflow = ci_add_overflow(__aln_x, __aln_mask, &__aln_sum)
41
42/* Rounds up without validating the alignment, wraps on overflow */
43#define ALIGN_UP_WRAPPING(x, align) \
44 ({ \
45 _ALIGN_UP_CAPTURE(x, align); \
46 (void) __aln_overflow; \
47 (typeof(__aln_x)) (__aln_sum & ~__aln_mask); \
48 })
49
50#define ALIGN_UP(x, align) \
51 ({ \
52 _ALIGN_UP_CAPTURE(x, align); \
53 (void) enforce(__aln_align > 0 && \
54 (__aln_align & (__aln_align - 1)) == 0 && \
55 !__aln_overflow); \
56 (typeof(__aln_x)) (__aln_sum & ~__aln_mask); \
57 })
58
59#define IS_ALIGNED(x, align) \
60 ({ \
61 _ALIGN_CAPTURE(x, align); \
62 (__aln_x & (__aln_align - 1)) == 0; \
63 })
64
65#define IS_ALIGNED_UNCHECKED(x, align) \
66 ({ \
67 _ALIGN_CAPTURE_UNCHECKED(x, align); \
68 __aln_align > 0 && (__aln_align & (__aln_align - 1)) == 0 && \
69 (__aln_x & (__aln_align - 1)) == 0; \
70 })
71
72/* TODO: This only operates on unsigned values, I do want signed
73 * support, so I might need to write that out */
74#define DIV_ROUND_UP(n, d) \
75 ({ \
76 __auto_type __div_n = (n); \
77 __auto_type __div_src_d = (d); \
78 ct_typecheck_unsigned_as(__div_n, n); \
79 ct_typecheck_widenable_to(__div_n, d); \
80 typeof(__div_n) __div_d = (typeof(__div_n)) __div_src_d; \
81 (void) enforce(__div_d > 0); \
82 (typeof(__div_n)) (__div_n / __div_d + (__div_n % __div_d != 0)); \
83 })
84